diff --git a/app/code/Magento/Backend/Controller/Adminhtml/Noroute/Index.php b/app/code/Magento/Backend/Controller/Adminhtml/Noroute/Index.php index 64038d17f14c64cee9a0cd01ef3bf2cfdccc05da..87153c381c1f7d053a40d67ec784f62d02375084 100644 --- a/app/code/Magento/Backend/Controller/Adminhtml/Noroute/Index.php +++ b/app/code/Magento/Backend/Controller/Adminhtml/Noroute/Index.php @@ -41,7 +41,7 @@ class Index extends \Magento\Backend\App\Action { /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->resultPageFactory->create(); - $resultPage->setStatusHeader(404, '1.1', 'Forbidden'); + $resultPage->setStatusHeader(404, '1.1', 'Not Found'); $resultPage->setHeader('Status', '404 File not found'); $resultPage->addHandle('adminhtml_noroute'); return $resultPage; diff --git a/app/code/Magento/Backend/etc/adminhtml/di.xml b/app/code/Magento/Backend/etc/adminhtml/di.xml index 5154c4eb56c915546545675ad6562d8684daf136..97a39139411c0bd4b48dc3e5285170f73e40e322 100644 --- a/app/code/Magento/Backend/etc/adminhtml/di.xml +++ b/app/code/Magento/Backend/etc/adminhtml/di.xml @@ -147,4 +147,9 @@ </argument> </arguments> </type> + <type name="Magento\Framework\View\Layout\Generator\Block"> + <arguments> + <argument name="defaultClass" xsi:type="string">Magento\Backend\Block\Template</argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Bundle/Model/OptionRepository.php b/app/code/Magento/Bundle/Model/OptionRepository.php index 43e46ef7f1c893fd931c6af3f43cf0dc9a1caf41..9940344b5b61cc6bdfb2bbeac88edd2977bd91c0 100644 --- a/app/code/Magento/Bundle/Model/OptionRepository.php +++ b/app/code/Magento/Bundle/Model/OptionRepository.php @@ -208,7 +208,7 @@ class OptionRepository implements \Magento\Bundle\Api\ProductOptionRepositoryInt } } else { if (!$existingOption->getOptionId()) { - throw new NoSuchEntityException('Requested option doesn\'t exist'); + throw new NoSuchEntityException(__('Requested option doesn\'t exist')); } $option->setData(array_merge($existingOption->getData(), $option->getData())); diff --git a/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js b/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js index 82033590b1135033f378b36f1649c6d8f6b4d27c..79050ca087740f26ce907fce33d8b7e775e36b55 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/region-updater.js @@ -206,7 +206,7 @@ define([ regionInput.removeClass('required-entry'); } - regionList.removeClass('required-entry').hide(); + regionList.removeClass('required-entry').prop('disabled', 'disabled').hide(); regionInput.show(); label.attr('for', regionInput.attr('id')); } diff --git a/app/code/Magento/Review/Block/Product/ReviewRenderer.php b/app/code/Magento/Review/Block/Product/ReviewRenderer.php index f60b33bffb8cb5a57cfbc36b7ae509c31cdcbda7..8aa10d2437cbbf21b157b4ac3b2ec68303c982f5 100644 --- a/app/code/Magento/Review/Block/Product/ReviewRenderer.php +++ b/app/code/Magento/Review/Block/Product/ReviewRenderer.php @@ -57,6 +57,10 @@ class ReviewRenderer extends \Magento\Framework\View\Element\Template implements $templateType = self::DEFAULT_VIEW, $displayIfNoReviews = false ) { + if (!$product->getRatingSummary()) { + $this->_reviewFactory->create()->getEntitySummary($product, $this->_storeManager->getStore()->getId()); + } + if (!$product->getRatingSummary() && !$displayIfNoReviews) { return ''; } @@ -68,9 +72,6 @@ class ReviewRenderer extends \Magento\Framework\View\Element\Template implements $this->setDisplayIfEmpty($displayIfNoReviews); - if (!$product->getRatingSummary()) { - $this->_reviewFactory->create()->getEntitySummary($product, $this->_storeManager->getStore()->getId()); - } $this->setProduct($product); return $this->toHtml(); diff --git a/app/code/Magento/Search/view/frontend/web/form-mini.js b/app/code/Magento/Search/view/frontend/web/form-mini.js index 7b2c7003074cbd176770c6258a6d23d5eba91a10..e8598f46eb5be7a1b70f8637623eddf46eb9578f 100644 --- a/app/code/Magento/Search/view/frontend/web/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/form-mini.js @@ -286,46 +286,50 @@ define([ $.getJSON(this.options.url, { q: value }, $.proxy(function (data) { - $.each(data, function (index, element) { - var html; - - element.index = index; - html = template({ - data: element + if (data.length) { + $.each(data, function (index, element) { + var html; + + element.index = index; + html = template({ + data: element + }); + dropdown.append(html); }); - dropdown.append(html); - }); - this.responseList.indexList = this.autoComplete.html(dropdown) - .css(clonePosition) - .show() - .find(this.options.responseFieldElements + ':visible'); - - this._resetResponseList(false); - this.element.removeAttr('aria-activedescendant'); - - if (this.responseList.indexList.length) { - this._updateAriaHasPopup(true); - } else { - this._updateAriaHasPopup(false); - } - this.responseList.indexList - .on('click', function (e) { - this.responseList.selected = $(e.currentTarget); - this.searchForm.trigger('submit'); - }.bind(this)) - .on('mouseenter mouseleave', function (e) { - this.responseList.indexList.removeClass(this.options.selectClass); - $(e.target).addClass(this.options.selectClass); - this.responseList.selected = $(e.target); - this.element.attr('aria-activedescendant', $(e.target).attr('id')); - }.bind(this)) - .on('mouseout', function (e) { - if (!this._getLastElement() && this._getLastElement().hasClass(this.options.selectClass)) { - $(e.target).removeClass(this.options.selectClass); - this._resetResponseList(false); - } - }.bind(this)); + this.responseList.indexList = this.autoComplete.html(dropdown) + .css(clonePosition) + .show() + .find(this.options.responseFieldElements + ':visible'); + + this._resetResponseList(false); + this.element.removeAttr('aria-activedescendant'); + + if (this.responseList.indexList.length) { + this._updateAriaHasPopup(true); + } else { + this._updateAriaHasPopup(false); + } + + this.responseList.indexList + .on('click', function (e) { + this.responseList.selected = $(e.currentTarget); + this.searchForm.trigger('submit'); + }.bind(this)) + .on('mouseenter mouseleave', function (e) { + this.responseList.indexList.removeClass(this.options.selectClass); + $(e.target).addClass(this.options.selectClass); + this.responseList.selected = $(e.target); + this.element.attr('aria-activedescendant', $(e.target).attr('id')); + }.bind(this)) + .on('mouseout', function (e) { + if (!this._getLastElement() && + this._getLastElement().hasClass(this.options.selectClass)) { + $(e.target).removeClass(this.options.selectClass); + this._resetResponseList(false); + } + }.bind(this)); + } }, this)); } else { this._resetResponseList(true); diff --git a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php index 7a1bbf986de6149068f8d1cdf9c92f0b3d3c2bc9..a0c687bde1b478334e7cee68c378528fc62499bf 100644 --- a/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php +++ b/app/code/Magento/Ui/Controller/Adminhtml/Index/Render.php @@ -7,13 +7,56 @@ namespace Magento\Ui\Controller\Adminhtml\Index; use Magento\Ui\Controller\Adminhtml\AbstractAction; use Magento\Framework\View\Element\UiComponentInterface; +use Magento\Backend\App\Action\Context; +use Magento\Framework\View\Element\UiComponentFactory; +use Psr\Log\LoggerInterface; +use Magento\Framework\Escaper; +use Magento\Framework\Controller\Result\JsonFactory; class Render extends AbstractAction { + /** + * @var JsonFactory + */ + private $resultJsonFactory; + + /** + * @var Escaper + */ + private $escaper; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @param Context $context + * @param UiComponentFactory $factory + * @param JsonFactory|null $resultJsonFactory + * @param Escaper|null $escaper + * @param LoggerInterface|null $logger + */ + public function __construct( + Context $context, + UiComponentFactory $factory, + JsonFactory $resultJsonFactory = null, + Escaper $escaper = null, + LoggerInterface $logger = null + ) { + parent::__construct($context, $factory); + $this->resultJsonFactory = $resultJsonFactory ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Controller\Result\JsonFactory::class); + $this->escaper = $escaper ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Escaper::class); + $this->logger = $logger ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Psr\Log\LoggerInterface::class); + } + /** * Action for AJAX request * - * @return void + * @return void|\Magento\Framework\Controller\ResultInterface */ public function execute() { @@ -22,10 +65,40 @@ class Render extends AbstractAction return; } - $component = $this->factory->create($this->_request->getParam('namespace')); - if ($this->validateAclResource($component->getContext()->getDataProvider()->getConfigData())) { - $this->prepareComponent($component); - $this->_response->appendBody((string) $component->render()); + try { + $component = $this->factory->create($this->_request->getParam('namespace')); + if ($this->validateAclResource($component->getContext()->getDataProvider()->getConfigData())) { + $this->prepareComponent($component); + $this->_response->appendBody((string) $component->render()); + } + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->logger->critical($e); + $result = [ + 'error' => $this->escaper->escapeHtml($e->getMessage()), + 'errorcode' => $this->escaper->escapeHtml($e->getCode()) + ]; + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setStatusHeader( + \Zend\Http\Response::STATUS_CODE_400, + \Zend\Http\AbstractMessage::VERSION_11, + 'Bad Request' + ); + return $resultJson->setData($result); + } catch (\Exception $e) { + $this->logger->critical($e); + $result = [ + 'error' => _('UI component could not be rendered because of system exception'), + 'errorcode' => $this->escaper->escapeHtml($e->getCode()) + ]; + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultJsonFactory->create(); + $resultJson->setStatusHeader( + \Zend\Http\Response::STATUS_CODE_400, + \Zend\Http\AbstractMessage::VERSION_11, + 'Bad Request' + ); + return $resultJson->setData($result); } } diff --git a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php index 5ab08a8a64a199231246c59252dca72cf9b57186..7d5de26be6d2ef856ecd922aadf7e2c4fe9ee1f5 100644 --- a/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php +++ b/app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/RenderTest.php @@ -5,7 +5,8 @@ */ namespace Magento\Ui\Test\Unit\Controller\Adminhtml\Index; -use \Magento\Ui\Controller\Adminhtml\Index\Render; +use Magento\Ui\Controller\Adminhtml\Index\Render; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -17,6 +18,11 @@ class RenderTest extends \PHPUnit\Framework\TestCase */ private $render; + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + /** * @var \PHPUnit_Framework_MockObject_MockObject */ @@ -73,6 +79,16 @@ class RenderTest extends \PHPUnit\Framework\TestCase */ private $uiComponentMock; + /** + * @var \Magento\Framework\Controller\Result\JsonFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $resultJsonFactoryMock; + + /** + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $loggerMock; + protected function setUp() { $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class) @@ -114,6 +130,14 @@ class RenderTest extends \PHPUnit\Framework\TestCase ['render'] ); + $this->resultJsonFactoryMock = $this->getMockBuilder( + \Magento\Framework\Controller\Result\JsonFactory::class + ) + ->disableOriginalConstructor() + ->getMock(); + + $this->loggerMock = $this->getMockForAbstractClass(\Psr\Log\LoggerInterface::class); + $this->contextMock->expects($this->any()) ->method('getRequest') ->willReturn($this->requestMock); @@ -136,7 +160,70 @@ class RenderTest extends \PHPUnit\Framework\TestCase ->method('getDataProvider') ->willReturn($this->dataProviderMock); - $this->render = new Render($this->contextMock, $this->uiFactoryMock); + $this->objectManagerHelper = new ObjectManagerHelper($this); + + $this->render = $this->objectManagerHelper->getObject( + \Magento\Ui\Controller\Adminhtml\Index\Render::class, + [ + 'context' => $this->contextMock, + 'factory' => $this->uiFactoryMock, + 'resultJsonFactory' => $this->resultJsonFactoryMock, + 'logger' => $this->loggerMock + ] + ); + } + + public function testExecuteAjaxRequestException() + { + $name = 'test-name'; + $renderedData = '<html>data</html>'; + + $this->requestMock->expects($this->any()) + ->method('getParam') + ->with('namespace') + ->willReturn($name); + $this->requestMock->expects($this->any()) + ->method('getParams') + ->willReturn([]); + $this->responseMock->expects($this->once()) + ->method('appendBody') + ->willThrowException(new \Exception('exception')); + + $jsonResultMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class) + ->disableOriginalConstructor() + ->setMethods(['setData']) + ->getMock(); + + $this->resultJsonFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($jsonResultMock); + + $jsonResultMock->expects($this->once()) + ->method('setData') + ->willReturnSelf(); + + $this->loggerMock->expects($this->once()) + ->method('critical') + ->willReturnSelf(); + + $this->dataProviderMock->expects($this->once()) + ->method('getConfigData') + ->willReturn([]); + + $this->uiComponentMock->expects($this->once()) + ->method('render') + ->willReturn($renderedData); + $this->uiComponentMock->expects($this->once()) + ->method('getChildComponents') + ->willReturn([]); + $this->uiComponentMock->expects($this->once()) + ->method('getContext') + ->willReturn($this->uiComponentContextMock); + $this->uiFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->uiComponentMock); + + $this->render->executeAjaxRequest(); } public function testExecuteAjaxRequest() diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index 22ba19657168d753355eab375f8e85f3b1e7825a..ecc4ec1902d87a7c334e218fdf629707b9747a8d 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -10,8 +10,10 @@ define([ 'underscore', 'mageUtils', 'uiLayout', - 'uiCollection' -], function (_, utils, layout, Collection) { + 'uiCollection', + 'mage/translate', + 'jquery' +], function (_, utils, layout, Collection, $t, $) { 'use strict'; /** @@ -48,6 +50,7 @@ define([ stickyTmpl: 'ui/grid/sticky/filters', _processed: [], columnsProvider: 'ns = ${ $.ns }, componentType = columns', + bookmarksProvider: 'ns = ${ $.ns }, componentType = bookmark', applied: { placeholder: true }, @@ -102,7 +105,9 @@ define([ applied: '${ $.provider }:params.filters' }, imports: { - 'onColumnsUpdate': '${ $.columnsProvider }:elems' + onColumnsUpdate: '${ $.columnsProvider }:elems', + onBackendError: '${ $.provider }:lastError', + bookmarksActiveIndex: '${ $.bookmarksProvider }:activeIndex' }, modules: { columns: '${ $.columnsProvider }', @@ -371,6 +376,37 @@ define([ */ onColumnsUpdate: function (columns) { columns.forEach(this.addFilter, this); + }, + + /** + * Provider ajax error listener. + * + * @param {bool} isError - Selected index of the filter. + */ + onBackendError: function (isError) { + var defaultMessage = 'Something went wrong with processing the default view and we have restored the ' + + 'filter to its original state.', + customMessage = 'Something went wrong with processing current custom view and filters have been ' + + 'reset to its original state. Please edit filters then click apply.'; + + if (isError) { + this.clear(); + + $('body').notification('clear') + .notification('add', { + error: true, + message: $.mage.__(this.bookmarksActiveIndex !== 'default' ? customMessage : defaultMessage), + + /** + * @param {String} message + */ + insertMethod: function (message) { + var $wrapper = $('<div/>').html(message); + + $('.page-main-actions').after($wrapper); + } + }); + } } }); }); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/provider.js b/app/code/Magento/Ui/view/base/web/js/grid/provider.js index 14a64216ef597988575f72330be0f382562b206a..25bf25e6f65c02477bf9ba38a3bf4db9cc40d966 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/provider.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/provider.js @@ -21,6 +21,7 @@ define([ return Element.extend({ defaults: { firstLoad: true, + lastError: false, storageConfig: { component: 'Magento_Ui/js/grid/data-storage', provider: '${ $.storageConfig.name }', @@ -120,7 +121,7 @@ define([ request .done(this.onReload) - .fail(this.onError); + .fail(this.onError.bind(this)); return request; }, @@ -144,6 +145,10 @@ define([ return; } + this.set('lastError', true); + + this.firstLoad = false; + alert({ content: $t('Something went wrong.') }); @@ -157,6 +162,8 @@ define([ onReload: function (data) { this.firstLoad = false; + this.set('lastError', false); + this.setData(data) .trigger('reloaded'); }, 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 e673431cd43ab3b21f47bba707f2a34261673898..76b00f56e780c52ef0640a2556c19dc1aad413f9 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 @@ -244,7 +244,7 @@ define([ ], 'stripped-min-length': [ function (value, param) { - return value.length >= param; + return _.isUndefined(value) || value.length === 0 || utils.stripHtml(value).length >= param; }, $.mage.__('Please enter at least {0} characters') ], 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 ae3857d8e4f6dce09fb3db380c6bc3299cb15a7a..87a3ed048dbb9af62ba28191cd29a33012d48dde 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 @@ -99,6 +99,18 @@ class StoreGrid extends Grid $this->_rootElement->find(sprintf($this->storeName, $websiteName), Locator::SELECTOR_XPATH)->click(); } + /** + * Search and open appropriate Website by name. + * + * @param string $websiteName + * @return void + */ + public function searchAndOpenWebsiteByName($websiteName) + { + $this->search(['website_title' => $websiteName]); + $this->_rootElement->find(sprintf($this->storeName, $websiteName), Locator::SELECTOR_XPATH)->click(); + } + /** * Search and open appropriate Store View. * diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php index 67ef167e3fcddcb1515eb749782d0bbfc47fb419..29efb2b4fa03712b3427ed955d508d71fbeea5ba 100755 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Grid.php @@ -60,6 +60,10 @@ class Grid extends DataGrid 'selector' => '[name="attribute_set_id"]', 'input' => 'select', ], + 'store_id' => [ + 'selector' => '[name="store_id"]', + 'input' => 'select', + ], ]; /** diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductGridIsRendered.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductGridIsRendered.php new file mode 100644 index 0000000000000000000000000000000000000000..1d5c1e33c94d668fcd4cd2c2c07a2146e1adb0db --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductGridIsRendered.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +/** + * Assert that product grid is rendered correctly. + */ +class AssertProductGridIsRendered extends \Magento\Mtf\Constraint\AbstractConstraint +{ + /** + * Assert that product grid is rendered correctly. + * + * @param \Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex $catalogProductIndex + * @return void + */ + public function processAssert( + \Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex $catalogProductIndex + ) { + $productId = $catalogProductIndex->open()->getProductGrid()->getFirstItemId(); + \PHPUnit_Framework_Assert::assertNotNull( + $productId, + 'Product grid is not rendered correctly.' + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Product grid is rendered correctly.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertResetFilterMessage.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertResetFilterMessage.php new file mode 100644 index 0000000000000000000000000000000000000000..5816744e7a3b23500001bb282673d2901a142f44 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertResetFilterMessage.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +/** + * Assert that filters have been reset successfully. + */ +class AssertResetFilterMessage extends \Magento\Mtf\Constraint\AbstractConstraint +{ + /** + * Assert message that filters have been reset. + * + * @param \Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex $catalogProductIndex + * @return void + */ + public function processAssert( + \Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex $catalogProductIndex + ) { + \PHPUnit_Framework_Assert::assertContains( + 'restored the filter to its original state', + $catalogProductIndex->getMessagesBlock()->getErrorMessage() + ); + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Filters have been reset successfully.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/GridFilteringDeletedEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/GridFilteringDeletedEntityTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..e49d4e3a9ccd4f80b42c0a86aee0b3041b11e12e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/GridFilteringDeletedEntityTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + --> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> + <testCase name="Magento\Ui\Test\TestCase\GridFilteringDeletedEntityTest" summary="Grid filtering by deleted entity" ticketId="MAGETWO-71940"> + <variation name="GridFilteringDeletedEntityTestVariation1"> + <data name="tag" xsi:type="string">severity:S2</data> + <data name="steps" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="0" xsi:type="string">Magento\Store\Test\TestStep\DeleteWebsitesEntityStep</item> + </item> + </data> + <data name="fixtureName" xsi:type="string">catalogProductSimple</data> + <data name="fixtureDataSet" xsi:type="string">product_with_additional_website</data> + <data name="filters" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="name" xsi:type="string">:name</item> + </item> + </data> + <data name="pageClass" xsi:type="string">Magento\Catalog\Test\Page\Adminhtml\CatalogProductIndex</data> + <data name="gridRetriever" xsi:type="string">getProductGrid</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductGridIsRendered"/> + <constraint name="Magento\Catalog\Test\Constraint\AssertResetFilterMessage"/> + </variation> + </testCase> +</config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml index d9c73fe52277c2a46c443a7aa3252361e9ddb55f..a524139d3f3ad602eceb155a642322e1b7c8be92 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct.xml @@ -1147,5 +1147,42 @@ <item name="value" xsi:type="string">170</item> </field> </dataset> + + <dataset name="with_only_two_options"> + <field name="name" xsi:type="string">Configurable product with two options %isolation%</field> + <field name="sku" xsi:type="string">sku_test_configurable_product_with_two_options_%isolation%</field> + <field name="price" xsi:type="array"> + <item name="value" xsi:type="string">40</item> + <item name="dataset" xsi:type="string">price_40</item> + </field> + <field name="product_has_weight" xsi:type="string">This item has weight</field> + <field name="weight" xsi:type="string">30</field> + <field name="status" xsi:type="string">Yes</field> + <field name="visibility" xsi:type="string">Catalog, Search</field> + <field name="tax_class_id" xsi:type="array"> + <item name="dataset" xsi:type="string">taxable_goods</item> + </field> + <field name="url_key" xsi:type="string">configurable-product-with-two-options-%isolation%</field> + <field name="configurable_attributes_data" xsi:type="array"> + <item name="dataset" xsi:type="string">two_options</item> + </field> + <field name="quantity_and_stock_status" xsi:type="array"> + <item name="is_in_stock" xsi:type="string">In Stock</item> + </field> + <field name="website_ids" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </item> + </field> + <field name="attribute_set_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="checkout_data" xsi:type="array"> + <item name="dataset" xsi:type="string">configurable_default_with_two_options</item> + </field> + <field name="category_ids" xsi:type="array"> + <item name="dataset" xsi:type="string">default_subcategory</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml index 003c129c9f9694c47bd0b06bad04873317603a76..136696ff81047d51e58bf50c0808680704f47b1c 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/Repository/ConfigurableProduct/CheckoutData.xml @@ -466,5 +466,26 @@ <item name="subtotal" xsi:type="string">15</item> </field> </dataset> + + <dataset name="configurable_default_with_two_options"> + <field name="options" xsi:type="array"> + <item name="configurable_options" xsi:type="array"> + <item name="0" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_0</item> + </item> + <item name="1" xsi:type="array"> + <item name="title" xsi:type="string">attribute_key_0</item> + <item name="value" xsi:type="string">option_key_1</item> + </item> + </item> + </field> + <field name="qty" xsi:type="string">3</field> + <field name="cartItem" xsi:type="array"> + <item name="price" xsi:type="string">40</item> + <item name="qty" xsi:type="string">3</item> + <item name="subtotal" xsi:type="string">120</item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Product/Sold/Grid.php b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Product/Sold/Grid.php index f74fe80f69eb3b009a3995d32c9b716739a524e0..a9233a1574e07adc6d6f1e4d7112097b58947a35 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Product/Sold/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Block/Adminhtml/Product/Sold/Grid.php @@ -34,6 +34,13 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid */ protected $product = './/*[contains(.,"%s")]/*[contains(@class,"col-qty")]'; + /** + * Product sku from grid locator + * + * @var string + */ + protected $productSku = './/*[contains(.,"%s")]/*[contains(@class,"col-sku")]'; + /** * Filter locator * @@ -102,4 +109,21 @@ class Grid extends \Magento\Backend\Test\Block\Widget\Grid } return $views; } + + /** + * Get product sku from Ordered Products Report grid + * + * @param OrderInjectable $order + * @return array + */ + public function getOrdersResultsforConfigurableProducts(OrderInjectable $order) + { + $products = $order->getEntityId()['products']; + $skus = []; + foreach ($products as $key => $productSku) { + $skus[$key] = $this->_rootElement + ->find(sprintf($this->productSku, $productSku->getName()), Locator::SELECTOR_XPATH)->getText(); + } + return $skus; + } } diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertOrderedProductReportForConfigurableProduct.php b/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertOrderedProductReportForConfigurableProduct.php new file mode 100644 index 0000000000000000000000000000000000000000..1d7f7020893540319e3e84b8a72da663db1988a8 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertOrderedProductReportForConfigurableProduct.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Reports\Test\Constraint; + +use Magento\ConfigurableProduct\Test\Fixture\ConfigurableProduct; +use Magento\Reports\Test\Page\Adminhtml\OrderedProductsReport; +use Magento\Sales\Test\Fixture\OrderInjectable; +use Magento\Mtf\Constraint\AbstractConstraint; + +/** + * Assert product name, sku and qty in Ordered Products report + * + */ +class AssertOrderedProductReportForConfigurableProduct extends AbstractConstraint +{ + /** + * Assert product name, sku and qty in Ordered Products report + * + * @param OrderedProductsReport $orderedProducts + * @param OrderInjectable $order + * @return void + */ + public function processAssert(OrderedProductsReport $orderedProducts, OrderInjectable $order) + { + $products = $order->getEntityId()['products']; + $simpleChildSku = $orderedProducts->getGridBlock()->getOrdersResultsforConfigurableProducts($order); + $filters = []; + foreach ($products as $product) { + /** @var ConfigurableProduct $product */ + if ($product->hasData('configurable_attributes_data')) { + $matrix = isset($product->getConfigurableAttributesData()['matrix']) ? + $product->getConfigurableAttributesData()['matrix'] : []; + foreach ($matrix as $variation) { + $filters[] = $variation['sku']; + } + } + } + \PHPUnit_Framework_Assert::assertContains( + $simpleChildSku[0], + $filters, + 'Ordered simple product sku is not present in the Reports grid' + ); + } + + /** + * Returns a string representation of the object + * + * @return string + */ + public function toString() + { + return 'Child product sku is present on the Ordered Products report grid'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.php b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.php index 570cbb5ba8f16b908f7d6baeae45c74e95e12886..b43eb335d36c53836b4c10ae83ec225ff7bdfae3 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.php @@ -28,7 +28,6 @@ class OrderedProductsReportEntityTest extends Injectable { /* tags */ const MVP = 'no'; - const STABLE = 'no'; /* end tags */ /** diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.xml index a6b325e19d29c826b5f833e2f95a271705a5a5f6..15947020c1bbea22c347af5e7908e0e77a65992c 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/OrderedProductsReportEntityTest.xml @@ -28,5 +28,13 @@ <data name="customersReport/report_period" xsi:type="string">Day</data> <constraint name="Magento\Reports\Test\Constraint\AssertOrderedProductResult" /> </variation> + <variation name="OrderedProductsReportEntityTestVariation4" summary="Ordered Product Report for Configurable Product" ticketId="MAGETWO-75456"> + <data name="order/dataset" xsi:type="string">configurable_product</data> + <data name="customersReport/report_from" xsi:type="string">m/d/Y -1 day</data> + <data name="customersReport/report_to" xsi:type="string">m/d/Y</data> + <data name="customersReport/report_period" xsi:type="string">Day</data> + <constraint name="Magento\Reports\Test\Constraint\AssertOrderedProductResult" /> + <constraint name="Magento\Reports\Test\Constraint\AssertOrderedProductReportForConfigurableProduct" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml index aff22086394d20c2c8ae305eacca73a792a9b531..e6c61c189d195937b17193c63b7ef7262842a040 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/Repository/OrderInjectable.xml @@ -326,5 +326,29 @@ <field name="base_currency_code" xsi:type="string">0</field> <field name="order_currency_code" xsi:type="string">USD</field> </dataset> + + <dataset name="configurable_product"> + <field name="entity_id" xsi:type="array"> + <item name="products" xsi:type="string">configurableProduct::with_only_two_options</item> + </field> + <field name="customer_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default</item> + </field> + <field name="billing_address_id" xsi:type="array"> + <item name="dataset" xsi:type="string">US_address</item> + </field> + <field name="store_id" xsi:type="array"> + <item name="dataset" xsi:type="string">default_store_view</item> + </field> + <field name="payment_auth_expiration" xsi:type="array"> + <item name="method" xsi:type="string">checkmo</item> + </field> + <field name="shipping_method" xsi:type="string">flatrate_flatrate</field> + <field name="payment_authorization_amount" xsi:type="array"> + <item name="method" xsi:type="string">free</item> + </field> + <field name="base_currency_code" xsi:type="string">0</field> + <field name="order_currency_code" xsi:type="string">USD</field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Store/Test/TestStep/DeleteWebsitesEntityStep.php b/dev/tests/functional/tests/app/Magento/Store/Test/TestStep/DeleteWebsitesEntityStep.php new file mode 100644 index 0000000000000000000000000000000000000000..c18f5629d2e02a65df0103726dcd807c718d66d7 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Store/Test/TestStep/DeleteWebsitesEntityStep.php @@ -0,0 +1,122 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Store\Test\TestStep; + +use Magento\Backend\Test\Page\Adminhtml\EditWebsite; +use Magento\Backend\Test\Page\Adminhtml\DeleteWebsite; +use Magento\Backend\Test\Page\Adminhtml\StoreIndex; +use Magento\Backup\Test\Page\Adminhtml\BackupIndex; +use Magento\Store\Test\Fixture\Store; +use Magento\Mtf\TestStep\TestStepInterface; +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\Fixture\FixtureInterface; + +/** + * Test Step for DeleteStoreEntity + */ +class DeleteWebsitesEntityStep implements TestStepInterface +{ + /* tags */ + const MVP = 'yes'; + const SEVERITY = 'S2'; + /* end tags */ + + /** + * Page BackupIndex + * + * @var BackupIndex + */ + private $backupIndex; + + /** + * Page StoreIndex + * + * @var StoreIndex + */ + private $storeIndex; + + /** + * Page EditWebsite + * + * @var EditWebsite + */ + private $editWebsite; + + /** + * Page StoreDelete + * + * @var DeleteWebsite + */ + private $deleteWebsite; + + /** + * Fixture factory. + * + * @var FixtureFactory + */ + private $fixtureFactory; + + /** + * Fixture factory. + * + * @var FixtureInterface + */ + private $item; + + /** + * @var string + */ + private $createBackup; + + /** + * Prepare pages for test + * + * @param BackupIndex $backupIndex + * @param StoreIndex $storeIndex + * @param EditWebsite $editWebsite + * @param DeleteWebsite $deleteWebsite + * @param FixtureFactory $fixtureFactory + * @param FixtureInterface $item + * @param string $createBackup + */ + public function __construct( + BackupIndex $backupIndex, + StoreIndex $storeIndex, + EditWebsite $editWebsite, + DeleteWebsite $deleteWebsite, + FixtureFactory $fixtureFactory, + FixtureInterface $item, + $createBackup = 'No' + ) { + $this->storeIndex = $storeIndex; + $this->editWebsite = $editWebsite; + $this->backupIndex = $backupIndex; + $this->deleteWebsite = $deleteWebsite; + $this->item = $item; + $this->createBackup = $createBackup; + $this->fixtureFactory = $fixtureFactory; + } + + /** + * Delete specific Store View. + * + * @return void + */ + public function run() + { + $this->backupIndex->open()->getBackupGrid()->massaction([], 'Delete', true, 'Select All'); + $this->storeIndex->open(); + $websiteNames = $this->item->getWebsiteIds(); + if (is_array($websiteNames) && count($websiteNames) > 0) { + $websiteName = end($websiteNames); + $this->storeIndex->getStoreGrid()->searchAndOpenWebsiteByName($websiteName); + $this->editWebsite->getFormPageActions()->delete(); + $this->deleteWebsite->getDeleteWebsiteForm()->fillForm(['create_backup' => $this->createBackup]); + $this->deleteWebsite->getFormPageActions()->delete(); + } + } +} diff --git a/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridFilteringDeletedEntityTest.php b/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridFilteringDeletedEntityTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6c5d91fda0d9cce1a30ee6f435086454881691cc --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Ui/Test/TestCase/GridFilteringDeletedEntityTest.php @@ -0,0 +1,136 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Ui\Test\TestCase; + +use Magento\Mtf\Fixture\FixtureFactory; +use Magento\Mtf\Fixture\FixtureInterface; +use Magento\Mtf\Page\PageFactory; +use Magento\Mtf\TestCase\Injectable; +use Magento\Ui\Test\Block\Adminhtml\DataGrid; + +/** + * Precondition: + * 1. Create items + * + * Steps: + * 1. Navigate to backend. + * 2. Go to grid page + * 3. Filter grid using provided columns + * 5. Perform Asserts + * + * @group Ui + * @ZephyrId MAGETWO-71940 + */ +class GridFilteringDeletedEntityTest extends Injectable +{ + /* tags */ + const SEVERITY = 'S2'; + const STABLE = 'no'; + const MVP = 'no'; + /* end tags */ + + /** + * Order index page. + * + * @var PageFactory + */ + protected $pageFactory; + + /** + * Fixture factory. + * + * @var FixtureFactory + */ + protected $fixtureFactory; + + /** + * Injection data. + * + * @param PageFactory $pageFactory + * @param FixtureFactory $fixtureFactory + * @return void + */ + public function __inject(PageFactory $pageFactory, FixtureFactory $fixtureFactory) + { + $this->pageFactory = $pageFactory; + $this->fixtureFactory = $fixtureFactory; + } + + /** + * @param string $pageClass + * @param string $gridRetriever + * @param string[] $filters + * @param string $fixtureName + * @param string[] $steps + * @param array $fixtureDataSet + * @return void + */ + public function test( + $pageClass, + $gridRetriever, + array $filters, + $fixtureName, + array $steps = [], + $fixtureDataSet = null + ) { + $item = $this->createItems($fixtureName, $fixtureDataSet); + $page = $this->pageFactory->create($pageClass); + + $page->open(); + /** @var DataGrid $gridBlock */ + $gridBlock = $page->$gridRetriever(); + $gridBlock->resetFilter(); + + foreach ($filters as $itemFilters) { + $filterArray = []; + foreach ($itemFilters as $itemFiltersName => $itemFilterValue) { + if (substr($itemFilterValue, 0, 1) === ':') { + $value = $item->getData(substr($itemFilterValue, 1)); + } else { + $value = $itemFilterValue; + } + $filterArray[$itemFiltersName] = $value; + } + + $storesArray = $item->getDataFieldConfig('website_ids')['source']->getStores(); + $store = end($storesArray); + $filterArray['store_id'] = $store->getName(); + $gridBlock->search($filterArray); + } + + if (!empty($steps)) { + foreach ($steps as $step) { + $this->processSteps($item, $step); + } + } + } + + /** + * @param string $fixtureName + * @param string $fixtureDataSet + * @return FixtureInterface + */ + private function createItems($fixtureName, $fixtureDataSet) + { + $item = $this->fixtureFactory->createByCode($fixtureName, ['dataset' => $fixtureDataSet]); + $item->persist(); + return $item; + } + + /** + * @param FixtureInterface $item + * @param array $steps + * @return void + */ + private function processSteps(FixtureInterface $item, $steps) + { + foreach ($steps as $step) { + $processStep = $this->objectManager->create($step, ['item' => $item]); + $processStep->run(); + } + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Product/ReviewRendererTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Product/ReviewRendererTest.php new file mode 100644 index 0000000000000000000000000000000000000000..749d6c74917b733acfae32926bf80e9192e3d969 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Block/Product/ReviewRendererTest.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Review\Block\Product; + +class ReviewRendererTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test verifies ReviewRenderer::getReviewsSummaryHtml call with $displayIfNoReviews = false + * The reviews summary will be shown as expected only if there is at least one review available + * + * @magentoDataFixture Magento/Review/_files/different_reviews.php + * @magentoAppArea frontend + */ + public function testGetReviewSummaryHtml() + { + $productSku = 'simple'; + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get($productSku); + /** @var ReviewRenderer $reviewRenderer */ + $reviewRenderer = $objectManager->create(ReviewRenderer::class); + $actualResult = $reviewRenderer->getReviewsSummaryHtml($product); + $this->assertEquals(2, $reviewRenderer->getReviewsCount()); + $this->assertContains('<span itemprop="reviewCount">2</span>', $actualResult); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php b/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php index 627434111fe79969d6730d10cb1acf2602f569dc..b7de6c99130e49c34a3df4156a15f4503d485105 100644 --- a/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php +++ b/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php @@ -71,3 +71,4 @@ $review->setEntityId( )->getStore()->getId() ] )->save(); +$review->aggregate(); diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php index ecdc51044430aeb49dfed36da3fd85c9baf7af03..b1cdb53288216f73935336e87b45a6569c866055 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Repository.php @@ -226,13 +226,15 @@ class Repository extends \Magento\Framework\Code\Generator\EntityAbstract /** @var ParameterReflection $parameterReflection */ $parameterReflection = $methodReflection->getParameters()[0]; $body = "if (!\$id) {\n" - . " throw new \\" . InputException::class . "('ID required');\n" + . " throw new \\" . InputException::class . "(new \\Magento\\Framework\\Phrase('ID required'));\n" . "}\n" . "if (!isset(\$this->registry[\$id])) {\n" . " \$entity = \$this->" . $this->_getSourcePersistorPropertyName() . "->loadEntity(\$id);\n" . " if (!\$entity->getId()) {\n" - . " throw new \\" . NoSuchEntityException::class . "('Requested entity doesn\\'t exist');\n" + . " throw new \\" . NoSuchEntityException::class . "(\n" + . " new \\Magento\\Framework\\Phrase('Requested entity doesn\\'t exist')\n" + . " );\n" . " }\n" . " \$this->registry[\$id] = \$entity;\n" . "}\n" diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleRepository.txt b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleRepository.txt index 1aeaec427d0d38c756599de1b2eb96612fdb9e2c..222d5c7ffc6d167b61e421485ea4a0761b320316 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleRepository.txt +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/SampleRepository.txt @@ -71,12 +71,14 @@ class SampleRepository implements SampleRepositoryInterface public function get($id) { if (!$id) { - throw new \Magento\Framework\Exception\InputException('ID required'); + throw new \Magento\Framework\Exception\InputException(new \Magento\Framework\Phrase('ID required')); } if (!isset($this->registry[$id])) { $entity = $this->sampleInterfacePersistor->loadEntity($id); if (!$entity->getId()) { - throw new \Magento\Framework\Exception\NoSuchEntityException('Requested entity doesn\'t exist'); + throw new \Magento\Framework\Exception\NoSuchEntityException( + new \Magento\Framework\Phrase('Requested entity doesn\'t exist') + ); } $this->registry[$id] = $entity; } diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/TSampleRepository.txt b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/TSampleRepository.txt index 53ec1801995ad7ec9f3944827ad44ec39ebbc344..e4067e70670a49e5691f3b672810bcc117961ff6 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/TSampleRepository.txt +++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Code/Generator/_files/TSampleRepository.txt @@ -71,12 +71,14 @@ class TSampleRepository implements TSampleRepositoryInterface public function get(int $id) : \Magento\Framework\ObjectManager\Code\Generator\TSampleInterface { if (!$id) { - throw new \Magento\Framework\Exception\InputException('ID required'); + throw new \Magento\Framework\Exception\InputException(new \Magento\Framework\Phrase('ID required')); } if (!isset($this->registry[$id])) { $entity = $this->tSampleInterfacePersistor->loadEntity($id); if (!$entity->getId()) { - throw new \Magento\Framework\Exception\NoSuchEntityException('Requested entity doesn\'t exist'); + throw new \Magento\Framework\Exception\NoSuchEntityException( + new \Magento\Framework\Phrase('Requested entity doesn\'t exist') + ); } $this->registry[$id] = $entity; } diff --git a/lib/internal/Magento/Framework/View/Layout/Generator/Block.php b/lib/internal/Magento/Framework/View/Layout/Generator/Block.php index d38c6b37fdd40a8b57c731f53f66a7dc56ec0926..e995b2c20a6ab5f4fd483a9b200c1a0b151dee4f 100755 --- a/lib/internal/Magento/Framework/View/Layout/Generator/Block.php +++ b/lib/internal/Magento/Framework/View/Layout/Generator/Block.php @@ -7,6 +7,7 @@ namespace Magento\Framework\View\Layout\Generator; use Magento\Framework\App\State; use Magento\Framework\ObjectManager\Config\Reader\Dom; +use Magento\Framework\View\Element\Template; use Magento\Framework\View\Layout; /** @@ -60,6 +61,13 @@ class Block implements Layout\GeneratorInterface */ protected $exceptionHandlerBlockFactory; + /** + * Default block class name. Will be used if no class name is specified in block configuration + * + * @var string + */ + private $defaultClass; + /** * @param \Magento\Framework\View\Element\BlockFactory $blockFactory * @param \Magento\Framework\Data\Argument\InterpreterInterface $argumentInterpreter @@ -69,6 +77,7 @@ class Block implements Layout\GeneratorInterface * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver * @param \Magento\Framework\View\Element\ExceptionHandlerBlockFactory $exceptionHandlerBlockFactory * @param State $appState + * @param string $defaultClass */ public function __construct( \Magento\Framework\View\Element\BlockFactory $blockFactory, @@ -78,7 +87,8 @@ class Block implements Layout\GeneratorInterface \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\App\ScopeResolverInterface $scopeResolver, \Magento\Framework\View\Element\ExceptionHandlerBlockFactory $exceptionHandlerBlockFactory, - State $appState + State $appState, + $defaultClass = Template::class ) { $this->blockFactory = $blockFactory; $this->argumentInterpreter = $argumentInterpreter; @@ -88,6 +98,7 @@ class Block implements Layout\GeneratorInterface $this->scopeResolver = $scopeResolver; $this->exceptionHandlerBlockFactory = $exceptionHandlerBlockFactory; $this->appState = $appState; + $this->defaultClass = $defaultClass; } /** @@ -210,7 +221,8 @@ class Block implements Layout\GeneratorInterface } // create block - $className = $attributes['class']; + $className = isset($attributes['class']) && !empty($attributes['class']) ? + $attributes['class'] : $this->defaultClass ; $block = $this->createBlock($className, $elementName, [ 'data' => $this->evaluateArguments($data['arguments']) ]); diff --git a/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php b/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php index a3d2ca3d8c408cdc5155fda77191b29d4dab89f5..e2d032da9b1966e37daf0b52b3151a351cf81804 100644 --- a/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php +++ b/setup/src/Magento/Setup/Console/Command/UpgradeCommand.php @@ -6,6 +6,7 @@ namespace Magento\Setup\Console\Command; use Magento\Deploy\Console\Command\App\ConfigImportCommand; +use Magento\Framework\App\State as AppState; use Magento\Framework\App\DeploymentConfig; use Magento\Framework\App\ObjectManager; use Magento\Framework\Setup\ConsoleLogger; @@ -37,16 +38,25 @@ class UpgradeCommand extends AbstractSetupCommand */ private $deploymentConfig; + /** + * @var AppState + */ + private $appState; + /** * Constructor * * @param InstallerFactory $installerFactory * @param DeploymentConfig $deploymentConfig */ - public function __construct(InstallerFactory $installerFactory, DeploymentConfig $deploymentConfig = null) - { + public function __construct( + InstallerFactory $installerFactory, + DeploymentConfig $deploymentConfig = null, + AppState $appState = null + ) { $this->installerFactory = $installerFactory; $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get(DeploymentConfig::class); + $this->appState = $appState ?: ObjectManager::getInstance()->get(AppState::class); parent::__construct(); } @@ -90,7 +100,7 @@ class UpgradeCommand extends AbstractSetupCommand $importConfigCommand->run($arrayInput, $output); } - if (!$keepGenerated) { + if (!$keepGenerated && $this->appState->getMode() === AppState::MODE_PRODUCTION) { $output->writeln( '<info>Please re-run Magento compile command. Use the command "setup:di:compile"</info>' ); diff --git a/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php b/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php index 59816ab77153b18c15bce682be94661035c7da9a..fb81c6677274ec0e026cfc69fd48f52dca3eaa87 100644 --- a/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Console/Command/UpgradeCommandTest.php @@ -6,6 +6,7 @@ namespace Magento\Setup\Test\Unit\Console\Command; use Magento\Framework\App\DeploymentConfig; +use Magento\Framework\App\State as AppState; use Magento\Framework\Console\Cli; use Magento\Setup\Console\Command\UpgradeCommand; use Magento\Setup\Model\Installer; @@ -29,11 +30,15 @@ class UpgradeCommandTest extends \PHPUnit\Framework\TestCase */ private $installerMock; + /** + * @var AppState|\PHPUnit_Framework_MockObject_MockObject + */ + private $appStateMock; + /** * @var UpgradeCommand */ private $upgradeCommand; - /** * @var CommandTester */ @@ -56,16 +61,24 @@ class UpgradeCommandTest extends \PHPUnit\Framework\TestCase $this->installerFactoryMock->expects($this->once()) ->method('create') ->willReturn($this->installerMock); + $this->appStateMock = $this->getMockBuilder(AppState::class) + ->disableOriginalConstructor() + ->getMock(); - $this->upgradeCommand = new UpgradeCommand($this->installerFactoryMock, $this->deploymentConfigMock); + $this->upgradeCommand = new UpgradeCommand( + $this->installerFactoryMock, + $this->deploymentConfigMock, + $this->appStateMock + ); $this->commandTester = new CommandTester($this->upgradeCommand); } /** * @dataProvider executeDataProvider */ - public function testExecute($options, $expectedString = '') + public function testExecute($options, $deployMode, $expectedString = '') { + $this->appStateMock->method('getMode')->willReturn($deployMode); $this->installerMock->expects($this->at(0)) ->method('updateModulesSequence'); $this->installerMock->expects($this->at(1)) @@ -85,11 +98,23 @@ class UpgradeCommandTest extends \PHPUnit\Framework\TestCase return [ [ 'options' => [], + 'deployMode' => \Magento\Framework\App\State::MODE_PRODUCTION, 'expectedString' => 'Please re-run Magento compile command. Use the command "setup:di:compile"' . PHP_EOL ], [ 'options' => ['--keep-generated' => true], + 'deployMode' => \Magento\Framework\App\State::MODE_PRODUCTION, + 'expectedString' => '' + ], + [ + 'options' => [], + 'deployMode' => \Magento\Framework\App\State::MODE_DEVELOPER, + 'expectedString' => '' + ], + [ + 'options' => [], + 'deployMode' => \Magento\Framework\App\State::MODE_DEFAULT, 'expectedString' => '' ], ];