diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js index 7caeb85190a3774f75984592d53800fa6bb03375..bb7697b22da123783745e7e3a43e0bcd5ecbf2f0 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js @@ -24,7 +24,8 @@ define([ * {String} */ id: 'co-transparent-form-braintree' - } + }, + isValidCardNumber: false }, /** @@ -113,11 +114,7 @@ define([ } if (event.target.fieldKey === 'number' && event.card) { - if (event.isValid) { - self.cardNumber = event.card; - } else { - self.cardNumber = null; - } + self.isValidCardNumber = event.isValid; self.selectedCardType( validator.getMageCardType(event.card.type, self.getCcAvailableTypes()) ); @@ -137,7 +134,7 @@ define([ $selector.removeClass(invalidClass); - if (this.selectedCardType() === null) { + if (this.selectedCardType() === null || !this.isValidCardNumber) { $(this.getSelector('cc_number')).addClass(invalidClass); return false; diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php index 206b2d8b59d9ddcf3bf75fca165b58e173865ea4..8afc9e369d7729189e9f1b0cbb62cade66c8ffae 100644 --- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php @@ -919,7 +919,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity $additionalAttributes[$fieldName] = $fieldName . ImportProduct::PAIR_NAME_VALUE_SEPARATOR . $attrValue; } - $data[$itemId][$storeId][$fieldName] = $attrValue; + $data[$itemId][$storeId][$fieldName] = htmlspecialchars_decode($attrValue); } } else { $this->collectMultiselectValues($item, $code, $storeId); @@ -934,6 +934,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity } if (!empty($additionalAttributes)) { + $additionalAttributes = array_map('htmlspecialchars_decode', $additionalAttributes); $data[$itemId][$storeId][self::COL_ADDITIONAL_ATTRIBUTES] = implode(Import::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, $additionalAttributes); } else { diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 03c2f3f4f511af289f63cf689f294397d090ef93..1eb3a680823038ecb7bd1e31d704a772cd8db04d 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -259,7 +259,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity ValidatorInterface::ERROR_MEDIA_PATH_NOT_ACCESSIBLE => 'Imported resource (image) does not exist in the local media storage', ValidatorInterface::ERROR_MEDIA_URL_NOT_ACCESSIBLE => 'Imported resource (image) could not be downloaded from external resource due to timeout or access permissions', ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid', - ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Specified url key already exists', + ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Specified URL key already exists', ]; /** diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js b/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js index 13673326605a467c0709d5e3ff98e12e4f6f0337..793978cb8e4742168e013fb3714d01e01262f399 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js +++ b/app/code/Magento/CheckoutAgreements/view/frontend/web/js/model/agreement-validator.js @@ -25,9 +25,18 @@ define( return true; } - var form = $('.payment-method._active form[data-role=checkout-agreements]'); - form.validation(); - return form.validation('isValid'); + return $('#co-payment-form').validate({ + errorClass: 'mage-error', + errorElement: 'div', + meta: 'validate', + errorPlacement: function (error, element) { + var errorPlacement = element; + if (element.is(':checkbox') || element.is(':radio')) { + errorPlacement = element.siblings('label').last(); + } + errorPlacement.after(error); + } + }).element('.payment-method._active div.checkout-agreements input'); } } } diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index 2a847b0019c4d905ca55bd472b7c57f725c3c5d6..7af0ab6952642a3513f9961a6c4bb56820a987d1 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -35,7 +35,7 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac /** * @var \Magento\Eav\Model\Entity\AttributeLoaderInterface */ - private $attributeLoader; + protected $attributeLoader; /** * Connection name @@ -1859,7 +1859,7 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac * * @deprecated */ - private function getAttributeLoader() + protected function getAttributeLoader() { if ($this->attributeLoader === null) { $this->attributeLoader= ObjectManager::getInstance()->get(AttributeLoaderInterface::class); diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php index e6149227a20785c1bc6ed2adc50b8cda51f98547..27f64633932f00536c6d15c45df8edf81de40ec0 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/Import/Validate.php @@ -55,7 +55,7 @@ class Validate extends ImportResultController $errorAggregator = $import->getErrorAggregator(); if (!$validationResult) { $resultBlock->addError( - __('Data validation is failed. Please fix errors and re-upload the file..') + __('Data validation failed. Please fix the following errors and upload the file again.') ); $this->addErrorMessages($resultBlock, $errorAggregator); } else { diff --git a/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php b/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php index a520c553d9ce36cd665581994af149ca53b52c25..c95415d49df326ac69d4afe671d03dc804a9dac9 100644 --- a/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php +++ b/app/code/Magento/ImportExport/Controller/Adminhtml/ImportResult.php @@ -84,7 +84,7 @@ abstract class ImportResult extends Import try { $resultBlock->addNotice( '<strong>' . __('Following Error(s) has been occurred during importing process:') . '</strong><br>' - . '<div class="import-error-wrapper">' . __('Only first 100 errors are displayed here. ') + . '<div class="import-error-wrapper">' . __('Only the first 100 errors are shown. ') . '<a href="' . $this->createDownloadUrlImportHistoryFile($this->createErrorReport($errorAggregator)) . '">' . __('Download full report') . '</a><br>' @@ -109,7 +109,7 @@ abstract class ImportResult extends Import $messages = []; $rowMessages = $errorAggregator->getRowsGroupedByErrorCode([], [AbstractEntity::ERROR_CODE_SYSTEM_EXCEPTION]); foreach ($rowMessages as $errorCode => $rows) { - $messages[] = $errorCode . ' ' . __('in rows:') . ' ' . implode(', ', $rows); + $messages[] = $errorCode . ' ' . __('in row(s):') . ' ' . implode(', ', $rows); } return $messages; } diff --git a/app/code/Magento/ImportExport/Model/Import.php b/app/code/Magento/ImportExport/Model/Import.php index 207cb96eaa99d71773c377b817a909cbe54e5aa0..1c38ab573e63980f907ea7b7408562089aea13c1 100644 --- a/app/code/Magento/ImportExport/Model/Import.php +++ b/app/code/Magento/ImportExport/Model/Import.php @@ -280,11 +280,11 @@ class Import extends \Magento\ImportExport\Model\AbstractModel $messages = []; if ($this->getProcessedRowsCount()) { if ($validationResult->getErrorsCount()) { - $messages[] = __('Data validation is failed. Please fix errors and re-upload the file.'); + $messages[] = __('Data validation failed. Please fix the following errors and upload the file again.'); // errors info foreach ($validationResult->getRowsGroupedByErrorCode() as $errorMessage => $rows) { - $error = $errorMessage . ' ' . __('in rows') . ': ' . implode(', ', $rows); + $error = $errorMessage . ' ' . __('in row(s)') . ': ' . implode(', ', $rows); $messages[] = $error; } } else { diff --git a/app/code/Magento/ImportExport/i18n/en_US.csv b/app/code/Magento/ImportExport/i18n/en_US.csv index 34a1de9e50439d38f4af0cf97ec2613e3bf300ce..53bc7470812bb9ddb4cc15f1e7a9a40f69f3f2b4 100644 --- a/app/code/Magento/ImportExport/i18n/en_US.csv +++ b/app/code/Magento/ImportExport/i18n/en_US.csv @@ -37,7 +37,7 @@ Status,Status "Maximum error count has been reached or system error is occurred!","Maximum error count has been reached or system error is occurred!" "Import successfully done","Import successfully done" "This file is empty. Please try another one.","This file is empty. Please try another one." -"Data validation is failed. Please fix errors and re-upload the file..","Data validation is failed. Please fix errors and re-upload the file.." +"Data validation failed. Please fix the following errors and upload the file again.","Data validation failed. Please fix the following errors and upload the file again." "File is valid! To start import process press ""Import"" button","File is valid! To start import process press ""Import"" button" "The file is valid, but we can\'t import it for some reason.","The file is valid, but we can\'t import it for some reason." "Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4","Checked rows: %1, checked entities: %2, invalid rows: %3, total errors: %4" @@ -45,10 +45,10 @@ Status,Status "Sorry, but the data is invalid or the file is not uploaded.","Sorry, but the data is invalid or the file is not uploaded." "Show more","Show more" "Additional data","Additional data" -"Following Error(s) has been occurred during importing process:","Following Error(s) has been occurred during importing process:" -"Only first 100 errors are displayed here. ","Only first 100 errors are displayed here. " +"The following error(s) occurred when importing the data:","The following error(s) occurred when importing the data:" +"Only the first 100 errors are shown. ","Only the first 100 errors are shown. " "Download full report","Download full report" -"in rows:","in rows:" +"in row(s):","in row(s):" "Make sure your file isn\'t more than %1M.","Make sure your file isn\'t more than %1M." "We can\'t provide the upload settings right now.","We can\'t provide the upload settings right now." "Created: %1, Updated: %2, Deleted: %3","Created: %1, Updated: %2, Deleted: %3" @@ -72,8 +72,8 @@ Status,Status "The destination directory is not writable.","The destination directory is not writable." "Destination file is not writable","Destination file is not writable" "The header column names are already set.","The header column names are already set." -"Data validation is failed. Please fix errors and re-upload the file.","Data validation is failed. Please fix errors and re-upload the file." -"in rows","in rows" +"Data validation failed. Please fix the following errors and upload the file again.","Data validation failed. Please fix the following errors and upload the file again." +"in row(s)","in row(s)" "The validation is complete.","The validation is complete." "This file does not contain any data.","This file does not contain any data." "Begin import of ""%1"" with ""%2"" behavior","Begin import of ""%1"" with ""%2"" behavior" diff --git a/app/code/Magento/Quote/Model/Quote/ShippingAssignment/ShippingAssignmentProcessor.php b/app/code/Magento/Quote/Model/Quote/ShippingAssignment/ShippingAssignmentProcessor.php index 209e8785a4c53bb671dcca6857fad9ab7d2635ed..7643160e4489be2ad3d76720441ebc304471f8c9 100644 --- a/app/code/Magento/Quote/Model/Quote/ShippingAssignment/ShippingAssignmentProcessor.php +++ b/app/code/Magento/Quote/Model/Quote/ShippingAssignment/ShippingAssignmentProcessor.php @@ -68,7 +68,8 @@ class ShippingAssignmentProcessor { /** @var \Magento\Quote\Model\Quote $quote */ foreach ($shippingAssignment->getItems() as $item) { - if (!$quote->getItemById($item->getItemId())) { + /** @var \Magento\Quote\Model\Quote\Item $item */ + if (!$quote->getItemById($item->getItemId()) && !$item->isDeleted()) { $this->cartItemPersister->save($quote, $item); } } diff --git a/app/code/Magento/Quote/Model/QuoteRepository.php b/app/code/Magento/Quote/Model/QuoteRepository.php index b395bcea40e1f9cacd96326b1be68ab87e1ee6cd..5396412cdd425a33ab962e35d396d962c41ff577 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository.php +++ b/app/code/Magento/Quote/Model/QuoteRepository.php @@ -101,7 +101,6 @@ class QuoteRepository implements \Magento\Quote\Api\CartRepositoryInterface $quote = $this->loadQuote('load', 'cartId', $cartId, $sharedStoreIds); $this->getLoadHandler()->load($quote); $this->quotesById[$cartId] = $quote; - $this->quotesByCustomerId[$quote->getCustomerId()] = $quote; } return $this->quotesById[$cartId]; } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/ShippingAssignment/ShippingAssignmentProcessorTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/ShippingAssignment/ShippingAssignmentProcessorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..52c0a48bfd2081e08da28acfa01c6c2d7df8ebd7 --- /dev/null +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/ShippingAssignment/ShippingAssignmentProcessorTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Quote\Test\Unit\Model\Quote\ShippingAssignment; + +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Quote\Api\Data\ShippingInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\ShippingAssignmentFactory; +use Magento\Quote\Model\Quote\Item\CartItemPersister; +use Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor; +use Magento\Quote\Model\Quote\ShippingAssignment\ShippingProcessor; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Class ShippingAssignmentProcessorTest + */ +class ShippingAssignmentProcessorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ShippingAssignmentFactory|MockObject + */ + private $shippingAssignmentFactory; + + /** + * @var ShippingProcessor|MockObject + */ + private $shippingProcessor; + + /** + * @var CartItemPersister|MockObject + */ + private $cartItemPersister; + + /** + * @var ShippingAssignmentProcessor + */ + private $shippingAssignmentProcessor; + + protected function setUp() + { + $this->shippingAssignmentFactory = $this->getMockBuilder(ShippingAssignmentFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->shippingProcessor = $this->getMockBuilder(ShippingProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cartItemPersister = $this->getMockBuilder(CartItemPersister::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->shippingAssignmentProcessor = new ShippingAssignmentProcessor( + $this->shippingAssignmentFactory, + $this->shippingProcessor, + $this->cartItemPersister + ); + } + + /** + * Test saving shipping assignments with deleted cart items + * + * @covers \Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor::save + */ + public function testSaveWithDeletedCartItems() + { + $shippingAssignment = $this->getMockForAbstractClass(ShippingAssignmentInterface::class); + $shipping = $this->getMockForAbstractClass(ShippingInterface::class); + $quoteId = 1; + + $quote = $this->getMockBuilder(Quote::class) + ->disableOriginalConstructor() + ->getMock(); + $quoteItem = $this->getMockBuilder(\Magento\Quote\Model\Quote\Item::class) + ->disableOriginalConstructor() + ->getMock(); + $quoteItem->expects(static::once()) + ->method('isDeleted') + ->willReturn(true); + $quoteItem->expects(static::once()) + ->method('getItemId') + ->willReturn($quoteId); + + $quote->expects(static::once()) + ->method('getItemById') + ->with($quoteId) + ->willReturn(null); + + $shippingAssignment->expects(static::once()) + ->method('getItems') + ->willReturn([$quoteItem]); + $shippingAssignment->expects(static::once()) + ->method('getShipping') + ->willReturn($shipping); + + $this->cartItemPersister->expects(static::never()) + ->method('save'); + + $this->shippingProcessor->expects(static::once()) + ->method('save') + ->with($shipping, $quote); + + $this->shippingAssignmentProcessor->save( + $quote, + $shippingAssignment + ); + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php index db0e186a50329fd08e6939d57636f7ff9490cec9..70c6489326ad53e93ceda37216ca083bc0baf190 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteRepositoryTest.php @@ -7,11 +7,13 @@ namespace Magento\Quote\Test\Unit\Model; -use Magento\Quote\Api\CartRepositoryInterface; - use Magento\Framework\Api\SortOrder; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; +use Magento\Quote\Model\QuoteRepository\LoadHandler; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase { /** @@ -54,6 +56,11 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase */ private $extensionAttributesJoinProcessorMock; + /** + * @var LoadHandler|\PHPUnit_Framework_MockObject_MockObject + */ + private $loadHandlerMock; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -107,6 +114,12 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase 'extensionAttributesJoinProcessor' => $this->extensionAttributesJoinProcessorMock ] ); + + $this->loadHandlerMock = $this->getMock(LoadHandler::class, [], [], '', false); + $reflection = new \ReflectionClass(get_class($this->model)); + $reflectionProperty = $reflection->getProperty('loadHandler'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->model, $this->loadHandlerMock); } /** @@ -132,21 +145,72 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase public function testGet() { - $this->markTestSkipped('MAGETWO-48531'); $cartId = 15; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); - $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); - $this->storeMock->expects($this->once())->method('getId')->willReturn(1); - $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); - $this->quoteMock->expects($this->once()) + $this->quoteFactoryMock->expects(static::once()) + ->method('create') + ->willReturn($this->quoteMock); + $this->storeManagerMock->expects(static::once()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->storeMock->expects(static::once()) + ->method('getId') + ->willReturn(1); + $this->quoteMock->expects(static::never()) + ->method('setSharedStoreIds'); + $this->quoteMock->expects(static::once()) ->method('load') ->with($cartId) ->willReturn($this->storeMock); - $this->quoteMock->expects($this->once())->method('getId')->willReturn($cartId); + $this->quoteMock->expects(static::once()) + ->method('getId') + ->willReturn($cartId); + $this->quoteMock->expects(static::never()) + ->method('getCustomerId'); + $this->loadHandlerMock->expects(static::once()) + ->method('load') + ->with($this->quoteMock); + + static::assertEquals($this->quoteMock, $this->model->get($cartId)); + static::assertEquals($this->quoteMock, $this->model->get($cartId)); + } + + public function testGetForCustomerAfterGet() + { + $cartId = 15; + $customerId = 23; + + $this->quoteFactoryMock->expects(static::exactly(2)) + ->method('create') + ->willReturn($this->quoteMock); + $this->storeManagerMock->expects(static::exactly(2)) + ->method('getStore') + ->willReturn($this->storeMock); + $this->storeMock->expects(static::exactly(2)) + ->method('getId') + ->willReturn(1); + $this->quoteMock->expects(static::never()) + ->method('setSharedStoreIds'); + $this->quoteMock->expects(static::once()) + ->method('load') + ->with($cartId) + ->willReturn($this->storeMock); + $this->quoteMock->expects(static::once()) + ->method('loadByCustomer') + ->with($customerId) + ->willReturn($this->storeMock); + $this->quoteMock->expects(static::exactly(3)) + ->method('getId') + ->willReturn($cartId); + $this->quoteMock->expects(static::any()) + ->method('getCustomerId') + ->willReturn($customerId); + $this->loadHandlerMock->expects(static::exactly(2)) + ->method('load') + ->with($this->quoteMock); - $this->assertEquals($this->quoteMock, $this->model->get($cartId)); - $this->assertEquals($this->quoteMock, $this->model->get($cartId)); + static::assertEquals($this->quoteMock, $this->model->get($cartId)); + static::assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); } public function testGetWithSharedStoreIds() @@ -174,22 +238,34 @@ class QuoteRepositoryTest extends \PHPUnit_Framework_TestCase public function testGetForCustomer() { - $this->markTestSkipped('MAGETWO-48531'); $cartId = 17; $customerId = 23; - $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($this->quoteMock); - $this->storeManagerMock->expects($this->once())->method('getStore')->willReturn($this->storeMock); - $this->storeMock->expects($this->once())->method('getId')->willReturn(1); - $this->quoteMock->expects($this->never())->method('setSharedStoreIds'); - $this->quoteMock->expects($this->once()) + $this->quoteFactoryMock->expects(static::once()) + ->method('create') + ->willReturn($this->quoteMock); + $this->storeManagerMock->expects(static::once()) + ->method('getStore') + ->willReturn($this->storeMock); + $this->storeMock->expects(static::once()) + ->method('getId') + ->willReturn(1); + $this->quoteMock->expects(static::never()) + ->method('setSharedStoreIds'); + $this->quoteMock->expects(static::once()) ->method('loadByCustomer') ->with($customerId) ->willReturn($this->storeMock); - $this->quoteMock->expects($this->exactly(2))->method('getId')->willReturn($cartId); + $this->quoteMock->expects(static::exactly(2)) + ->method('getId') + ->willReturn($cartId); + + $this->loadHandlerMock->expects(static::once()) + ->method('load') + ->with($this->quoteMock); - $this->assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); - $this->assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); + static::assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); + static::assertEquals($this->quoteMock, $this->model->getForCustomer($customerId)); } /** diff --git a/app/code/Magento/Sales/Block/Order/Totals.php b/app/code/Magento/Sales/Block/Order/Totals.php index 06a04048ff3f645dbbfc64904b1b45f451ad9616..6f267f51275a065c95f542cea72e8940a1e735f2 100644 --- a/app/code/Magento/Sales/Block/Order/Totals.php +++ b/app/code/Magento/Sales/Block/Order/Totals.php @@ -164,7 +164,7 @@ class Totals extends \Magento\Framework\View\Element\Template $this->_totals['base_grandtotal'] = new \Magento\Framework\DataObject( [ 'code' => 'base_grandtotal', - 'value' => $this->getOrder()->formatPrice($source->getGrandTotal()), + 'value' => $this->getOrder()->formatBasePrice($source->getBaseGrandTotal()), 'label' => __('Grand Total to be Charged'), 'is_formated' => true, ] diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php index 1fb983e3228ba8971c2447a0c54afe44faebd153..3c0fb3273981fdab50cf53ac26f21bdaff4a2db2 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create.php @@ -243,6 +243,7 @@ abstract class Create extends \Magento\Backend\App\Action $removeFrom = (string)$this->getRequest()->getPost('from'); if ($removeItemId && $removeFrom) { $this->_getOrderCreateModel()->removeItem($removeItemId, $removeFrom); + $this->_getOrderCreateModel()->recollectCart(); } /** diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 51bb09e7e9a7830169910cd464fe5f34c01c6c5d..d8f65c0a28c3702fcb555e69ca288e364d566aa7 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -931,6 +931,8 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ $this->quoteRepository->save($this->getCustomerCart()); } + $this->recollectCart(); + return $this; } @@ -951,8 +953,7 @@ class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\ $cart = $this->getCustomerCart(); if ($cart) { $cart->removeItem($itemId); - $cart->collectTotals(); - $this->quoteRepository->save($cart); + $this->_needCollectCart = true; } break; case 'wishlist': diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php index f3f5647f431d8ca13f734273f7c1fcecaf1fe70b..fd5e2ae5355148d7cac8d4148a27836c06e63d91 100644 --- a/app/code/Magento/Sales/Model/Order/Shipment.php +++ b/app/code/Magento/Sales/Model/Order/Shipment.php @@ -438,16 +438,11 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa public function getCommentsCollection($reload = false) { if (!$this->hasData(ShipmentInterface::COMMENTS) || $reload) { - $comments = $this->_commentCollectionFactory->create()->setShipmentFilter($this->getId()) + $comments = $this->_commentCollectionFactory->create() + ->setShipmentFilter($this->getId()) ->setCreatedAtOrder(); $this->setComments($comments); - /** - * When shipment created with adding comment, - * comments collection must be loaded before we added this comment. - */ - $this->getComments()->load(); - if ($this->getId()) { foreach ($this->getComments() as $comment) { $comment->setShipment($this); @@ -578,6 +573,7 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa } //@codeCoverageIgnoreStart + /** * Returns tracks * @@ -714,6 +710,19 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa */ public function getComments() { + if (!$this->getId()) { + return $this->getData(ShipmentInterface::COMMENTS); + } + + if ($this->getData(ShipmentInterface::COMMENTS) == null) { + $collection = $this->_commentCollectionFactory->create() + ->setShipmentFilter($this->getId()); + + foreach ($collection as $item) { + $item->setShipment($this); + } + $this->setData(ShipmentInterface::COMMENTS, $collection->getItems()); + } return $this->getData(ShipmentInterface::COMMENTS); } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php index 40c216436fa23bfd1ea053805bee2d5b69bc90ac..19d0c89d3b27fec0e82cafab1fc19db0601260d9 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/ShipmentTest.php @@ -5,18 +5,39 @@ */ namespace Magento\Sales\Test\Unit\Model\Order; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order\Shipment; +use Magento\Sales\Model\Order\Shipment\Item as ShipmentItem; +use Magento\Sales\Model\ResourceModel\Order\Shipment\Comment\Collection; +use Magento\Sales\Model\ResourceModel\Order\Shipment\Comment\CollectionFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + class ShipmentTest extends \PHPUnit_Framework_TestCase { + /** + * @var CollectionFactory|MockObject + */ + private $commentCollectionFactory; + + /** + * @var Collection|MockObject + */ + private $commentCollection; + /** * @var \Magento\Sales\Model\Order\shipment */ - protected $shipmentModel; + private $shipmentModel; protected function setUp() { - $helperManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $arguments = []; - $this->shipmentModel = $helperManager->getObject('Magento\Sales\Model\Order\Shipment', $arguments); + $helperManager = new ObjectManager($this); + + $this->initCommentsCollectionFactoryMock(); + + $this->shipmentModel = $helperManager->getObject(Shipment::class, [ + 'commentCollectionFactory' => $this->commentCollectionFactory + ]); } public function testGetIncrementId() @@ -24,4 +45,102 @@ class ShipmentTest extends \PHPUnit_Framework_TestCase $this->shipmentModel->setIncrementId('test_increment_id'); $this->assertEquals('test_increment_id', $this->shipmentModel->getIncrementId()); } + + /** + * @covers \Magento\Sales\Model\Order\Shipment::getCommentsCollection + */ + public function testGetCommentsCollection() + { + $shipmentId = 1; + $this->shipmentModel->setId($shipmentId); + + $shipmentItem = $this->getMockBuilder(ShipmentItem::class) + ->disableOriginalConstructor() + ->setMethods(['setShipment']) + ->getMock(); + $shipmentItem->expects(static::once()) + ->method('setShipment') + ->with($this->shipmentModel); + $collection = [$shipmentItem]; + + $this->commentCollection->expects(static::once()) + ->method('setShipmentFilter') + ->with($shipmentId) + ->willReturnSelf(); + $this->commentCollection->expects(static::once()) + ->method('setCreatedAtOrder') + ->willReturnSelf(); + + $this->commentCollection->expects(static::once()) + ->method('load') + ->willReturnSelf(); + + $reflection = new \ReflectionClass(Collection::class); + $reflectionProperty = $reflection->getProperty('_items'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->commentCollection, $collection); + + $expected = $this->shipmentModel->getCommentsCollection(); + + static::assertEquals($expected, $this->commentCollection); + } + + /** + * @covers \Magento\Sales\Model\Order\Shipment::getComments + */ + public function testGetComments() + { + $shipmentId = 1; + $this->shipmentModel->setId($shipmentId); + + $shipmentItem = $this->getMockBuilder(ShipmentItem::class) + ->disableOriginalConstructor() + ->setMethods(['setShipment']) + ->getMock(); + $shipmentItem->expects(static::once()) + ->method('setShipment') + ->with($this->shipmentModel); + $collection = [$shipmentItem]; + + $this->commentCollection->expects(static::once()) + ->method('setShipmentFilter') + ->with($shipmentId) + ->willReturnSelf(); + + $this->commentCollection->expects(static::once()) + ->method('load') + ->willReturnSelf(); + + $reflection = new \ReflectionClass(Collection::class); + $reflectionProperty = $reflection->getProperty('_items'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->commentCollection, $collection); + + $this->commentCollection->expects(static::once()) + ->method('getItems') + ->willReturn($collection); + + static::assertEquals($this->shipmentModel->getComments(), $collection); + } + + /** + * Creates mock for comments collection factory + * @return void + */ + private function initCommentsCollectionFactoryMock() + { + $this->commentCollection = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->setMethods(['setShipmentFilter', 'setCreatedAtOrder', 'getItems', 'load', '__wakeup']) + ->getMock(); + + $this->commentCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->commentCollectionFactory->expects(static::any()) + ->method('create') + ->willReturn($this->commentCollection); + } } diff --git a/app/code/Magento/Theme/Controller/Result/MessagePlugin.php b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php new file mode 100644 index 0000000000000000000000000000000000000000..ab9ae231339462e4363de473f31e9b451dbe5d09 --- /dev/null +++ b/app/code/Magento/Theme/Controller/Result/MessagePlugin.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Controller\Result; + +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Message\MessageInterface; + +/** + * Plugin for putting messages to cookies + */ +class MessagePlugin +{ + /** + * Cookies name for messages + */ + const MESSAGES_COOKIES_NAME = 'mage-messages'; + + /** + * @var \Magento\Framework\Stdlib\CookieManagerInterface + */ + private $cookieManager; + + /** + * @var \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory + */ + private $cookieMetadataFactory; + + /** + * @var \Magento\Framework\Message\ManagerInterface + */ + private $messageManager; + + /** + * @var \Magento\Framework\View\Element\Message\InterpretationStrategyInterface + */ + private $interpretationStrategy; + + /** + * @var \Magento\Framework\Json\Helper\Data + */ + private $jsonHelper; + + /** + * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager + * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory + * @param \Magento\Framework\Message\ManagerInterface $messageManager + * @param \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy + * @param \Magento\Framework\Json\Helper\Data $jsonHelper + */ + public function __construct( + \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager, + \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory, + \Magento\Framework\Message\ManagerInterface $messageManager, + \Magento\Framework\View\Element\Message\InterpretationStrategyInterface $interpretationStrategy, + \Magento\Framework\Json\Helper\Data $jsonHelper + ) { + $this->cookieManager = $cookieManager; + $this->cookieMetadataFactory = $cookieMetadataFactory; + $this->messageManager = $messageManager; + $this->jsonHelper = $jsonHelper; + $this->interpretationStrategy = $interpretationStrategy; + } + + /** + * @param ResultInterface $subject + * @param ResultInterface $result + * @return ResultInterface + */ + public function afterRenderResult( + ResultInterface $subject, + ResultInterface $result + ) { + if (!($subject instanceof Json)) { + $publicCookieMetadata = $this->cookieMetadataFactory->createPublicCookieMetadata(); + $publicCookieMetadata->setDurationOneYear(); + $publicCookieMetadata->setPath('/'); + $publicCookieMetadata->setHttpOnly(false); + $this->cookieManager->setPublicCookie( + self::MESSAGES_COOKIES_NAME, + $this->jsonHelper->jsonEncode($this->getMessages()), + $publicCookieMetadata + ); + } + + return $result; + } + + /** + * Return messages array and clean message manager messages + * + * @return array + */ + protected function getMessages() + { + $messages = $this->getCookiesMessages(); + /** @var MessageInterface $message */ + foreach ($this->messageManager->getMessages(true)->getItems() as $message) { + $messages[] = [ + 'type' => $message->getType(), + 'text' => $this->interpretationStrategy->interpret($message), + ]; + } + return $messages; + } + + /** + * Return messages stored in cookies + * + * @return array + */ + protected function getCookiesMessages() + { + try { + $messages = $this->jsonHelper->jsonDecode( + $this->cookieManager->getCookie(self::MESSAGES_COOKIES_NAME, $this->jsonHelper->jsonEncode([])) + ); + if (!is_array($messages)) { + $messages = []; + } + } catch (\Zend_Json_Exception $e) { + $messages = []; + } + return $messages; + } +} diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php new file mode 100644 index 0000000000000000000000000000000000000000..33ddec92b1fbd2806f1680ebfaa8775a04f8fb46 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/MessagePluginTest.php @@ -0,0 +1,415 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Theme\Test\Unit\Controller\Result; + +use Magento\Framework\Controller\Result\Json; +use Magento\Framework\Controller\Result\Redirect; +use Magento\Framework\Json\Helper\Data; +use Magento\Framework\Message\Collection; +use Magento\Framework\Message\ManagerInterface; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory; +use Magento\Framework\Stdlib\Cookie\PublicCookieMetadata; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; +use Magento\Theme\Controller\Result\MessagePlugin; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class MessagePluginTest extends \PHPUnit_Framework_TestCase +{ + /** @var MessagePlugin */ + protected $model; + + /** @var CookieManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $cookieManagerMock; + + /** @var CookieMetadataFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $cookieMetadataFactoryMock; + + /** @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $managerMock; + + /** @var InterpretationStrategyInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $interpretationStrategyMock; + + /** @var Data|\PHPUnit_Framework_MockObject_MockObject */ + protected $dataMock; + + protected function setUp() + { + $this->cookieManagerMock = $this->getMockBuilder(CookieManagerInterface::class) + ->getMockForAbstractClass(); + $this->cookieMetadataFactoryMock = $this->getMockBuilder(CookieMetadataFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->managerMock = $this->getMockBuilder(ManagerInterface::class) + ->getMockForAbstractClass(); + $this->interpretationStrategyMock = $this->getMockBuilder(InterpretationStrategyInterface::class) + ->getMockForAbstractClass(); + $this->dataMock = $this->getMockBuilder(Data::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->model = new MessagePlugin( + $this->cookieManagerMock, + $this->cookieMetadataFactoryMock, + $this->managerMock, + $this->interpretationStrategyMock, + $this->dataMock + ); + } + + public function testAfterRenderResultJson() + { + /** @var Json|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Json::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieManagerMock->expects($this->never()) + ->method('setPublicCookie'); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } + + public function testAfterRenderResult() + { + + $existingMessages = [ + [ + 'type' => 'message0type', + 'text' => 'message0text', + ], + ]; + $messageType = 'message1type'; + $messageText = 'message1text'; + $messages = [ + [ + 'type' => $messageType, + 'text' => $messageText, + ], + ]; + $messages = array_merge($existingMessages, $messages); + + /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicCookieMetadata|\PHPUnit_Framework_MockObject_MockObject $cookieMetadataMock */ + $cookieMetadataMock = $this->getMockBuilder(PublicCookieMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieMetadataFactoryMock->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($cookieMetadataMock); + + $this->cookieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode($messages), + $cookieMetadataMock + ); + $this->cookieManagerMock->expects($this->once()) + ->method('getCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode([]) + ) + ->willReturn(\Zend_Json::encode($existingMessages)); + + $this->dataMock->expects($this->any()) + ->method('jsonDecode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::decode($data); + } + ); + $this->dataMock->expects($this->any()) + ->method('jsonEncode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::encode($data); + } + ); + + /** @var MessageInterface|\PHPUnit_Framework_MockObject_MockObject $messageMock */ + $messageMock = $this->getMockBuilder(MessageInterface::class) + ->getMock(); + $messageMock->expects($this->once()) + ->method('getType') + ->willReturn($messageType); + + $this->interpretationStrategyMock->expects($this->once()) + ->method('interpret') + ->with($messageMock) + ->willReturn($messageText); + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + $collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$messageMock]); + + $this->managerMock->expects($this->once()) + ->method('getMessages') + ->with(true, null) + ->willReturn($collectionMock); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } + + public function testAfterRenderResultWithoutExisting() + { + $messageType = 'message1type'; + $messageText = 'message1text'; + $messages = [ + [ + 'type' => $messageType, + 'text' => $messageText, + ], + ]; + + /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicCookieMetadata|\PHPUnit_Framework_MockObject_MockObject $cookieMetadataMock */ + $cookieMetadataMock = $this->getMockBuilder(PublicCookieMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieMetadataFactoryMock->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($cookieMetadataMock); + + $this->cookieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode($messages), + $cookieMetadataMock + ); + $this->cookieManagerMock->expects($this->once()) + ->method('getCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode([]) + ) + ->willReturn(\Zend_Json::encode([])); + + $this->dataMock->expects($this->any()) + ->method('jsonDecode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::decode($data); + } + ); + $this->dataMock->expects($this->any()) + ->method('jsonEncode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::encode($data); + } + ); + + /** @var MessageInterface|\PHPUnit_Framework_MockObject_MockObject $messageMock */ + $messageMock = $this->getMockBuilder(MessageInterface::class) + ->getMock(); + $messageMock->expects($this->once()) + ->method('getType') + ->willReturn($messageType); + + $this->interpretationStrategyMock->expects($this->once()) + ->method('interpret') + ->with($messageMock) + ->willReturn($messageText); + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + $collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$messageMock]); + + $this->managerMock->expects($this->once()) + ->method('getMessages') + ->with(true, null) + ->willReturn($collectionMock); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } + + public function testAfterRenderResultWithWrongJson() + { + $messageType = 'message1type'; + $messageText = 'message1text'; + $messages = [ + [ + 'type' => $messageType, + 'text' => $messageText, + ], + ]; + + /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicCookieMetadata|\PHPUnit_Framework_MockObject_MockObject $cookieMetadataMock */ + $cookieMetadataMock = $this->getMockBuilder(PublicCookieMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieMetadataFactoryMock->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($cookieMetadataMock); + + $this->cookieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode($messages), + $cookieMetadataMock + ); + $this->cookieManagerMock->expects($this->once()) + ->method('getCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode([]) + ) + ->willReturn(\Zend_Json::encode([])); + + $this->dataMock->expects($this->any()) + ->method('jsonDecode') + ->willThrowException(new \Zend_Json_Exception); + $this->dataMock->expects($this->any()) + ->method('jsonEncode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::encode($data); + } + ); + + /** @var MessageInterface|\PHPUnit_Framework_MockObject_MockObject $messageMock */ + $messageMock = $this->getMockBuilder(MessageInterface::class) + ->getMock(); + $messageMock->expects($this->once()) + ->method('getType') + ->willReturn($messageType); + + $this->interpretationStrategyMock->expects($this->once()) + ->method('interpret') + ->with($messageMock) + ->willReturn($messageText); + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + $collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$messageMock]); + + $this->managerMock->expects($this->once()) + ->method('getMessages') + ->with(true, null) + ->willReturn($collectionMock); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } + + public function testAfterRenderResultWithWrongArray() + { + $messageType = 'message1type'; + $messageText = 'message1text'; + $messages = [ + [ + 'type' => $messageType, + 'text' => $messageText, + ], + ]; + + /** @var Redirect|\PHPUnit_Framework_MockObject_MockObject $resultMock */ + $resultMock = $this->getMockBuilder(Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicCookieMetadata|\PHPUnit_Framework_MockObject_MockObject $cookieMetadataMock */ + $cookieMetadataMock = $this->getMockBuilder(PublicCookieMetadata::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->cookieMetadataFactoryMock->expects($this->once()) + ->method('createPublicCookieMetadata') + ->willReturn($cookieMetadataMock); + + $this->cookieManagerMock->expects($this->once()) + ->method('setPublicCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode($messages), + $cookieMetadataMock + ); + $this->cookieManagerMock->expects($this->once()) + ->method('getCookie') + ->with( + MessagePlugin::MESSAGES_COOKIES_NAME, + \Zend_Json::encode([]) + ) + ->willReturn(\Zend_Json::encode('string')); + + $this->dataMock->expects($this->any()) + ->method('jsonDecode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::decode($data); + } + ); + $this->dataMock->expects($this->any()) + ->method('jsonEncode') + ->willReturnCallback( + function ($data) { + return \Zend_Json::encode($data); + } + ); + + /** @var MessageInterface|\PHPUnit_Framework_MockObject_MockObject $messageMock */ + $messageMock = $this->getMockBuilder(MessageInterface::class) + ->getMock(); + $messageMock->expects($this->once()) + ->method('getType') + ->willReturn($messageType); + + $this->interpretationStrategyMock->expects($this->once()) + ->method('interpret') + ->with($messageMock) + ->willReturn($messageText); + + /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */ + $collectionMock = $this->getMockBuilder(Collection::class) + ->disableOriginalConstructor() + ->getMock(); + $collectionMock->expects($this->once()) + ->method('getItems') + ->willReturn([$messageMock]); + + $this->managerMock->expects($this->once()) + ->method('getMessages') + ->with(true, null) + ->willReturn($collectionMock); + + $this->assertEquals($resultMock, $this->model->afterRenderResult($resultMock, $resultMock)); + } +} diff --git a/app/code/Magento/Theme/etc/frontend/di.xml b/app/code/Magento/Theme/etc/frontend/di.xml index a260164e4f598eea2c1775a7ef71b439153f4f95..85086d694f378bf0ef862c4219188abf3d82b043 100644 --- a/app/code/Magento/Theme/etc/frontend/di.xml +++ b/app/code/Magento/Theme/etc/frontend/di.xml @@ -23,4 +23,7 @@ </argument> </arguments> </type> + <type name="Magento\Framework\Controller\ResultInterface"> + <plugin name="result-messages" type="Magento\Theme\Controller\Result\MessagePlugin"/> + </type> </config> diff --git a/app/code/Magento/Theme/view/frontend/templates/messages.phtml b/app/code/Magento/Theme/view/frontend/templates/messages.phtml index 595c4fa7d3d6ef9a9babf294f920149066371eee..2bd2357a27e1adbf79a9dbf7c34cc9c6d2d0da29 100644 --- a/app/code/Magento/Theme/view/frontend/templates/messages.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/messages.phtml @@ -5,6 +5,14 @@ */ ?> <div data-bind="scope: 'messages'"> + <div data-bind="foreach: { data: cookieMessages, as: 'message' }" class="messages"> + <div data-bind="attr: { + class: 'message-' + message.type + ' ' + message.type + ' message', + 'data-ui-id': 'message-' + message.type + }"> + <div data-bind="html: message.text"></div> + </div> + </div> <div data-bind="foreach: { data: messages().messages, as: 'message' }" class="messages"> <div data-bind="attr: { class: 'message-' + message.type + ' ' + message.type + ' message', diff --git a/app/code/Magento/Theme/view/frontend/web/js/view/messages.js b/app/code/Magento/Theme/view/frontend/web/js/view/messages.js index 29252aabb894a7f2cef00688a3648c195807984a..2969fdedd7d411a156befcbc456e5d8476fabde9 100644 --- a/app/code/Magento/Theme/view/frontend/web/js/view/messages.js +++ b/app/code/Magento/Theme/view/frontend/web/js/view/messages.js @@ -3,16 +3,24 @@ * See COPYING.txt for license details. */ define([ + 'jquery', 'uiComponent', - 'Magento_Customer/js/customer-data' -], function (Component, customerData) { + 'Magento_Customer/js/customer-data', + 'jquery/jquery-storageapi' +], function ($, Component, customerData) { 'use strict'; return Component.extend({ + defaults: { + cookieMessages: [], + messages: [] + }, initialize: function () { this._super(); + this.cookieMessages = $.cookieStorage.get('mage-messages'); this.messages = customerData.get('messages').extend({disposableCustomerData: 'messages'}); + $.cookieStorage.setConf({path: '/', expires: -1}).set('mage-messages', null); } }); }); diff --git a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php index 85a8b25c71510809c5e025f457594409e1bb5990..44754d37d405537e73d305e23d88a95c6c592cb6 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php +++ b/dev/tests/integration/framework/Magento/TestFramework/TestCase/AbstractController.php @@ -9,6 +9,10 @@ */ namespace Magento\TestFramework\TestCase; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; +use Magento\Theme\Controller\Result\MessagePlugin; + /** * @SuppressWarnings(PHPMD.NumberOfChildren) */ @@ -182,7 +186,7 @@ abstract class AbstractController extends \PHPUnit_Framework_TestCase * Assert that actual session messages meet expectations: * Usage examples: * $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR); - * $this->assertSessionMessages($this->equalTo(array('Entity has been saved.')), + * $this->assertSessionMessages($this->equalTo(['Entity has been saved.'], * \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS); * * @param \PHPUnit_Framework_Constraint $constraint Constraint to compare actual messages against @@ -196,6 +200,44 @@ abstract class AbstractController extends \PHPUnit_Framework_TestCase $messageManagerClass = 'Magento\Framework\Message\Manager' ) { $this->_assertSessionErrors = false; + + $messages = $this->getMessages($messageType, $messageManagerClass); + + $this->assertThat( + $messages, + $constraint, + 'Session messages do not meet expectations ' . var_export($messages, true) + ); + } + + /** + * Return all stored messages + * + * @param string|null $messageType + * @param string $messageManagerClass + * @return array + */ + protected function getMessages( + $messageType = null, + $messageManagerClass = 'Magento\Framework\Message\Manager' + ) { + return array_merge( + $this->getSessionMessages($messageType, $messageManagerClass), + $this->getCookieMessages($messageType) + ); + } + + /** + * Return messages stored in session + * + * @param string|null $messageType + * @param string $messageManagerClass + * @return array + */ + protected function getSessionMessages( + $messageType = null, + $messageManagerClass = 'Magento\Framework\Message\Manager' + ) { /** @var $messageManager \Magento\Framework\Message\ManagerInterface */ $messageManager = $this->_objectManager->get($messageManagerClass); /** @var $messages \Magento\Framework\Message\AbstractMessage[] */ @@ -204,14 +246,46 @@ abstract class AbstractController extends \PHPUnit_Framework_TestCase } else { $messages = $messageManager->getMessages()->getItemsByType($messageType); } + + /** @var $messageManager InterpretationStrategyInterface */ + $interpretationStrategy = $this->_objectManager->get(InterpretationStrategyInterface::class); + $actualMessages = []; foreach ($messages as $message) { - $actualMessages[] = $message->getText(); + $actualMessages[] = $interpretationStrategy->interpret($message); } - $this->assertThat( - $actualMessages, - $constraint, - 'Session messages do not meet expectations ' . var_export($actualMessages, true) - ); + + return $actualMessages; + } + + /** + * Return messages stored in cookies by type + * + * @param string|null $messageType + * @return array + */ + protected function getCookieMessages($messageType = null) + { + /** @var $cookieManager CookieManagerInterface */ + $cookieManager = $this->_objectManager->get(CookieManagerInterface::class); + try { + $messages = \Zend_Json::decode( + $cookieManager->getCookie(MessagePlugin::MESSAGES_COOKIES_NAME, \Zend_Json::encode([])) + ); + if (!is_array($messages)) { + $messages = []; + } + } catch (\Zend_Json_Exception $e) { + $messages = []; + } + + $actualMessages = []; + foreach ($messages as $message) { + if ($messageType === null || $message['type'] == $messageType) { + $actualMessages[] = $message['text']; + } + } + + return $actualMessages; } } diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/TestCase/ControllerAbstractTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/TestCase/ControllerAbstractTest.php index ad396dd7a36ea3577704c6ab23f77085c3f80dd7..b97d48584553520b3068d908cd0b5125b728471d 100644 --- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/TestCase/ControllerAbstractTest.php +++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/TestCase/ControllerAbstractTest.php @@ -5,6 +5,10 @@ */ namespace Magento\Test\TestCase; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; + /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -15,11 +19,27 @@ class ControllerAbstractTest extends \Magento\TestFramework\TestCase\AbstractCon /** @var \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\Message\Manager */ private $messageManager; + /** @var \PHPUnit_Framework_MockObject_MockObject | InterpretationStrategyInterface */ + private $interpretationStrategyMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject | CookieManagerInterface */ + private $cookieManagerMock; + protected function setUp() { $testObjectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->messageManager = $this->getMock('\Magento\Framework\Message\Manager', [], [], '', false); + $this->cookieManagerMock = $this->getMock(CookieManagerInterface::class, [], [], '', false); + $this->interpretationStrategyMock = $this->getMock(InterpretationStrategyInterface::class, [], [], '', false); + $this->interpretationStrategyMock->expects($this->any()) + ->method('interpret') + ->willReturnCallback( + function (MessageInterface $message) { + return $message->getText(); + } + ); + $request = $testObjectManager->getObject('Magento\TestFramework\Request'); $response = $testObjectManager->getObject('Magento\TestFramework\Response'); $this->_objectManager = $this->getMock( @@ -37,6 +57,8 @@ class ControllerAbstractTest extends \Magento\TestFramework\TestCase\AbstractCon ['Magento\Framework\App\RequestInterface', $request], ['Magento\Framework\App\ResponseInterface', $response], ['Magento\Framework\Message\Manager', $this->messageManager], + [CookieManagerInterface::class, $this->cookieManagerMock], + [InterpretationStrategyInterface::class, $this->interpretationStrategyMock], ] ) ); @@ -139,17 +161,21 @@ class ControllerAbstractTest extends \Magento\TestFramework\TestCase\AbstractCon public function assertSessionMessagesDataProvider() { return [ - 'message waning type filtering' => [ - ['some_warning'], - \Magento\Framework\Message\MessageInterface::TYPE_WARNING, + 'message warning type filtering' => [ + ['some_warning', 'warning_cookie'], + MessageInterface::TYPE_WARNING, ], 'message error type filtering' => [ - ['error_one', 'error_two'], - \Magento\Framework\Message\MessageInterface::TYPE_ERROR, + ['error_one', 'error_two', 'error_cookie'], + MessageInterface::TYPE_ERROR, + ], + 'message notice type filtering' => [ + ['some_notice', 'notice_cookie'], + MessageInterface::TYPE_NOTICE, ], 'message success type filtering' => [ - ['success!'], - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS, + ['success!', 'success_cookie'], + MessageInterface::TYPE_SUCCESS, ], ]; } @@ -166,6 +192,10 @@ class ControllerAbstractTest extends \Magento\TestFramework\TestCase\AbstractCon 'error_two', 'some_notice', 'success!', + 'warning_cookie', + 'notice_cookie', + 'success_cookie', + 'error_cookie', ] ) ); @@ -192,5 +222,28 @@ class ControllerAbstractTest extends \Magento\TestFramework\TestCase\AbstractCon ->addMessage(new \Magento\Framework\Message\Success('success!')); $this->messageManager->expects($this->any())->method('getMessages') ->will($this->returnValue($messagesCollection)); + + $cookieMessages = [ + [ + 'type' => 'warning', + 'text' => 'warning_cookie', + ], + [ + 'type' => 'notice', + 'text' => 'notice_cookie', + ], + [ + 'type' => 'success', + 'text' => 'success_cookie', + ], + [ + 'type' => 'error', + 'text' => 'error_cookie', + ], + ]; + + $this->cookieManagerMock->expects($this->any()) + ->method('getCookie') + ->willReturn(\Zend_Json::encode($cookieMessages)); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 2c476dd7d5e57affaeec4ead1774ad4395850623..413a8941d3d43226648f4ada9eaa59eb246404f7 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -7,6 +7,7 @@ // @codingStandardsIgnoreFile namespace Magento\Catalog\Controller\Product; +use Magento\Framework\Message\MessageInterface; /** * @magentoDataFixture Magento/Catalog/controllers/_files/products.php @@ -43,15 +44,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController ) ); - /** @var $messageManager \Magento\Framework\Message\Manager */ - $messageManager = $objectManager->get('Magento\Framework\Message\Manager'); - $this->assertInstanceOf( - 'Magento\Framework\Message\Success', - $messageManager->getMessages()->getLastAddedMessage() - ); - $this->assertContains( - 'Simple Product 1 Name', - (string)$messageManager->getMessages()->getLastAddedMessage()->getText() + $this->assertSessionMessages( + $this->equalTo(['You added product Simple Product 1 Name to the comparison list.']), + MessageInterface::TYPE_SUCCESS ); $this->assertRedirect(); @@ -76,16 +71,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController $product = $this->productRepository->get('simple_product_2'); $this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId()); - /** @var $messageManager \Magento\Framework\Message\Manager */ - $messageManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get('Magento\Framework\Message\Manager'); - $this->assertInstanceOf( - 'Magento\Framework\Message\Success', - $messageManager->getMessages()->getLastAddedMessage() - ); - $this->assertContains( - 'Simple Product 2 Name', - (string)$messageManager->getMessages()->getLastAddedMessage()->getText() + $this->assertSessionMessages( + $this->equalTo(['You removed product Simple Product 2 Name from the comparison list.']), + MessageInterface::TYPE_SUCCESS ); $this->assertRedirect(); @@ -99,15 +87,11 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController $product = $this->productRepository->get('simple_product_1'); $this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId()); $secondProduct = $this->productRepository->get('simple_product_2'); - /** @var $messageManager \Magento\Framework\Message\Manager */ - $messageManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get('Magento\Framework\Message\Manager'); - $this->assertInstanceOf( - 'Magento\Framework\Message\Success', - $messageManager->getMessages()->getLastAddedMessage() + + $this->assertSessionMessages( + $this->equalTo(['You removed product Simple Product 1 Name from the comparison list.']), + MessageInterface::TYPE_SUCCESS ); - $this->assertContains('Simple Product 1 Name', - (string)$messageManager->getMessages()->getLastAddedMessage()->getText()); $this->assertRedirect(); @@ -146,12 +130,9 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController $this->dispatch('catalog/product_compare/clear'); - /** @var $messageManager \Magento\Framework\Message\Manager */ - $messageManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->get('Magento\Framework\Message\Manager'); - $this->assertInstanceOf( - 'Magento\Framework\Message\Success', - $messageManager->getMessages()->getLastAddedMessage() + $this->assertSessionMessages( + $this->equalTo(['You cleared the comparison list.']), + MessageInterface::TYPE_SUCCESS ); $this->assertRedirect(); @@ -167,17 +148,13 @@ class CompareTest extends \Magento\TestFramework\TestCase\AbstractController $this->_prepareCompareListWithProductNameXss(); $product = $this->productRepository->get('product-with-xss'); $this->dispatch('catalog/product_compare/remove/product/' . $product->getEntityId() . '?nocookie=1'); - $messages = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - 'Magento\Framework\Message\Manager' - )->getMessages()->getItems(); - $isProductNamePresent = false; - foreach ($messages as $message) { - if (strpos($message->getText(), '<script>alert("xss");</script>') !== false) { - $isProductNamePresent = true; - } - $this->assertNotContains('<script>alert("xss");</script>', (string)$message->getText()); - } - $this->assertTrue($isProductNamePresent, 'Product name was not found in session messages'); + + $this->assertSessionMessages( + $this->equalTo( + ['You removed product <script>alert("xss");</script> from the comparison list.'] + ), + MessageInterface::TYPE_SUCCESS + ); } protected function _prepareCompareListWithProductNameXss() diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php index d0fe9b7cc3a30b2138ce266cc5420227f81f9cae..f224df65cd0211d91dffa707a5dcdacde190e25b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/multiselect_attribute.php @@ -38,11 +38,13 @@ $attribute->setData( 'option_1' => ['Option 1'], 'option_2' => ['Option 2'], 'option_3' => ['Option 3'], + 'option_4' => ['Option 4 "!@#$%^&*'], ], 'order' => [ 'option_1' => 1, 'option_2' => 2, 'option_3' => 3, + 'option_4' => 4, ], ], ] diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute.php index 3ccc3e58b64f1d28cbc3e7f80f0d55c251c54f48..dc70dc3719881251c2cf611fd4c70573b57fa7cf 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute.php @@ -59,7 +59,7 @@ $product->setTypeId( )->setVisibility( \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH )->setMultiselectAttribute( - [$optionIds[1], $optionIds[2]] + [$optionIds[1], $optionIds[2], $optionIds[3]] )->setStatus( \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED )->setStockData( diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php index 62577a8318b0a51d666c3feff28cf40387fc0148..9564636cf9aaa2cf3ca692587b44bc0211da1d71 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php @@ -77,7 +77,10 @@ class ProductTest extends \PHPUnit_Framework_TestCase $exportData = $this->model->export(); $this->assertContains('New Product', $exportData); - $this->assertContains('Option 1 Value 1', $exportData); + $this->assertContains('Option 1 & Value 1"', $exportData); + $this->assertContains('Option 1 & Value 2"', $exportData); + $this->assertContains('Option 1 & Value 3"', $exportData); + $this->assertContains('Option 4 ""!@#$%^&*', $exportData); $this->assertContains('test_option_code_2', $exportData); $this->assertContains('max_characters=10', $exportData); } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 3be0fa114309cd100e90f3b0145de47f422ce92a..962665967a3bc87e18d182a8ab58118af765d165 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -1073,7 +1073,7 @@ class ProductTest extends \Magento\TestFramework\Indexer\TestCase $this->assertTrue($errors->getErrorsCount() == $errorsCount); if ($errorsCount >= 1) { $this->assertEquals( - "Specified url key already exists", + "Specified URL key already exists", $errors->getErrorByRowNumber(1)[0]->getErrorMessage() ); } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php index 57e70b4b87748b7a630cb9436f71a3db821b6c38..5daefb4912b3f676d90e6eef7ee1d0f158616446 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/ProductTest.php @@ -103,8 +103,6 @@ class ProductTest extends AbstractProductExportImportTestCase 'simple', ] ], - // @todo uncomment after resolving MAGETWO-49676 - /* 'simple-product-crosssell' => [ [ 'Magento/Catalog/_files/products_crosssell.php' @@ -129,7 +127,6 @@ class ProductTest extends AbstractProductExportImportTestCase 'simple', ] ], - */ ]; } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php index fa2c18b2f5255d1e285c560408100091eb921c9c..9531a39ef804cc0fdb43c87190833a77faed3ecf 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/_files/product_export_data.php @@ -23,8 +23,9 @@ $customOptions = [ 'type' => 'drop_down', 'is_require' => 1, 'values' => [ - 1 => ['option_type_id' => -1, 'title' => 'Option 1 Value 1', 'price' => '1.00', 'price_type' => 'fixed'], - 2 => ['option_type_id' => -1, 'title' => 'Option 1 Value 2', 'price' => '2.00', 'price_type' => 'fixed'] + 1 => ['option_type_id' => -1, 'title' => 'Option 1 & Value 1"', 'price' => '1.00', 'price_type' => 'fixed'], + 2 => ['option_type_id' => -1, 'title' => 'Option 1 & Value 2"', 'price' => '2.00', 'price_type' => 'fixed'], + 3 => ['option_type_id' => -1, 'title' => 'Option 1 & Value 3"', 'price' => '3.00', 'price_type' => 'fixed'] ] ], [ diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Adminhtml/Paypal/ReportsTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Adminhtml/Paypal/ReportsTest.php index c8660cb759bde5c54d021b15a074c5684a3cd4ec..c18880325bb41872a51f6e07fe8c8aff612c55b7 100644 --- a/dev/tests/integration/testsuite/Magento/Paypal/Adminhtml/Paypal/ReportsTest.php +++ b/dev/tests/integration/testsuite/Magento/Paypal/Adminhtml/Paypal/ReportsTest.php @@ -23,7 +23,7 @@ class ReportsTest extends \Magento\TestFramework\TestCase\AbstractBackendControl { $this->dispatch('backend/paypal/paypal_reports/fetch'); $this->assertSessionMessages( - $this->equalTo(['We can\'t fetch reports from "login@127.0.0.1."']), + $this->equalTo(['We can\'t fetch reports from "login@127.0.0.1."']), \Magento\Framework\Message\MessageInterface::TYPE_ERROR ); } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php index 5bbd7dc1b8f55e43a69828e0d994f57af6b4cae5..e409a55c504b5b90e8ce4d2be55cd176f25f7e46 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Controller/IndexTest.php @@ -5,8 +5,6 @@ */ namespace Magento\Wishlist\Controller; -use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; - class IndexTest extends \Magento\TestFramework\TestCase\AbstractController { /** @@ -96,28 +94,16 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractController $product = $productRepository->get('product-with-xss'); $this->dispatch('wishlist/index/add/product/' . $product->getId() . '?nocookie=1'); - $messages = $this->_messages->getMessages()->getItems(); - $isProductNamePresent = false; - /** @var InterpretationStrategyInterface $interpretationStrategy */ - $interpretationStrategy = $this->_objectManager->create( - 'Magento\Framework\View\Element\Message\InterpretationStrategyInterface' + $this->assertSessionMessages( + $this->equalTo( + [ + "\n<script>alert("xss");</script> has been added to your Wish List. " + . 'Click <a href="http://localhost/index.php/">here</a> to continue shopping.', + ] + ), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS ); - foreach ($messages as $message) { - if ( - strpos( - $interpretationStrategy->interpret($message), - '<script>alert("xss");</script>' - ) !== false - ) { - $isProductNamePresent = true; - } - $this->assertNotContains( - '<script>alert("xss");</script>', - $interpretationStrategy->interpret($message) - ); - } - $this->assertTrue($isProductNamePresent, 'Product name was not found in session messages'); } /** diff --git a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Persistor.php b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Persistor.php index 5c7e80e7b3467cb7d185219311a6844217e71d79..14825ffd0f5a0b0725ef16de979deae475fb53fe 100644 --- a/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Persistor.php +++ b/lib/internal/Magento/Framework/ObjectManager/Code/Generator/Persistor.php @@ -282,11 +282,11 @@ class Persistor extends \Magento\Framework\Code\Generator\EntityAbstract */ protected function _getRegisterDeletedMethod() { - $body = "\$hash = spl_object_hash(\$entity);" - . "array_push(\$this->stack, \$hash);" - . "\$this->entitiesPool[\$hash] = [" - . " 'entity' => \$entity," - . " 'action' => 'removed'" + $body = "\$hash = spl_object_hash(\$entity);\n" + . "array_push(\$this->stack, \$hash);\n" + . "\$this->entitiesPool[\$hash] = [\n" + . " 'entity' => \$entity,\n" + . " 'action' => 'removed'\n" . "];"; return [ 'name' => 'registerDeleted', @@ -321,12 +321,13 @@ class Persistor extends \Magento\Framework\Code\Generator\EntityAbstract . " \$hash = array_pop(\$this->stack);\n" . " if (isset(\$this->entitiesPool[\$hash])) {\n" . " \$data = \$this->entitiesPool[\$hash];\n" + . " \$entity = \$data['entity'];\n" . " if (\$data['action'] == 'created') {\n" - . " \$this->{$this->_getSourceResourcePropertyName()}->save(\$data['entity']);\n" - . " \$ids[] = \$data['entity']->getId();\n" + . " \$this->{$this->_getSourceResourcePropertyName()}->save(\$entity);\n" + . " \$ids[] = \$entity->getId();\n" . " } else {\n" - . " \$ids[] = \$data['entity']->getId();\n" - . " \$this->{$this->_getSourceResourcePropertyName()}->delete(\$data['removed']);\n" + . " \$ids[] = \$entity->getId();\n" + . " \$this->{$this->_getSourceResourcePropertyName()}->delete(\$entity);\n" . " }\n" . " }\n" . " unset(\$this->entitiesPool[\$hash]);\n" @@ -372,14 +373,16 @@ class Persistor extends \Magento\Framework\Code\Generator\EntityAbstract protected function _getDoPersistEntityMethod() { $body = "\$hash = spl_object_hash(\$entity);\n" + . "\$action = 'created';\n" . "if (isset(\$this->entitiesPool[\$hash])) {\n" - . "\$tempStack = \$this->stack;\n" - . "array_flip(\$tempStack);\n" - . "unset(\$tempStack[\$hash]);\n" - . "\$this->stack = array_flip(\$tempStack);\n" - . "unset(\$this->entitiesPool[\$hash]);\n" + . " \$action = \$this->entitiesPool[\$hash]['action'];\n" + . " \$tempStack = \$this->stack;\n" + . " array_flip(\$tempStack);\n" + . " unset(\$tempStack[\$hash]);\n" + . " \$this->stack = array_flip(\$tempStack);\n" + . " unset(\$this->entitiesPool[\$hash]);\n" . "}\n" - . "\$this->registerNew(\$entity);\n" + . "\$action == 'created' ? \$this->registerNew(\$entity) : \$this->registerDeleted(\$entity);\n" . "return \$this->doPersist(1);"; return [ 'name' => 'doPersistEntity', @@ -401,6 +404,7 @@ class Persistor extends \Magento\Framework\Code\Generator\EntityAbstract ] ]; } + /** * Returns registerDelete() method * @@ -445,8 +449,8 @@ class Persistor extends \Magento\Framework\Code\Generator\EntityAbstract { $body = "\$hash = spl_object_hash(\$entity);\n" . "\$data = [\n" - . "'entity' => \$entity,\n" - . "'action' => 'created'\n" + . " 'entity' => \$entity,\n" + . " 'action' => 'created'\n" . "];\n" . "array_push(\$this->stack, \$hash);\n" . "\$this->entitiesPool[\$hash] = \$data;";