diff --git a/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php b/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php index c5c08a0552f42..93732b1b52f0c 100644 --- a/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php +++ b/app/code/Magento/Catalog/Block/Ui/ProductViewCounter.php @@ -153,6 +153,7 @@ public function getCurrentProductData() $this->productRenderCollectorComposite ->collect($product, $productRender); $data = $this->hydrator->extract($productRender); + $data['is_available'] = $product->isAvailable(); $currentProductData = [ 'items' => [ diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryLinkActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryLinkActionGroup.xml index 60438e23e084c..2fb18ddfc9eb6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryLinkActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminClickOnAdvancedInventoryLinkActionGroup.xml @@ -17,5 +17,7 @@ + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index b5fa46635f433..1a57ea3adac19 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -39,6 +39,10 @@ TestFooBar foobar + + Simple Product Double Space + simple-product double-space + Pursuit Lumaflex&trade; Tone Band x&trade; diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminShowDoubleSpacesInProductGrid.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShowDoubleSpacesInProductGrid.xml new file mode 100644 index 0000000000000..c3e939b4155c8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminShowDoubleSpacesInProductGrid.xml @@ -0,0 +1,52 @@ + + + + + + + + + + <description value="Admin should be able to see double spaces in the Name and Sku fields in the product grid"/> + <testCaseId value="MC-40725"/> + <useCaseId value="MC-40122"/> + <severity value="AVERAGE"/> + <group value="Catalog"/> + </annotations> + + <before> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProductWithDoubleSpaces" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <magentoCLI command="cron:run --group=index" stepKey="cronRun"/> + <magentoCLI command="cron:run --group=index" stepKey="cronRunSecondTime"/> + </before> + + <after> + <actionGroup ref="AdminDeleteAllProductsFromGridActionGroup" stepKey="deleteProduct"/> + <actionGroup ref="ClearFiltersAdminProductGridActionGroup" stepKey="clearGridFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminOpenCatalogProductPageActionGroup" stepKey="goToProductCatalogPage"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="searchForProduct"> + <argument name="product" value="$createProduct$"/> + </actionGroup> + <actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="assertProductName"> + <argument name="column" value="Name"/> + <argument name="value" value="$createProduct.name$"/> + </actionGroup> + <actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="assertProductSku"> + <argument name="column" value="SKU"/> + <argument name="value" value="$createProduct.sku$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php index 6026d1462e461..87f5be4b21333 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Ui/ProductViewCounterTest.php @@ -166,6 +166,7 @@ public function testGetCurrentProductDataWithNonEmptyProduct() { $productMock = $this->getMockBuilder(ProductInterface::class) ->disableOriginalConstructor() + ->addMethods(['isAvailable']) ->getMockForAbstractClass(); $productRendererMock = $this->getMockBuilder(ProductRenderInterface::class) ->disableOriginalConstructor() @@ -173,7 +174,6 @@ public function testGetCurrentProductDataWithNonEmptyProduct() $storeMock = $this->getMockBuilder(Store::class) ->disableOriginalConstructor() ->getMock(); - $this->registryMock->expects($this->once()) ->method('registry') ->with('product') 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 88bb578712056..2cd2a15b04900 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 @@ -132,7 +132,7 @@ <settings> <addField>true</addField> <filter>text</filter> - <bodyTmpl>ui/grid/cells/html</bodyTmpl> + <bodyTmpl>Magento_Catalog/grid/cells/preserved</bodyTmpl> <label translate="true">Name</label> </settings> </column> @@ -155,7 +155,7 @@ <column name="sku" sortOrder="60"> <settings> <filter>text</filter> - <bodyTmpl>ui/grid/cells/html</bodyTmpl> + <bodyTmpl>Magento_Catalog/grid/cells/preserved</bodyTmpl> <label translate="true">SKU</label> </settings> </column> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/template/grid/cells/preserved.html b/app/code/Magento/Catalog/view/adminhtml/web/template/grid/cells/preserved.html new file mode 100644 index 0000000000000..936342df23795 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/template/grid/cells/preserved.html @@ -0,0 +1,7 @@ +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<div class="data-grid-cell-content white-space-preserved" html="$col.getLabel($row())"/> diff --git a/app/code/Magento/Catalog/view/base/web/js/product/addtocart-button.js b/app/code/Magento/Catalog/view/base/web/js/product/addtocart-button.js index 4baf082b37c02..f599d05ba5ea9 100644 --- a/app/code/Magento/Catalog/view/base/web/js/product/addtocart-button.js +++ b/app/code/Magento/Catalog/view/base/web/js/product/addtocart-button.js @@ -55,6 +55,16 @@ define([ return row['is_salable']; }, + /** + * Depends on this option, stock status text can be "In stock" or "Out Of Stock" + * + * @param {Object} row + * @returns {Boolean} + */ + isAvailable: function (row) { + return row['is_available']; + }, + /** * Depends on this option, "Add to cart" button can be shown or hide. Depends on backend configuration * diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml index 07cca77178a38..b7d6e1f2079a0 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml @@ -78,7 +78,7 @@ </button> </form> <?php else :?> - <?php if ($item->getIsSalable()) :?> + <?php if ($item->isAvailable()) :?> <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> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index a831bd7be6f71..4fba22f41c9de 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -153,7 +153,7 @@ $_helper = $block->getData('outputHelper'); <?php endforeach; ?> </ol> </div> - <?= $block->getToolbarHtml() ?> + <?= $block->getChildBlock('toolbar')->setIsBottom(true)->toHtml() ?> <script type="text/x-magento-init"> { "[data-role=tocart-form], .form.map.checkout": { diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml index e426b940deab7..6fd619de7fd6c 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml @@ -287,7 +287,7 @@ $_item = null; </form> <?php endif; ?> <?php else:?> - <?php if ($_item->getIsSalable()):?> + <?php if ($_item->isAvailable()):?> <div class="stock available"> <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml index 76ef6baf4993e..3c8687d090baf 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml @@ -10,27 +10,23 @@ * * @var $block \Magento\Catalog\Block\Product\ProductList\Toolbar */ - -// phpcs:disable Magento2.Security.IncludeFile.FoundIncludeFile -// phpcs:disable PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket ?> <?php if ($block->getCollection()->getSize()) :?> <?php $widget = $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonDecode($block->getWidgetOptionsJson()); $widgetOptions = $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($widget['productListToolbarForm']); ?> <div class="toolbar toolbar-products" data-mage-init='{"productListToolbarForm":<?= /* @noEscape */ $widgetOptions ?>}'> - <?php if ($block->isExpanded()) :?> - <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/viewmode.phtml')) ?> - <?php endif; ?> - - <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/amount.phtml')) ?> - - <?= $block->getPagerHtml() ?> - - <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/limiter.phtml')) ?> - - <?php if ($block->isExpanded()) :?> - <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/sorter.phtml')) ?> - <?php endif; ?> + <?php if ($block->getIsBottom()): ?> + <?= $block->getPagerHtml() ?> + <?= $block->fetchView($block->getTemplateFile('Magento_Catalog::product/list/toolbar/limiter.phtml')) ?> + <?php else: ?> + <?php if ($block->isExpanded()): ?> + <?= $block->fetchView($block->getTemplateFile('Magento_Catalog::product/list/toolbar/viewmode.phtml')) ?> + <?php endif ?> + <?= $block->fetchView($block->getTemplateFile('Magento_Catalog::product/list/toolbar/amount.phtml')) ?> + <?php if ($block->isExpanded()): ?> + <?= $block->fetchView($block->getTemplateFile('Magento_Catalog::product/list/toolbar/sorter.phtml')) ?> + <?php endif ?> + <?php endif ?> </div> <?php endif ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml index 6cebd51284f48..49dd702a6e39c 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml @@ -63,7 +63,7 @@ $_helper = $this->helper(Magento\Catalog\Helper\Output::class); . ' data-mage-init=\'{ "redirectUrl": { "event": "click", url: "' . $block->escapeUrl($block->getAddToCartUrl($_product)) . '"} }\'>' . '<span>' . $block->escapeHtml(__('Add to Cart')) . '</span></button>'; } else { - $info['button'] = $_product->getIsSalable() ? '<div class="stock available"><span>' . $block->escapeHtml(__('In stock')) . '</span></div>' : + $info['button'] = $_product->isAvailable() ? '<div class="stock available"><span>' . $block->escapeHtml(__('In stock')) . '</span></div>' : '<div class="stock unavailable"><span>' . $block->escapeHtml(__('Out of stock')) . '</span></div>'; } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml index 53a0682311b1f..fce91564c96a2 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml @@ -52,7 +52,7 @@ </button> <?php endif; ?> <?php else :?> - <?php if ($_product->getIsSalable()) :?> + <?php if ($_product->isAvailable()) :?> <div class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml index 5108c488aec19..66683ef328e08 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml @@ -89,7 +89,7 @@ if ($exist = ($block->getProductCollection() && $block->getProductCollection()-> </button> <?php endif; ?> <?php else :?> - <?php if ($_item->getIsSalable()) :?> + <?php if ($_item->isAvailable()) :?> <div class="stock available"> <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml index 378cd49493a6e..ceb32e78c7e44 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml @@ -88,7 +88,7 @@ if ($exist = ($block->getProductCollection() && $block->getProductCollection()-> </button> <?php endif; ?> <?php else :?> - <?php if ($_item->getIsSalable()) :?> + <?php if ($_item->isAvailable()) :?> <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> diff --git a/app/code/Magento/Catalog/view/frontend/web/template/product/addtocart-button.html b/app/code/Magento/Catalog/view/frontend/web/template/product/addtocart-button.html index 05dbf02703285..867b5f40d98db 100644 --- a/app/code/Magento/Catalog/view/frontend/web/template/product/addtocart-button.html +++ b/app/code/Magento/Catalog/view/frontend/web/template/product/addtocart-button.html @@ -15,10 +15,10 @@ </button> </if> - <ifnot args="isSalable($row())"> + <if args="isAvailable($row()) === false"> <div class="stock unavailable"> <text args="$t('Availability')"/> <span translate="'Out of stock'"/> </div> - </ifnot> + </if> </if> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml index 2c38f14f53379..b93c2af43e64d 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminCatalogInventoryChangeManageStockActionGroup.xml @@ -16,6 +16,8 @@ <argument name="manageStock" type="string" defaultValue="Yes"/> </arguments> <conditionalClick selector="{{AdminProductFormSection.advancedInventoryLink}}" dependentSelector="{{AdminProductFormAdvancedInventorySection.advancedInventoryModal}}" visible="false" stepKey="openAdvancedInventoryWindow"/> + <!-- Wait for close button appeared. That means animation finished and modal window is fully visible --> + <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.advancedInventoryCloseButton}}" stepKey="waitForCloseButtonAppeared"/> <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.useConfigSettings}}" stepKey="waitForAdvancedInventoryModalWindowOpen"/> <uncheckOption selector="{{AdminProductFormAdvancedInventorySection.useConfigSettings}}" stepKey="uncheckManageStockConfigSetting"/> <selectOption selector="{{AdminProductFormAdvancedInventorySection.manageStock}}" userInput="{{manageStock}}" stepKey="changeManageStock"/> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml index a5e4d3e9c2af7..e8871365dabea 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/ActionGroup/AdminProductSetMaxQtyAllowedInShoppingCartActionGroup.xml @@ -13,11 +13,11 @@ <argument name="qty" type="string"/> </arguments> <conditionalClick selector="{{AdminProductFormSection.advancedInventoryLink}}" dependentSelector="{{AdminProductFormAdvancedInventorySection.advancedInventoryModal}}" visible="false" stepKey="clickOnAdvancedInventoryLinkIfNeeded"/> + <!-- Wait for close button appeared. That means animation finished and modal window is fully visible --> + <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.advancedInventoryCloseButton}}" stepKey="waitForCloseButtonAppeared"/> <waitForElementVisible selector="{{AdminProductFormAdvancedInventorySection.maxiQtyConfigSetting}}" stepKey="waitForAdvancedInventoryModalWindowOpen"/> <uncheckOption selector="{{AdminProductFormAdvancedInventorySection.maxiQtyConfigSetting}}" stepKey="uncheckMaxQtyCheckBox"/> <fillField selector="{{AdminProductFormAdvancedInventorySection.maxiQtyAllowedInCart}}" userInput="{{qty}}" stepKey="fillMaxAllowedQty"/> <click selector="{{AdminSlideOutDialogSection.doneButton}}" stepKey="clickDone"/> </actionGroup> - - </actionGroups> 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 9637815c90eef..000f3ffd36934 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 @@ -77,7 +77,7 @@ use Magento\Framework\App\Action\Action; </button> </form> <?php else: ?> - <?php if ($_item->getIsSalable()): ?> + <?php if ($_item->isAvailable()): ?> <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> diff --git a/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php b/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php index 40ce2252581cf..205f0aa8dbd2e 100644 --- a/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php +++ b/app/code/Magento/Checkout/Controller/Cart/UpdateItemOptions.php @@ -1,20 +1,29 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\Checkout\Controller\Cart; +use Magento\Checkout\Controller\Cart; +use Magento\Checkout\Helper\Cart as CartHelper; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Locale\ResolverInterface; +use Psr\Log\LoggerInterface; -class UpdateItemOptions extends \Magento\Checkout\Controller\Cart implements HttpPostActionInterface +/** + * Process updating product options in a cart item. + */ +class UpdateItemOptions extends Cart implements HttpPostActionInterface { /** - * Update product configuration for a cart item + * Update product configuration for a cart item. * - * @return \Magento\Framework\Controller\Result\Redirect + * @return Redirect * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -28,27 +37,27 @@ public function execute() } try { if (isset($params['qty'])) { - $filter = new \Zend_Filter_LocalizedToNormalized( - ['locale' => $this->_objectManager->get( - \Magento\Framework\Locale\ResolverInterface::class - )->getLocale()] + $inputFilter = new \Zend_Filter_LocalizedToNormalized( + [ + 'locale' => $this->_objectManager->get(ResolverInterface::class)->getLocale(), + ] ); - $params['qty'] = $filter->filter($params['qty']); + $params['qty'] = $inputFilter->filter($params['qty']); } $quoteItem = $this->cart->getQuote()->getItemById($id); if (!$quoteItem) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __("The quote item isn't found. Verify the item and try again.") ); } - $item = $this->cart->updateItem($id, new \Magento\Framework\DataObject($params)); + $item = $this->cart->updateItem($id, new DataObject($params)); if (is_string($item)) { - throw new \Magento\Framework\Exception\LocalizedException(__($item)); + throw new LocalizedException(__($item)); } if ($item->getHasError()) { - throw new \Magento\Framework\Exception\LocalizedException(__($item->getMessage())); + throw new LocalizedException(__($item->getMessage())); } $related = $this->getRequest()->getParam('related_product'); @@ -73,7 +82,7 @@ public function execute() } return $this->_goBack($this->_url->getUrl('checkout/cart')); } - } catch (\Magento\Framework\Exception\LocalizedException $e) { + } catch (LocalizedException $e) { if ($this->_checkoutSession->getUseNotice(true)) { $this->messageManager->addNoticeMessage($e->getMessage()); } else { @@ -87,14 +96,15 @@ public function execute() if ($url) { return $this->resultRedirectFactory->create()->setUrl($url); } else { - $cartUrl = $this->_objectManager->get(\Magento\Checkout\Helper\Cart::class)->getCartUrl(); - return $this->resultRedirectFactory->create()->setUrl($this->_redirect->getRedirectUrl($cartUrl)); + $cartUrl = $this->_objectManager->get(CartHelper::class)->getCartUrl(); + return $this->resultRedirectFactory->create()->setUrl($cartUrl); } } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, __('We can\'t update the item right now.')); - $this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e); + $this->_objectManager->get(LoggerInterface::class)->critical($e); return $this->_goBack(); } + return $this->resultRedirectFactory->create()->setPath('*/*'); } } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php index 449fdb4224a57..34b1e949271d7 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Save.php @@ -3,13 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Cms\Controller\Adminhtml\Page; -use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Backend\App\Action; +use Magento\Backend\Model\View\Result\Redirect; +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; use Magento\Cms\Model\Page; +use Magento\Cms\Model\PageFactory; +use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Controller\ResultInterface; use Magento\Framework\Exception\LocalizedException; /** @@ -17,7 +24,7 @@ * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class Save extends \Magento\Backend\App\Action implements HttpPostActionInterface +class Save extends Action implements HttpPostActionInterface { /** * Authorization level of a basic admin session @@ -37,12 +44,12 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac protected $dataPersistor; /** - * @var \Magento\Cms\Model\PageFactory + * @var PageFactory */ private $pageFactory; /** - * @var \Magento\Cms\Api\PageRepositoryInterface + * @var PageRepositoryInterface */ private $pageRepository; @@ -50,21 +57,20 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac * @param Action\Context $context * @param PostDataProcessor $dataProcessor * @param DataPersistorInterface $dataPersistor - * @param \Magento\Cms\Model\PageFactory|null $pageFactory - * @param \Magento\Cms\Api\PageRepositoryInterface|null $pageRepository + * @param PageFactory|null $pageFactory + * @param PageRepositoryInterface|null $pageRepository */ public function __construct( Action\Context $context, PostDataProcessor $dataProcessor, DataPersistorInterface $dataPersistor, - \Magento\Cms\Model\PageFactory $pageFactory = null, - \Magento\Cms\Api\PageRepositoryInterface $pageRepository = null + PageFactory $pageFactory = null, + PageRepositoryInterface $pageRepository = null ) { $this->dataProcessor = $dataProcessor; $this->dataPersistor = $dataPersistor; - $this->pageFactory = $pageFactory ?: ObjectManager::getInstance()->get(\Magento\Cms\Model\PageFactory::class); - $this->pageRepository = $pageRepository - ?: ObjectManager::getInstance()->get(\Magento\Cms\Api\PageRepositoryInterface::class); + $this->pageFactory = $pageFactory ?: ObjectManager::getInstance()->get(PageFactory::class); + $this->pageRepository = $pageRepository ?: ObjectManager::getInstance()->get(PageRepositoryInterface::class); parent::__construct($context); } @@ -72,12 +78,12 @@ public function __construct( * Save action * * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @return \Magento\Framework\Controller\ResultInterface + * @return ResultInterface */ public function execute() { $data = $this->getRequest()->getPostValue(); - /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); if ($data) { $data = $this->dataProcessor->filter($data); @@ -88,7 +94,7 @@ public function execute() $data['page_id'] = null; } - /** @var \Magento\Cms\Model\Page $model */ + /** @var Page $model */ $model = $this->pageFactory->create(); $id = $this->getRequest()->getParam('page_id'); @@ -117,7 +123,7 @@ public function execute() } catch (LocalizedException $e) { $this->messageManager->addExceptionMessage($e->getPrevious() ?: $e); } catch (\Throwable $e) { - $this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the page.')); + $this->messageManager->addErrorMessage(__('Something went wrong while saving the page.')); } $this->dataPersistor->set('cms_page', $data); @@ -129,10 +135,10 @@ public function execute() /** * Process result redirect * - * @param \Magento\Cms\Api\Data\PageInterface $model - * @param \Magento\Backend\Model\View\Result\Redirect $resultRedirect + * @param PageInterface $model + * @param Redirect $resultRedirect * @param array $data - * @return \Magento\Backend\Model\View\Result\Redirect + * @return Redirect * @throws LocalizedException */ private function processResultRedirect($model, $resultRedirect, $data) @@ -149,7 +155,7 @@ private function processResultRedirect($model, $resultRedirect, $data) '*/*/edit', [ 'page_id' => $newPage->getId(), - '_current' => true + '_current' => true, ] ); } diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php index 67a7f0a934480..1cd85364b2cdd 100644 --- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php +++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Page/SaveTest.php @@ -1,4 +1,5 @@ <?php + /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. @@ -7,6 +8,7 @@ namespace Magento\Cms\Test\Unit\Controller\Adminhtml\Page; +use Magento\Backend\App\Action\Context; use Magento\Backend\Model\View\Result\Redirect; use Magento\Backend\Model\View\Result\RedirectFactory; use Magento\Cms\Api\PageRepositoryInterface; @@ -18,7 +20,6 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Message\ManagerInterface; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -82,13 +83,14 @@ class SaveTest extends TestCase */ private $pageId = 1; + /** + * @inheirtDoc + */ protected function setUp(): void { - $objectManager = new ObjectManager($this); - $this->resultRedirectFactory = $this->getMockBuilder(RedirectFactory::class) ->disableOriginalConstructor() - ->setMethods(['create']) + ->onlyMethods(['create']) ->getMock(); $this->resultRedirect = $this->getMockBuilder(Redirect::class) ->disableOriginalConstructor() @@ -98,7 +100,7 @@ protected function setUp(): void ->willReturn($this->resultRedirect); $this->dataProcessorMock = $this->getMockBuilder( PostDataProcessor::class - )->setMethods(['filter'])->disableOriginalConstructor() + )->onlyMethods(['filter'])->disableOriginalConstructor() ->getMock(); $this->dataPersistorMock = $this->getMockBuilder(DataPersistorInterface::class) ->getMock(); @@ -108,27 +110,28 @@ protected function setUp(): void $this->messageManagerMock = $this->getMockBuilder(ManagerInterface::class) ->getMockForAbstractClass(); $this->eventManagerMock = $this->getMockBuilder(\Magento\Framework\Event\ManagerInterface::class) - ->setMethods(['dispatch']) + ->onlyMethods(['dispatch']) ->getMockForAbstractClass(); $this->pageFactory = $this->getMockBuilder(PageFactory::class) ->disableOriginalConstructor() - ->setMethods(['create']) + ->onlyMethods(['create']) ->getMock(); $this->pageRepository = $this->getMockBuilder(PageRepositoryInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->saveController = $objectManager->getObject( - Save::class, - [ - 'request' => $this->requestMock, - 'messageManager' => $this->messageManagerMock, - 'eventManager' => $this->eventManagerMock, - 'resultRedirectFactory' => $this->resultRedirectFactory, - 'dataProcessor' => $this->dataProcessorMock, - 'dataPersistor' => $this->dataPersistorMock, - 'pageFactory' => $this->pageFactory, - 'pageRepository' => $this->pageRepository - ] + $context = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + $context->method('getRequest')->willReturn($this->requestMock); + $context->method('getMessageManager')->willReturn($this->messageManagerMock); + $context->method('getEventManager')->willReturn($this->eventManagerMock); + $context->method('getResultRedirectFactory')->willReturn($this->resultRedirectFactory); + $this->saveController = new Save( + $context, + $this->dataProcessorMock, + $this->dataPersistorMock, + $this->pageFactory, + $this->pageRepository ); } @@ -140,7 +143,7 @@ public function testSaveAction() 'stores' => ['0'], 'is_active' => true, 'content' => '"><script>alert("cookie: "+document.cookie)</script>', - 'back' => 'close' + 'back' => 'close', ]; $filteredPostData = [ @@ -149,7 +152,7 @@ public function testSaveAction() 'stores' => ['0'], 'is_active' => true, 'content' => '"><script>alert("cookie: "+document.cookie)</script>', - 'back' => 'close' + 'back' => 'close', ]; $this->dataProcessorMock->expects($this->any()) @@ -236,7 +239,7 @@ public function testSaveAndContinue() 'stores' => ['0'], 'is_active' => true, 'content' => '"><script>alert("cookie: "+document.cookie)</script>', - 'back' => 'continue' + 'back' => 'continue', ]; $this->requestMock->expects($this->any())->method('getPostValue')->willReturn($postData); $this->requestMock->expects($this->atLeastOnce()) @@ -304,12 +307,13 @@ public function testSaveActionThrowsException() $this->pageRepository->expects($this->once())->method('getById')->with($this->pageId)->willReturn($page); $page->expects($this->once())->method('setData'); $this->pageRepository->expects($this->once())->method('save')->with($page) - ->willThrowException(new \Exception('Error message.')); + ->willThrowException(new \Error('Error message.')); $this->messageManagerMock->expects($this->never()) ->method('addSuccessMessage'); $this->messageManagerMock->expects($this->once()) - ->method('addExceptionMessage'); + ->method('addErrorMessage') + ->with('Something went wrong while saving the page.'); $this->dataPersistorMock->expects($this->any()) ->method('set') @@ -318,7 +322,7 @@ public function testSaveActionThrowsException() [ 'page_id' => $this->pageId, 'layout_update_xml' => null, - 'custom_layout_update_xml' => null + 'custom_layout_update_xml' => null, ] ); diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomWelcomeMessageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomWelcomeMessageActionGroup.xml new file mode 100644 index 0000000000000..966529d280cfb --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontCustomWelcomeMessageActionGroup.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="AssertStorefrontCustomWelcomeMessageActionGroup"> + <annotations> + <description>Validates that the custom Welcome message is present on storefront header.</description> + </annotations> + <arguments> + <argument name="customMessage" type="string" defaultValue="Welcome to "Food & Drinks" store"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="waitForWelcomeMessage"/> + <see userInput="{{customMessage}}" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="verifyCustomMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml index ba6d9e0abec71..e5397e758d63f 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml @@ -12,7 +12,7 @@ ?> <?php $_product = $block->getProduct() ?> -<?php if ($_product->getIsSalable()) : ?> +<?php if ($_product->isAvailable()) : ?> <div class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> diff --git a/app/code/Magento/Reports/view/frontend/templates/product/widget/viewed/item.phtml b/app/code/Magento/Reports/view/frontend/templates/product/widget/viewed/item.phtml index 562c9a2b63a99..da11582a16133 100644 --- a/app/code/Magento/Reports/view/frontend/templates/product/widget/viewed/item.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/product/widget/viewed/item.phtml @@ -60,7 +60,7 @@ $rating = 'short'; </button> <?php endif; ?> <?php else : ?> - <?php if ($item->getIsSalable()) : ?> + <?php if ($item->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Reports/view/frontend/templates/widget/compared/column/compared_default_list.phtml b/app/code/Magento/Reports/view/frontend/templates/widget/compared/column/compared_default_list.phtml index a54259280e381..a9d5718449cd5 100644 --- a/app/code/Magento/Reports/view/frontend/templates/widget/compared/column/compared_default_list.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/widget/compared/column/compared_default_list.phtml @@ -78,7 +78,7 @@ if ($exist = $block->getRecentlyComparedProducts()) { <?php endif; ?> </div> <?php else : ?> - <?php if ($_product->getIsSalable()) : ?> + <?php if ($_product->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_grid.phtml b/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_grid.phtml index ad6b33820c752..1222490065185 100644 --- a/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_grid.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_grid.phtml @@ -83,7 +83,7 @@ if ($exist = $block->getRecentlyComparedProducts()) { </button> <?php endif; ?> <?php else : ?> - <?php if ($_item->getIsSalable()) : ?> + <?php if ($_item->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_list.phtml b/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_list.phtml index ba7a50eef6485..6f7b4f4f66f27 100644 --- a/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_list.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/widget/compared/content/compared_list.phtml @@ -84,7 +84,7 @@ if ($exist = $block->getRecentlyComparedProducts()) { </button> <?php endif; ?> <?php else : ?> - <?php if ($_item->getIsSalable()) : ?> + <?php if ($_item->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Reports/view/frontend/templates/widget/viewed/column/viewed_default_list.phtml b/app/code/Magento/Reports/view/frontend/templates/widget/viewed/column/viewed_default_list.phtml index 16fc2b070b95c..3e5cd15bbc62b 100644 --- a/app/code/Magento/Reports/view/frontend/templates/widget/viewed/column/viewed_default_list.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/widget/viewed/column/viewed_default_list.phtml @@ -81,7 +81,7 @@ if ($exist = ($block->getRecentlyViewedProducts() && $block->getRecentlyViewedPr <?php endif; ?> </div> <?php else : ?> - <?php if ($_product->getIsSalable()) : ?> + <?php if ($_product->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_grid.phtml b/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_grid.phtml index 567c3ebc57f9b..c2f98e72909d6 100644 --- a/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_grid.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_grid.phtml @@ -86,7 +86,7 @@ if ($exist = ($block->getRecentlyViewedProducts() && $block->getRecentlyViewedPr </button> <?php endif; ?> <?php else : ?> - <?php if ($_item->getIsSalable()) : ?> + <?php if ($_item->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_list.phtml b/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_list.phtml index 9a8bb9c3b734f..32cf0bc69d1e5 100644 --- a/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_list.phtml +++ b/app/code/Magento/Reports/view/frontend/templates/widget/viewed/content/viewed_list.phtml @@ -88,7 +88,7 @@ if ($exist = ($block->getRecentlyViewedProducts() && $block->getRecentlyViewedPr </button> <?php endif; ?> <?php else : ?> - <?php if ($_item->getIsSalable()) : ?> + <?php if ($_item->isAvailable()) : ?> <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> diff --git a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php index c2238ff1a3809..f88fecf84be6a 100644 --- a/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php +++ b/app/code/Magento/Shipping/Model/Carrier/AbstractCarrierOnline.php @@ -654,7 +654,7 @@ public function getMethodPrice($cost, $method = '') 'free_shipping_enable' ) && $this->getConfigData( 'free_shipping_subtotal' - ) <= $this->_rawRequest->getBaseSubtotalInclTax() ? '0.00' : $this->getFinalPriceWithHandlingFee( + ) <= $this->_rawRequest->getValueWithDiscount() ? '0.00' : $this->getFinalPriceWithHandlingFee( $cost ); } diff --git a/app/code/Magento/Theme/view/frontend/templates/html/header.phtml b/app/code/Magento/Theme/view/frontend/templates/html/header.phtml index cbefb82f23e33..c59ca42f50602 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/header.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/header.phtml @@ -6,6 +6,7 @@ /** * @var \Magento\Theme\Block\Html\Header $block + * @var \Magento\Framework\Escaper $escaper */ $welcomeMessage = $block->getWelcome(); ?> @@ -13,12 +14,12 @@ $welcomeMessage = $block->getWelcome(); <li class="greet welcome" data-bind="scope: 'customer'"> <!-- ko if: customer().fullname --> <span class="logged-in" - data-bind="text: new String('<?= $block->escapeHtml(__('Welcome, %1!', '%1')) ?>').replace('%1', customer().fullname)"> + data-bind="text: new String('<?= $escaper->escapeHtml(__('Welcome, %1!', '%1')) ?>').replace('%1', customer().fullname)"> </span> <!-- /ko --> <!-- ko ifnot: customer().fullname --> <span class="not-logged-in" - data-bind='html:"<?= $block->escapeHtml($welcomeMessage) ?>"'></span> + data-bind="html: '<?= $escaper->escapeHtmlAttr($welcomeMessage) ?>'"></span> <?= $block->getBlockHtml('header.additional') ?> <!-- /ko --> </li> diff --git a/app/code/Magento/Translation/Test/Mftf/Data/TranslationData.xml b/app/code/Magento/Translation/Test/Mftf/Data/TranslationData.xml index 4f787a52ba093..0b356cf9a73cb 100644 --- a/app/code/Magento/Translation/Test/Mftf/Data/TranslationData.xml +++ b/app/code/Magento/Translation/Test/Mftf/Data/TranslationData.xml @@ -171,4 +171,11 @@ <data key="original">Shipping Method:</data> <data key="custom">Shipping Method:</data> </entity> + <entity name="RevertWelcomeMessageTranslate" extends="CustomTranslationData"> + <requiredEntity type="translation_operation_translate">RevertWelcomeMessageTranslateData</requiredEntity> + </entity> + <entity name="RevertWelcomeMessageTranslateData" type="translation_operation_translate"> + <data key="original">Default welcome msg!</data> + <data key="custom">Default welcome msg!</data> + </entity> </entities> diff --git a/app/code/Magento/Translation/Test/Mftf/Section/StorefrontPanelHeaderTranslationSection.xml b/app/code/Magento/Translation/Test/Mftf/Section/StorefrontPanelHeaderTranslationSection.xml new file mode 100644 index 0000000000000..f58d24dfa1af7 --- /dev/null +++ b/app/code/Magento/Translation/Test/Mftf/Section/StorefrontPanelHeaderTranslationSection.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="StorefrontPanelHeaderTranslationSection"> + <element name="welcomeMessage" type="text" selector="header>.panel .greet.welcome span" /> + </section> +</sections> diff --git a/app/code/Magento/Translation/Test/Mftf/Test/StorefrontInlineTranslationWithQuoteSymbolsTest.xml b/app/code/Magento/Translation/Test/Mftf/Test/StorefrontInlineTranslationWithQuoteSymbolsTest.xml new file mode 100644 index 0000000000000..b24917c7fe818 --- /dev/null +++ b/app/code/Magento/Translation/Test/Mftf/Test/StorefrontInlineTranslationWithQuoteSymbolsTest.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="StorefrontInlineTranslationWithQuoteSymbolsTest"> + <annotations> + <features value="Translation"/> + <stories value="Inline Translation"/> + <title value="Inline translation with quote symbols"/> + <description value="As merchant I want to be able to rename text labels using quote symbols in it"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-41175"/> + <useCaseId value="MC-23989"/> + <group value="translation"/> + <group value="developer_mode_only"/> + </annotations> + + <before> + <createData entity="Simple_US_Customer_Multiple_Addresses" stepKey="createCustomer"/> + <magentoCLI command="config:set {{EnableTranslateInlineForStorefront.path}} {{EnableTranslateInlineForStorefront.value}}" stepKey="enableTranslateInlineStorefront"/> + <createData entity="RevertWelcomeMessageTranslate" stepKey="revertWelcomeMessageTranslation"> + <requiredEntity createDataKey="createCustomer"/> + </createData> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + <createData entity="SimpleProduct2" stepKey="createProductSecond"/> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanCacheAfterTranslateEnabled"> + <argument name="tags" value=""/> + </actionGroup> + </before> + + <after> + <magentoCLI command="config:set {{EnableTranslateInlineForStorefront.path}} {{EnableTranslateInlineForStorefront.value}}" stepKey="enableTranslateInlineStorefront"/> + <createData entity="RevertWelcomeMessageTranslate" stepKey="revertWelcomeMessageTranslation"> + <requiredEntity createDataKey="createCustomer"/> + </createData> + <magentoCLI command="config:set {{DisableTranslateInlineForStorefront.path}} {{DisableTranslateInlineForStorefront.value}}" stepKey="disableTranslateInlineForStorefront"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createProductSecond" stepKey="deleteProductSecond"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanCacheAfterTranslateDisabled"> + <argument name="tags" value=""/> + </actionGroup> + </after> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductPage"> + <argument name="productUrl" value="$createProduct.custom_attributes[url_key]$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontDefaultWelcomeMessageActionGroup" stepKey="assertDefaultWelcomeMessage"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addProductToCart"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeProductInMiniCart"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + + <actionGroup ref="AssertElementInTranslateInlineModeActionGroup" stepKey="assertWelcomeMessageInInlineTranslateMode"> + <argument name="elementSelector" value="{{StorefrontPanelHeaderTranslationSection.welcomeMessage}}"/> + </actionGroup> + <actionGroup ref="StorefrontOpenInlineTranslationPopupActionGroup" stepKey="openWelcomeMessageInlineTranslatePopup"> + <argument name="elementSelector" value="{{StorefrontPanelHeaderTranslationSection.welcomeMessage}}"/> + </actionGroup> + <actionGroup ref="StorefrontFillCustomTranslationFieldActionGroup" stepKey="fillInlineTranslateNewValue"> + <argument name="translateText" value="Welcome to "Food & Drinks" store"/> + </actionGroup> + <actionGroup ref="StorefrontSubmitInlineTranslationFormActionGroup" stepKey="saveInlineTranslateNewValue"/> + + <magentoCLI command="config:set {{DisableTranslateInlineForStorefront.path}} {{DisableTranslateInlineForStorefront.value}}" stepKey="disableTranslateInlineForStorefront"/> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanCacheAfterTranslateDisabled"> + <argument name="tags" value=""/> + </actionGroup> + + <actionGroup ref="ReloadPageActionGroup" stepKey="reloadPage"/> + <actionGroup ref="AssertStorefrontCustomWelcomeMessageActionGroup" stepKey="verifyTranslatedWelcomeMessage"/> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeProductInMiniCartAgain"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openSecondProductPage"> + <argument name="productUrl" value="$createProductSecond.custom_attributes[url_key]$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCustomWelcomeMessageActionGroup" stepKey="verifyTranslatedWelcomeMessageForSecondProduct"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondProductToCart"> + <argument name="productName" value="$createProductSecond.name$"/> + </actionGroup> + <actionGroup ref="AssertOneProductNameInMiniCartActionGroup" stepKey="seeSecondProductInMiniCart"> + <argument name="productName" value="$createProductSecond.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php index daff459d7ba3f..07da63fa8476e 100644 --- a/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php +++ b/app/code/Magento/Ups/Test/Unit/Model/CarrierTest.php @@ -226,7 +226,7 @@ public function testGetMethodPrice( ->willReturn($freeShippingEnabled); $request = new RateRequest(); - $request->setBaseSubtotalInclTax($requestSubtotal); + $request->setValueWithDiscount($requestSubtotal); $this->model->setRawRequest($request); $price = $this->model->getMethodPrice($cost, $shippingMethod); $this->assertEquals($expectedPrice, $price); diff --git a/app/code/Magento/Weee/Plugin/Catalog/Ui/Component/Listing/Columns.php b/app/code/Magento/Weee/Plugin/Catalog/Ui/Component/Listing/Columns.php new file mode 100644 index 0000000000000..8716884b6a5ea --- /dev/null +++ b/app/code/Magento/Weee/Plugin/Catalog/Ui/Component/Listing/Columns.php @@ -0,0 +1,49 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Plugin\Catalog\Ui\Component\Listing; + +use Magento\Catalog\Ui\Component\Listing\Attribute\Repository; +use Magento\Catalog\Ui\Component\Listing\Columns as DefaultColumns; +use Magento\Weee\Model\Attribute\Backend\Weee\Tax; + +/** + * Class Columns + */ +class Columns +{ + /** + * @var Repository + */ + private $attributeRepository; + + /** + * @param Repository $attributeRepository + */ + public function __construct( + Repository $attributeRepository + ) { + $this->attributeRepository = $attributeRepository; + } + + /** + * Makes column for FPT attribute in grid not sortable + * + * @param DefaultColumns $subject + */ + public function afterPrepare(DefaultColumns $subject) : void + { + foreach ($this->attributeRepository->getList() as $attribute) { + if ($attribute->getBackendModel() === Tax::class) { + $column = $subject->getComponent($attribute->getAttributeCode()); + $columnConfig = $column->getData('config'); + $columnConfig['sortable'] = false; + $column->setData('config', $columnConfig); + } + } + } +} diff --git a/app/code/Magento/Weee/etc/di.xml b/app/code/Magento/Weee/etc/di.xml index ccc849f4d8493..fa3fafc5a914a 100644 --- a/app/code/Magento/Weee/etc/di.xml +++ b/app/code/Magento/Weee/etc/di.xml @@ -84,4 +84,7 @@ <type name="Magento\Catalog\Model\ResourceModel\Attribute\RemoveProductAttributeData"> <plugin name="removeWeeAttributesData" type="Magento\Weee\Plugin\Catalog\ResourceModel\Attribute\RemoveProductWeeData" /> </type> + <type name="Magento\Catalog\Ui\Component\Listing\Columns"> + <plugin name="changeWeeColumnConfig" type="Magento\Weee\Plugin\Catalog\Ui\Component\Listing\Columns"/> + </type> </config> diff --git a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less index 3e9f2d4401b05..d88260e01b25d 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less +++ b/app/design/adminhtml/Magento/backend/Magento_Ui/web/css/source/module/_data-grid.less @@ -66,6 +66,10 @@ display: inline-block; overflow: hidden; width: 100%; + + &.white-space-preserved { + white-space: pre; + } } body._in-resize { diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php index 00c3133c25439..36bca76b28de1 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/CategoryTest.php @@ -8,14 +8,18 @@ namespace Magento\Catalog\Controller; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Category\Attribute\LayoutUpdateManager; use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel; +use Magento\Catalog\Model\ResourceModel\Category\Collection; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory; use Magento\Catalog\Model\Session; use Magento\Framework\App\Http\Context; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Registry; use Magento\Framework\View\LayoutInterface; +use Magento\Store\Model\Store; use Magento\TestFramework\Catalog\Model\CategoryLayoutUpdateManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\AbstractController; @@ -53,6 +57,11 @@ class CategoryTest extends AbstractController */ private $httpContext; + /** + * @var CollectionFactory + */ + private $categoryCollectionFactory; + /** * @inheritdoc */ @@ -64,6 +73,8 @@ protected function setUp(): void $this->objectManager->configure([ 'preferences' => [LayoutUpdateManager::class => CategoryLayoutUpdateManager::class] ]); + + $this->categoryCollectionFactory = $this->objectManager->create(CollectionFactory::class); $this->registry = $this->objectManager->get(Registry::class); $this->layout = $this->objectManager->get(LayoutInterface::class); $this->session = $this->objectManager->get(Session::class); @@ -233,4 +244,50 @@ public function testViewWithRememberPaginationAndPreviousValue(): void $this->assertEquals($newPaginationValue, $this->session->getData(ToolbarModel::LIMIT_PARAM_NAME)); $this->assertEquals($newPaginationValue, $this->httpContext->getValue(ToolbarModel::LIMIT_PARAM_NAME)); } + + /** + * Test to generate category page without duplicate html element ids + * + * @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php + * @magentoDataFixture Magento/Catalog/_files/catalog_category_product_reindex_all.php + * @magentoDataFixture Magento/Catalog/_files/catalog_product_category_reindex_all.php + * @magentoDbIsolation disabled + */ + public function testViewWithoutDuplicateHmlElementIds(): void + { + $category = $this->loadCategory('Category 999', Store::DEFAULT_STORE_ID); + $this->dispatch('catalog/category/view/id/' . $category->getId()); + + $responseHtml = $this->getResponse()->getBody(); + $htmlElementIds = ['modes-label', 'mode-list', 'toolbar-amount', 'sorter', 'limiter']; + foreach ($htmlElementIds as $elementId) { + $matches = []; + $idAttribute = "id=\"$elementId\""; + preg_match_all("/$idAttribute/mx", $responseHtml, $matches); + $this->assertCount(1, $matches[0]); + $this->assertEquals($idAttribute, $matches[0][0]); + } + } + + /** + * Loads category by id + * + * @param string $categoryName + * @param int $storeId + * @return CategoryInterface + */ + private function loadCategory(string $categoryName, int $storeId): CategoryInterface + { + /** @var Collection $categoryCollection */ + $categoryCollection = $this->categoryCollectionFactory->create(); + /** @var CategoryInterface $category */ + $category = $categoryCollection->setStoreId($storeId) + ->addAttributeToSelect('display_mode', 'left') + ->addAttributeToFilter(CategoryInterface::KEY_NAME, $categoryName) + ->setPageSize(1) + ->getFirstItem(); + $category->setStoreId($storeId); + + return $category; + } } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemOptionsTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemOptionsTest.php new file mode 100644 index 0000000000000..c7765b2d663f9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/UpdateItemOptionsTest.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Checkout\Controller\Cart; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\Message\MessageInterface; +use Magento\Quote\Model\Quote\Item as QuoteItem; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Integration tests for \Magento\Checkout\Controller\Cart\UpdateItemOptions class. + */ +class UpdateItemOptionsTest extends AbstractController +{ + /** + * @var FormKey + */ + private $formKey; + + /** + * @var CheckoutSession + */ + private $checkoutSession; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->formKey = $this->_objectManager->get(FormKey::class); + $this->checkoutSession = $this->_objectManager->get(CheckoutSession::class); + $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Tests that product is successfully updated in the shopping cart. + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product.php + */ + public function testUpdateProductOptionsInQuote() + { + $product = $this->productRepository->get('simple'); + $quoteItem = $this->checkoutSession->getQuote()->getItemByProduct($product); + $postData = $this->preparePostData($product, $quoteItem); + $this->dispatchUpdateItemOptionsRequest($postData); + $this->assertTrue($this->getResponse()->isRedirect()); + $this->assertRedirect($this->stringContains('/checkout/cart/')); + $message = (string)__( + '%1 was updated in your shopping cart.', + $product->getName() + ); + $this->assertSessionMessages( + $this->containsEqual($message), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Tests that product can't be updated with an empty shopping cart. + * + * @magentoAppArea frontend + * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product.php + */ + public function testUpdateProductOptionsWithEmptyQuote() + { + $product = $this->productRepository->get('simple'); + $quoteItem = $this->checkoutSession->getQuote()->getItemByProduct($product); + $postData = $this->preparePostData($product, $quoteItem); + $this->checkoutSession->clearQuote(); + $this->dispatchUpdateItemOptionsRequest($postData); + $this->assertTrue($this->getResponse()->isRedirect()); + $this->assertRedirect($this->stringContains('/checkout/cart/')); + $message = (string)__('The quote item isn't found. Verify the item and try again.'); + $this->assertSessionMessages( + $this->containsEqual($message), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Prepare post data for the request. + * + * @param ProductInterface $product + * @param QuoteItem|bool $quoteItem + * @return array + */ + private function preparePostData(ProductInterface $product, $quoteItem): array + { + return [ + 'product' => $product->getId(), + 'selected_configurable_option' => '', + 'related_product' => '', + 'item' => $quoteItem->getId(), + 'form_key' => $this->formKey->getFormKey(), + 'qty' => '2', + ]; + } + + /** + * Perform request for updating product options in a quote item. + * + * @param array $postData + * @return void + */ + private function dispatchUpdateItemOptionsRequest(array $postData): void + { + $this->getRequest()->setPostValue($postData); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('checkout/cart/updateItemOptions/id/' . $postData['item']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php index 552040489e253..57c6042ca25c9 100644 --- a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php +++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php @@ -442,17 +442,8 @@ private function getExpectedLabelRequestXml( */ public function testCollectRates() { - $requestData = $this->getRequestData(); - //phpcs:disable Magento2.Functions.DiscouragedFunction - $response = new Response( - 200, - [], - file_get_contents(__DIR__ . '/../_files/dhl_quote_response.xml') - ); - //phpcs:enable Magento2.Functions.DiscouragedFunction - $this->httpClient->nextResponses(array_fill(0, Carrier::UNAVAILABLE_DATE_LOOK_FORWARD + 1, $response)); - /** @var RateRequest $request */ - $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $this->setNextResponse(__DIR__ . '/../_files/dhl_quote_response.xml'); + $request = $this->createRequest(); $expectedRates = [ ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 45.85, 'method' => 'E', 'price' => 45.85], ['carrier' => 'dhl', 'carrier_title' => 'DHL Title', 'cost' => 35.26, 'method' => 'Q', 'price' => 35.26], @@ -487,11 +478,9 @@ public function testCollectRates() */ public function testCollectRatesWithoutDimensions(?string $size, ?string $height, ?string $width, ?string $depth) { - $requestData = $this->getRequestData(); $this->setDhlConfig(['size' => $size, 'height' => $height, 'width' => $width, 'depth' => $depth]); - /** @var RateRequest $request */ - $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $request = $this->createRequest(); $this->dhlCarrier = Bootstrap::getObjectManager()->create(Carrier::class); $this->dhlCarrier->collectRates($request)->getAllRates(); @@ -511,15 +500,13 @@ public function testCollectRatesWithoutDimensions(?string $size, ?string $height public function testGetRatesWithHttpException(): void { $this->setDhlConfig(['showmethod' => 1]); - $requestData = $this->getRequestData(); $deferredResponse = $this->getMockBuilder(HttpResponseDeferredInterface::class) ->onlyMethods(['get']) ->getMockForAbstractClass(); $exception = new HttpException('Exception message'); $deferredResponse->method('get')->willThrowException($exception); $this->httpClient->setDeferredResponseMock($deferredResponse); - /** @var RateRequest $request */ - $request = Bootstrap::getObjectManager()->create(RateRequest::class, $requestData); + $request = $this->createRequest(); $this->dhlCarrier = Bootstrap::getObjectManager()->create(Carrier::class); $resultRate = $this->dhlCarrier->collectRates($request)->getAllRates()[0]; $error = Bootstrap::getObjectManager()->get(Error::class); @@ -563,6 +550,77 @@ private function setDhlConfig(array $params) } } + /** + * Tests that the free rate is returned when sending a quotes request + * + * @param array $addRequestData + * @param bool $freeShippingExpects + * @magentoConfigFixture default_store carriers/dhl/active 1 + * @magentoConfigFixture default_store carriers/dhl/id some ID + * @magentoConfigFixture default_store carriers/dhl/shipment_days Mon,Tue,Wed,Thu,Fri,Sat + * @magentoConfigFixture default_store carriers/dhl/intl_shipment_days Mon,Tue,Wed,Thu,Fri,Sat + * @magentoConfigFixture default_store carriers/dhl/allowed_methods IE + * @magentoConfigFixture default_store carriers/dhl/international_service IE + * @magentoConfigFixture default_store carriers/dhl/gateway_url https://xmlpi-ea.dhl.com/XMLShippingServlet + * @magentoConfigFixture default_store carriers/dhl/id some ID + * @magentoConfigFixture default_store carriers/dhl/password some password + * @magentoConfigFixture default_store carriers/dhl/content_type N + * @magentoConfigFixture default_store carriers/dhl/nondoc_methods 1,3,4,8,P,Q,E,F,H,J,M,V,Y + * @magentoConfigFixture default_store carriers/dhl/showmethod' => 1, + * @magentoConfigFixture default_store carriers/dhl/title DHL Title + * @magentoConfigFixture default_store carriers/dhl/specificerrmsg dhl error message + * @magentoConfigFixture default_store carriers/dhl/unit_of_measure K + * @magentoConfigFixture default_store carriers/dhl/size 1 + * @magentoConfigFixture default_store carriers/dhl/height 1.6 + * @magentoConfigFixture default_store carriers/dhl/width 1.6 + * @magentoConfigFixture default_store carriers/dhl/depth 1.6 + * @magentoConfigFixture default_store carriers/dhl/debug 1 + * @magentoConfigFixture default_store carriers/dhl/free_method_nondoc P + * @magentoConfigFixture default_store carriers/dhl/free_shipping_enable 1 + * @magentoConfigFixture default_store carriers/dhl/free_shipping_subtotal 25 + * @magentoConfigFixture default_store shipping/origin/country_id GB + * @magentoAppIsolation enabled + * @dataProvider collectRatesWithFreeShippingDataProvider + */ + public function testCollectRatesWithFreeShipping(array $addRequestData, bool $freeShippingExpects): void + { + $this->setNextResponse(__DIR__ . '/../_files/dhl_quote_response.xml'); + $request = $this->createRequest($addRequestData); + + $actualRates = $this->dhlCarrier->collectRates($request)->getAllRates(); + $freeRateExists = false; + foreach ($actualRates as $actualRate) { + $actualRate = $actualRate->getData(); + if ($actualRate['method'] === 'P' && (float)$actualRate['price'] === 0.0) { + $freeRateExists = true; + break; + } + } + + self::assertEquals($freeShippingExpects, $freeRateExists); + } + + /** + * @return array + */ + public function collectRatesWithFreeShippingDataProvider(): array + { + return [ + [ + ['package_value' => 25, 'package_value_with_discount' => 22], + false + ], + [ + ['package_value' => 25, 'package_value_with_discount' => 25], + true + ], + [ + ['package_value' => 28, 'package_value_with_discount' => 25], + true + ], + ]; + } + /** * Returns request data. * @@ -571,47 +629,80 @@ private function setDhlConfig(array $params) private function getRequestData(): array { 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' => [], - ] + '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' => [], ]; } + + /** + * Set next response content from file + * + * @param string $file + */ + private function setNextResponse(string $file): void + { + //phpcs:disable Magento2.Functions.DiscouragedFunction + $response = new Response( + 200, + [], + file_get_contents($file) + ); + //phpcs:enable Magento2.Functions.DiscouragedFunction + $this->httpClient->nextResponses( + array_fill(0, Carrier::UNAVAILABLE_DATE_LOOK_FORWARD + 1, $response) + ); + } + + /** + * Create Rate Request + * + * @param array $addRequestData + * @return RateRequest + */ + private function createRequest(array $addRequestData = []): RateRequest + { + $requestData = $this->getRequestData(); + if (!empty($addRequestData)) { + $requestData = array_merge($requestData, $addRequestData); + } + + return Bootstrap::getObjectManager()->create(RateRequest::class, ['data' => $requestData]); + } } diff --git a/dev/tests/integration/testsuite/Magento/Weee/Plugin/Catalog/Ui/Component/Listing/ColumnsTest.php b/dev/tests/integration/testsuite/Magento/Weee/Plugin/Catalog/Ui/Component/Listing/ColumnsTest.php new file mode 100644 index 0000000000000..31746a7e381c0 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Weee/Plugin/Catalog/Ui/Component/Listing/ColumnsTest.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Weee\Plugin\Catalog\Ui\Component\Listing; + +use Magento\Catalog\Ui\Component\Listing\Attribute\Repository; +use Magento\Catalog\Ui\Component\Listing\Columns; +use Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Class ColumnsTest + * Check if FPT attribute column in product grid won't be sortable + */ +class ColumnsTest extends TestCase +{ + /** + * @var Columns + */ + private $columns; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $objectManager = Bootstrap::getObjectManager(); + $attributeRepository = $objectManager->get(Repository::class); + $dataProvider = $objectManager->create( + ProductDataProvider::class, + [ + 'name' => "product_listing_data_source", + 'primaryFieldName' => "entity_id", + 'requestFieldName' => "id", + ] + ); + $context = $objectManager->create(ContextInterface::class); + $context->setDataProvider($dataProvider); + $this->columns = $objectManager->create( + Columns::class, + ['attributeRepository' => $attributeRepository, 'context' => $context] + ); + } + + /** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Weee/_files/fixed_product_attribute.php + */ + public function testGetProductWeeeAttributesConfig() + { + $this->columns->prepare(); + $column = $this->columns->getComponent('fixed_product_attribute'); + $columnConfig = $column->getData('config'); + $this->assertArrayHasKey('sortable', $columnConfig); + $this->assertFalse($columnConfig['sortable']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Weee/_files/fixed_product_attribute.php b/dev/tests/integration/testsuite/Magento/Weee/_files/fixed_product_attribute.php index a74305d7db424..f75a10a07979f 100644 --- a/dev/tests/integration/testsuite/Magento/Weee/_files/fixed_product_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Weee/_files/fixed_product_attribute.php @@ -6,6 +6,9 @@ declare(strict_types=1); +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; +Resolver::getInstance()->requireDataFixture('Magento/Weee/_files/fixed_product_attribute_rollback.php'); + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); /** @var \Magento\Eav\Model\Entity\Attribute\Set $attributeSet */ @@ -27,6 +30,7 @@ 'attribute_group_id' => $attributeGroupId, 'frontend_input' => 'weee', 'frontend_label' => 'fixed product tax', + 'is_used_in_grid' => '1', ]; /** @var \Magento\Catalog\Model\Entity\Attribute $attribute */ diff --git a/lib/internal/Magento/Framework/App/FrontController.php b/lib/internal/Magento/Framework/App/FrontController.php index 2f96b24176943..f80d17c072f94 100644 --- a/lib/internal/Magento/Framework/App/FrontController.php +++ b/lib/internal/Magento/Framework/App/FrontController.php @@ -184,6 +184,10 @@ private function processRequest( //Validating a request only once. if (!$this->validatedRequest) { + $area = $this->areaList->getArea($this->appState->getAreaCode()); + $area->load(Area::PART_DESIGN); + $area->load(Area::PART_TRANSLATE); + try { $this->requestValidator->validate($request, $actionInstance); } catch (InvalidRequestException $exception) { @@ -193,9 +197,6 @@ private function processRequest( ["exception" => $exception] ); $result = $exception->getReplaceResult(); - $area = $this->areaList->getArea($this->appState->getAreaCode()); - $area->load(Area::PART_DESIGN); - $area->load(Area::PART_TRANSLATE); if ($messages = $exception->getMessages()) { foreach ($messages as $message) { $this->messages->addErrorMessage($message); diff --git a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php index 20da38100b0f4..fdb109ce30708 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/FrontControllerTest.php @@ -226,7 +226,10 @@ public function testDispatched() $this->routerList->expects($this->any()) ->method('current') ->willReturn($this->router); - + $this->appStateMock->expects($this->any())->method('getAreaCode')->willReturn('frontend'); + $this->areaMock->expects($this->at(0))->method('load')->with(Area::PART_DESIGN)->willReturnSelf(); + $this->areaMock->expects($this->at(1))->method('load')->with(Area::PART_TRANSLATE)->willReturnSelf(); + $this->areaListMock->expects($this->any())->method('getArea')->willReturn($this->areaMock); $this->request->expects($this->at(0))->method('isDispatched')->willReturn(false); $this->request->expects($this->at(1))->method('setDispatched')->with(true); $this->request->expects($this->at(2))->method('isDispatched')->willReturn(true); @@ -261,6 +264,10 @@ public function testDispatchedNotFoundException() ->method('current') ->willReturn($this->router); + $this->appStateMock->expects($this->any())->method('getAreaCode')->willReturn('frontend'); + $this->areaMock->expects($this->at(0))->method('load')->with(Area::PART_DESIGN)->willReturnSelf(); + $this->areaMock->expects($this->at(1))->method('load')->with(Area::PART_TRANSLATE)->willReturnSelf(); + $this->areaListMock->expects($this->any())->method('getArea')->willReturn($this->areaMock); $this->request->expects($this->at(0))->method('isDispatched')->willReturn(false); $this->request->expects($this->at(1))->method('initForward'); $this->request->expects($this->at(2))->method('setActionName')->with('noroute');