diff --git a/.htaccess b/.htaccess
index 71a5cf708dbc5..e07a564bc0ab6 100644
--- a/.htaccess
+++ b/.htaccess
@@ -37,29 +37,6 @@
DirectoryIndex index.php
-
-############################################
-## adjust memory limit
-
- php_value memory_limit 756M
- php_value max_execution_time 18000
-
-############################################
-## disable automatic session start
-## before autoload was initialized
-
- php_flag session.auto_start off
-
-############################################
-## enable resulting html compression
-
- #php_flag zlib.output_compression on
-
-###########################################
-## disable user agent verification to not break multiple image upload
-
- php_flag suhosin.session.cryptua off
-
############################################
## adjust memory limit
diff --git a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php
index ec2e697ccc849..b736700a4481d 100644
--- a/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php
+++ b/app/code/Magento/Catalog/Model/Product/ProductFrontendAction/Synchronizer.php
@@ -20,6 +20,8 @@
*
* Service which allows to sync product widget information, such as product id with db. In order to reuse this info
* on different devices
+ *
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class Synchronizer
{
@@ -94,6 +96,7 @@ public function __construct(
*
* @param string $namespace
* @return int
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
private function getLifeTimeByNamespace($namespace)
{
@@ -119,6 +122,7 @@ private function getLifeTimeByNamespace($namespace)
* @param array $productsData (product action data, that came from frontend)
* @param string $typeId namespace (type of action)
* @return array
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
private function filterNewestActions(array $productsData, $typeId)
{
@@ -166,6 +170,7 @@ private function getProductIdsByActions(array $actions)
* @param array $productsData
* @param string $typeId
* @return void
+ * @throws \Exception
*/
public function syncActions(array $productsData, $typeId)
{
@@ -189,8 +194,7 @@ public function syncActions(array $productsData, $typeId)
foreach ($collection as $item) {
$this->entityManager->delete($item);
}
-
- foreach ($productsData as $productId => $productData) {
+ foreach ($productsData as $productData) {
/** @var ProductFrontendActionInterface $action */
$action = $this->productFrontendActionFactory->create(
[
@@ -198,7 +202,7 @@ public function syncActions(array $productsData, $typeId)
'visitor_id' => $customerId ? null : $visitorId,
'customer_id' => $this->session->getCustomerId(),
'added_at' => $productData['added_at'],
- 'product_id' => $productId,
+ 'product_id' => $productData['product_id'],
'type_id' => $typeId
]
]
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
index 78cdac8e8a8a0..9a455c12e6055 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php
@@ -1911,6 +1911,7 @@ protected function _productLimitationJoinPrice()
* @param bool $joinLeft
* @return $this
* @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function _productLimitationPrice($joinLeft = false)
{
@@ -1929,14 +1930,14 @@ protected function _productLimitationPrice($joinLeft = false)
$connection = $this->getConnection();
$select = $this->getSelect();
- $joinCond = join(
- ' AND ',
- [
- 'price_index.entity_id = e.entity_id',
- $connection->quoteInto('price_index.website_id = ?', $filters['website_id']),
- $connection->quoteInto('price_index.customer_group_id = ?', $filters['customer_group_id'])
- ]
- );
+ $joinCondArray = [];
+ $joinCondArray[] = 'price_index.entity_id = e.entity_id';
+ $joinCondArray[] = $connection->quoteInto('price_index.customer_group_id = ?', $filters['customer_group_id']);
+ // Add website condition only if it's different from admin scope
+ if (((int) $filters['website_id']) !== Store::DEFAULT_STORE_ID) {
+ $joinCondArray[] = $connection->quoteInto('price_index.website_id = ?', $filters['website_id']);
+ }
+ $joinCond = join(' AND ', $joinCondArray);
$fromPart = $select->getPart(\Magento\Framework\DB\Select::FROM);
if (!isset($fromPart['price_index'])) {
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php
index 38bed83cb9504..8b70899cd26d3 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/ProductFrontendAction/SynchronizerTest.php
@@ -82,15 +82,15 @@ public function testFilterProductActions()
{
$typeId = 'recently_compared_product';
$productsData = [
- 1 => [
+ 'website-1-1' => [
'added_at' => 12,
'product_id' => 1,
],
- 2 => [
+ 'website-1-2' => [
'added_at' => 13,
'product_id' => '2',
],
- 3 => [
+ 'website-2-3' => [
'added_at' => 14,
'product_id' => 3,
]
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml
index 5a31f3d125c81..24cae93ca61c0 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml
@@ -4,11 +4,15 @@
* See COPYING.txt for license details.
*/
?>
-
+
-escapeHtml($block->getCustomAttributes()) ?>
- src="= $block->escapeUrl($block->getImageUrl()) ?>"
- width="= $block->escapeHtmlAttr($block->getWidth()) ?>"
- height="= $block->escapeHtmlAttr($block->getHeight()) ?>"
- alt="= /* @noEscape */ $block->stripTags($block->getLabel(), null, true) ?>" />
+escapeHtml($block->getCustomAttributes()) ?>
+ src="= $escaper->escapeUrl($block->getImageUrl()) ?>"
+ loading="lazy"
+ width="= $escaper->escapeHtmlAttr($block->getWidth()) ?>"
+ height="= $escaper->escapeHtmlAttr($block->getHeight()) ?>"
+ alt="= $escaper->escapeHtmlAttr($block->getLabel()) ?>" />
diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml
index 33f7620f1a1f5..e8ddabce504ea 100644
--- a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml
+++ b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml
@@ -4,16 +4,20 @@
* See COPYING.txt for license details.
*/
?>
-
+
+ style="width:= $escaper->escapeHtmlAttr($block->getWidth()) ?>px;">
- escapeHtmlAttr($block->getCustomAttributes()) ?>
- src="= $block->escapeUrl($block->getImageUrl()) ?>"
- max-width="= $block->escapeHtmlAttr($block->getWidth()) ?>"
- max-height="= $block->escapeHtmlAttr($block->getHeight()) ?>"
- alt="= /* @noEscape */ $block->stripTags($block->getLabel(), null, true) ?>"/>
+ escapeHtmlAttr($block->getCustomAttributes()) ?>
+ src="= $escaper->escapeUrl($block->getImageUrl()) ?>"
+ loading="lazy"
+ width="= $escaper->escapeHtmlAttr($block->getWidth()) ?>"
+ height="= $escaper->escapeHtmlAttr($block->getHeight()) ?>"
+ alt="= $escaper->escapeHtmlAttr($block->getLabel()) ?>"/>
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
index ae5f0f5d79e2a..d1bce7aaaf951 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php
@@ -307,6 +307,7 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
// Can't add new translated strings in patch release
'invalidLayoutUpdate' => 'Invalid format.',
'insufficientPermissions' => 'Invalid format.',
+ ValidatorInterface::ERROR_SKU_MARGINAL_WHITESPACES => 'SKU contains marginal whitespaces'
];
//@codingStandardsIgnoreEnd
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php
index f41596ad185a6..f13b603003898 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php
@@ -87,6 +87,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn
const ERROR_DUPLICATE_MULTISELECT_VALUES = 'duplicatedMultiselectValues';
+ const ERROR_SKU_MARGINAL_WHITESPACES = 'skuMarginalWhitespaces';
+
/**
* Value that means all entities (e.g. websites, groups etc.)
*/
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
index 4b7416f6ad9a6..b2eca68db4d1c 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/Validator.php
@@ -10,7 +10,7 @@
use Magento\Catalog\Model\Product\Attribute\Backend\Sku;
/**
- * Class Validator
+ * Product import model validator
*
* @api
* @since 100.0.2
@@ -72,8 +72,12 @@ protected function textValidation($attrCode, $type)
$val = $this->string->cleanString($this->_rowData[$attrCode]);
if ($type == 'text') {
$valid = $this->string->strlen($val) < Product::DB_MAX_TEXT_LENGTH;
- } else if ($attrCode == Product::COL_SKU) {
+ } elseif ($attrCode == Product::COL_SKU) {
$valid = $this->string->strlen($val) <= SKU::SKU_MAX_LENGTH;
+ if ($this->string->strlen($val) !== $this->string->strlen(trim($val))) {
+ $this->_addMessages([RowValidatorInterface::ERROR_SKU_MARGINAL_WHITESPACES]);
+ return false;
+ }
} else {
$valid = $this->string->strlen($val) < Product::DB_MAX_VARCHAR_LENGTH;
}
@@ -359,5 +363,7 @@ public function init($context)
foreach ($this->validators as $validator) {
$validator->init($context);
}
+
+ return $this;
}
}
diff --git a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php
index eca994de0892f..f9340a495de65 100644
--- a/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php
+++ b/app/code/Magento/CatalogWidget/Model/Rule/Condition/Product.php
@@ -11,6 +11,7 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\ProductCategoryList;
+use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Store\Model\Store;
/**
@@ -122,46 +123,34 @@ protected function _addSpecialAttributes(array &$attributes)
/**
* Add condition to collection
*
- * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+ * @param Collection $collection
* @return $this
*/
public function addToCollection($collection)
{
$attribute = $this->getAttributeObject();
+ $attributeCode = $attribute->getAttributeCode();
+ if ($attributeCode !== 'price' || !$collection->getLimitationFilters()->isUsingPriceIndex()) {
+ if ($collection->isEnabledFlat()) {
+ if ($attribute->isEnabledInFlat()) {
+ $alias = array_keys($collection->getSelect()->getPart('from'))[0];
+ $this->joinedAttributes[$attributeCode] = $alias . '.' . $attributeCode;
+ } else {
+ $alias = 'at_' . $attributeCode;
+ if (!in_array($alias, array_keys($collection->getSelect()->getPart('from')))) {
+ $collection->joinAttribute($attributeCode, "catalog_product/$attributeCode", 'entity_id');
+ }
- if ($collection->isEnabledFlat()) {
- if ($attribute->isEnabledInFlat()) {
- $alias = array_keys($collection->getSelect()->getPart('from'))[0];
- $this->joinedAttributes[$attribute->getAttributeCode()] = $alias . '.' . $attribute->getAttributeCode();
- } else {
- $alias = 'at_' . $attribute->getAttributeCode();
- if (!in_array($alias, array_keys($collection->getSelect()->getPart('from')))) {
- $collection->joinAttribute(
- $attribute->getAttributeCode(),
- 'catalog_product/'.$attribute->getAttributeCode(),
- 'entity_id'
- );
+ $this->joinedAttributes[$attributeCode] = $alias . '.value';
}
-
- $this->joinedAttributes[$attribute->getAttributeCode()] = $alias . '.value';
+ } elseif ($attributeCode !== 'category_ids' && !$attribute->isStatic()) {
+ $this->addAttributeToCollection($attribute, $collection);
+ $attributes = $this->getRule()->getCollectedAttributes();
+ $attributes[$attributeCode] = true;
+ $this->getRule()->setCollectedAttributes($attributes);
}
- return $this;
}
- if ('category_ids' == $attribute->getAttributeCode() || $attribute->isStatic()) {
- return $this;
- }
-
- if ($attribute->getBackend() && $attribute->isScopeGlobal()) {
- $this->addGlobalAttribute($attribute, $collection);
- } else {
- $this->addNotGlobalAttribute($attribute, $collection);
- }
-
- $attributes = $this->getRule()->getCollectedAttributes();
- $attributes[$attribute->getAttributeCode()] = true;
- $this->getRule()->setCollectedAttributes($attributes);
-
return $this;
}
@@ -169,12 +158,12 @@ public function addToCollection($collection)
* Adds Attributes that belong to Global Scope
*
* @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
- * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+ * @param Collection $collection
* @return $this
*/
protected function addGlobalAttribute(
\Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute,
- \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+ Collection $collection
) {
switch ($attribute->getBackendType()) {
case 'decimal':
@@ -207,12 +196,12 @@ protected function addGlobalAttribute(
* Adds Attributes that don't belong to Global Scope
*
* @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
- * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+ * @param Collection $collection
* @return $this
*/
protected function addNotGlobalAttribute(
\Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute,
- \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
+ Collection $collection
) {
$storeId = $this->storeManager->getStore()->getId();
$values = $collection->getAllAttributeValues($attribute);
@@ -255,6 +244,8 @@ public function getMappedSqlField()
$result = parent::getMappedSqlField();
} elseif (isset($this->joinedAttributes[$this->getAttribute()])) {
$result = $this->joinedAttributes[$this->getAttribute()];
+ } elseif ($this->getAttribute() === 'price') {
+ $result = 'price_index.min_price';
} elseif ($this->getAttributeObject()->isStatic()) {
$result = $this->getAttributeObject()->getAttributeCode();
} elseif ($this->getValueParsed()) {
@@ -267,11 +258,27 @@ public function getMappedSqlField()
/**
* @inheritdoc
*
- * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection
+ * @param Collection $productCollection
* @return $this
*/
public function collectValidatedAttributes($productCollection)
{
return $this->addToCollection($productCollection);
}
+
+ /**
+ * Add attribute to collection based on scope
+ *
+ * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute
+ * @param Collection $collection
+ * @return void
+ */
+ private function addAttributeToCollection($attribute, $collection): void
+ {
+ if ($attribute->getBackend() && $attribute->isScopeGlobal()) {
+ $this->addGlobalAttribute($attribute, $collection);
+ } else {
+ $this->addNotGlobalAttribute($attribute, $collection);
+ }
+ }
}
diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php
index 97d0b35a2354f..a3370b2666264 100644
--- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php
+++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Directive.php
@@ -21,6 +21,7 @@
use Magento\Framework\Controller\Result\RawFactory;
use Magento\Backend\App\Action\Context;
use Magento\Framework\App\ObjectManager;
+use Magento\Framework\Filesystem\Driver\File;
/**
* Process template text for wysiwyg editor.
@@ -67,6 +68,11 @@ class Directive extends Action implements HttpGetActionInterface
*/
private $filter;
+ /**
+ * @var File
+ */
+ private $file;
+
/**
* Constructor
*
@@ -77,6 +83,7 @@ class Directive extends Action implements HttpGetActionInterface
* @param LoggerInterface|null $logger
* @param Config|null $config
* @param Filter|null $filter
+ * @param File|null $file
*/
public function __construct(
Context $context,
@@ -85,7 +92,8 @@ public function __construct(
AdapterFactory $adapterFactory = null,
LoggerInterface $logger = null,
Config $config = null,
- Filter $filter = null
+ Filter $filter = null,
+ File $file = null
) {
parent::__construct($context);
$this->urlDecoder = $urlDecoder;
@@ -94,6 +102,7 @@ public function __construct(
$this->logger = $logger ?: ObjectManager::getInstance()->get(LoggerInterface::class);
$this->config = $config ?: ObjectManager::getInstance()->get(Config::class);
$this->filter = $filter ?: ObjectManager::getInstance()->get(Filter::class);
+ $this->file = $file ?: ObjectManager::getInstance()->get(File::class);
}
/**
@@ -127,6 +136,15 @@ public function execute()
$this->logger->warning($e);
}
}
+ $mimeType = $image->getMimeType();
+ unset($image);
+ // To avoid issues with PNG images with alpha blending we return raw file
+ // after validation as an image source instead of generating the new PNG image
+ // with image adapter
+ $content = $this->file->fileGetContents($imagePath);
+ $resultRaw->setHeader('Content-Type', $mimeType);
+ $resultRaw->setContents($content);
+
return $resultRaw;
}
}
diff --git a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php
index 5fea276225622..be9f6b8d8ccd5 100644
--- a/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Controller/Adminhtml/Wysiwyg/DirectiveTest.php
@@ -5,100 +5,123 @@
*/
namespace Magento\Cms\Test\Unit\Controller\Adminhtml\Wysiwyg;
+use Magento\Backend\App\Action\Context;
+use Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive;
+use Magento\Cms\Model\Template\Filter;
+use Magento\Cms\Model\Wysiwyg\Config;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\App\ResponseInterface;
+use Magento\Framework\Controller\Result\Raw;
+use Magento\Framework\Controller\Result\RawFactory;
+use Magento\Framework\Filesystem\Driver\File;
+use Magento\Framework\Image\Adapter\AdapterInterface;
+use Magento\Framework\Image\AdapterFactory;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Framework\Url\DecoderInterface;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject;
+use Psr\Log\LoggerInterface;
+
/**
* @covers \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class DirectiveTest extends \PHPUnit\Framework\TestCase
+class DirectiveTest extends TestCase
{
const IMAGE_PATH = 'pub/media/wysiwyg/image.jpg';
/**
- * @var \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive
+ * @var Directive
*/
protected $wysiwygDirective;
/**
- * @var \Magento\Backend\App\Action\Context|\PHPUnit_Framework_MockObject_MockObject
+ * @var Context|PHPUnit_Framework_MockObject_MockObject
*/
protected $actionContextMock;
/**
- * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var RequestInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $requestMock;
/**
- * @var \Magento\Framework\Url\DecoderInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var DecoderInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $urlDecoderMock;
/**
- * @var \Magento\Framework\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ObjectManagerInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $objectManagerMock;
/**
- * @var \Magento\Cms\Model\Template\Filter|\PHPUnit_Framework_MockObject_MockObject
+ * @var Filter|PHPUnit_Framework_MockObject_MockObject
*/
protected $templateFilterMock;
/**
- * @var \Magento\Framework\Image\AdapterFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var AdapterFactory|PHPUnit_Framework_MockObject_MockObject
*/
protected $imageAdapterFactoryMock;
/**
- * @var \Magento\Framework\Image\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var AdapterInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $imageAdapterMock;
/**
- * @var \Magento\Framework\App\ResponseInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ResponseInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $responseMock;
/**
- * @var \Magento\Cms\Model\Wysiwyg\Config|\PHPUnit_Framework_MockObject_MockObject
+ * @var File|PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $fileMock;
+
+ /**
+ * @var Config|PHPUnit_Framework_MockObject_MockObject
*/
protected $wysiwygConfigMock;
/**
- * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var LoggerInterface|PHPUnit_Framework_MockObject_MockObject
*/
protected $loggerMock;
/**
- * @var \Magento\Framework\Controller\Result\RawFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var RawFactory|PHPUnit_Framework_MockObject_MockObject
*/
protected $rawFactoryMock;
/**
- * @var \Magento\Framework\Controller\Result\Raw|\PHPUnit_Framework_MockObject_MockObject
+ * @var Raw|PHPUnit_Framework_MockObject_MockObject
*/
protected $rawMock;
protected function setUp()
{
- $this->actionContextMock = $this->getMockBuilder(\Magento\Backend\App\Action\Context::class)
+ $this->actionContextMock = $this->getMockBuilder(Context::class)
->disableOriginalConstructor()
->getMock();
- $this->requestMock = $this->getMockBuilder(\Magento\Framework\App\RequestInterface::class)
+ $this->requestMock = $this->getMockBuilder(RequestInterface::class)
->disableOriginalConstructor()
->getMock();
- $this->urlDecoderMock = $this->getMockBuilder(\Magento\Framework\Url\DecoderInterface::class)
+ $this->urlDecoderMock = $this->getMockBuilder(DecoderInterface::class)
->disableOriginalConstructor()
->getMock();
- $this->objectManagerMock = $this->getMockBuilder(\Magento\Framework\ObjectManagerInterface::class)
+ $this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class)
->disableOriginalConstructor()
->getMock();
- $this->templateFilterMock = $this->getMockBuilder(\Magento\Cms\Model\Template\Filter::class)
+ $this->templateFilterMock = $this->getMockBuilder(Filter::class)
->disableOriginalConstructor()
->getMock();
- $this->imageAdapterFactoryMock = $this->getMockBuilder(\Magento\Framework\Image\AdapterFactory::class)
+ $this->imageAdapterFactoryMock = $this->getMockBuilder(AdapterFactory::class)
->disableOriginalConstructor()
->getMock();
- $this->imageAdapterMock = $this->getMockBuilder(\Magento\Framework\Image\Adapter\AdapterInterface::class)
+ $this->imageAdapterMock = $this->getMockBuilder(AdapterInterface::class)
->disableOriginalConstructor()
->setMethods(
[
@@ -117,21 +140,25 @@ protected function setUp()
]
)
->getMock();
- $this->responseMock = $this->getMockBuilder(\Magento\Framework\App\ResponseInterface::class)
+ $this->responseMock = $this->getMockBuilder(ResponseInterface::class)
->disableOriginalConstructor()
->setMethods(['setHeader', 'setBody', 'sendResponse'])
->getMock();
- $this->wysiwygConfigMock = $this->getMockBuilder(\Magento\Cms\Model\Wysiwyg\Config::class)
+ $this->fileMock = $this->getMockBuilder(File::class)
->disableOriginalConstructor()
+ ->setMethods(['fileGetContents'])
->getMock();
- $this->loggerMock = $this->getMockBuilder(\Psr\Log\LoggerInterface::class)
+ $this->wysiwygConfigMock = $this->getMockBuilder(Config::class)
->disableOriginalConstructor()
->getMock();
- $this->rawFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\RawFactory::class)
+ $this->loggerMock = $this->getMockBuilder(LoggerInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->rawFactoryMock = $this->getMockBuilder(RawFactory::class)
->setMethods(['create'])
->disableOriginalConstructor()
->getMock();
- $this->rawMock = $this->getMockBuilder(\Magento\Framework\Controller\Result\Raw::class)
+ $this->rawMock = $this->getMockBuilder(Raw::class)
->disableOriginalConstructor()
->getMock();
@@ -145,9 +172,9 @@ protected function setUp()
->method('getObjectManager')
->willReturn($this->objectManagerMock);
- $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $objectManager = new ObjectManager($this);
$this->wysiwygDirective = $objectManager->getObject(
- \Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive::class,
+ Directive::class,
[
'context' => $this->actionContextMock,
'urlDecoder' => $this->urlDecoderMock,
@@ -155,7 +182,8 @@ protected function setUp()
'adapterFactory' => $this->imageAdapterFactoryMock,
'logger' => $this->loggerMock,
'config' => $this->wysiwygConfigMock,
- 'filter' => $this->templateFilterMock
+ 'filter' => $this->templateFilterMock,
+ 'file' => $this->fileMock,
]
);
}
@@ -172,23 +200,29 @@ public function testExecute()
$this->imageAdapterMock->expects($this->once())
->method('open')
->with(self::IMAGE_PATH);
- $this->imageAdapterMock->expects($this->once())
+ $this->imageAdapterMock->expects($this->atLeastOnce())
->method('getMimeType')
->willReturn($mimeType);
- $this->rawMock->expects($this->once())
+ $this->rawMock->expects($this->atLeastOnce())
->method('setHeader')
->with('Content-Type', $mimeType)
->willReturnSelf();
- $this->rawMock->expects($this->once())
+ $this->rawMock->expects($this->atLeastOnce())
->method('setContents')
->with($imageBody)
->willReturnSelf();
$this->imageAdapterMock->expects($this->once())
->method('getImage')
->willReturn($imageBody);
+ $this->fileMock->expects($this->once())
+ ->method('fileGetContents')
+ ->willReturn($imageBody);
$this->rawFactoryMock->expects($this->any())
->method('create')
->willReturn($this->rawMock);
+ $this->imageAdapterFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->imageAdapterMock);
$this->assertSame(
$this->rawMock,
@@ -217,20 +251,23 @@ public function testExecuteException()
$this->imageAdapterMock->expects($this->at(1))
->method('open')
->with($placeholderPath);
- $this->imageAdapterMock->expects($this->once())
+ $this->imageAdapterMock->expects($this->atLeastOnce())
->method('getMimeType')
->willReturn($mimeType);
- $this->rawMock->expects($this->once())
+ $this->rawMock->expects($this->atLeastOnce())
->method('setHeader')
->with('Content-Type', $mimeType)
->willReturnSelf();
- $this->rawMock->expects($this->once())
+ $this->rawMock->expects($this->atLeastOnce())
->method('setContents')
->with($imageBody)
->willReturnSelf();
- $this->imageAdapterMock->expects($this->once())
+ $this->imageAdapterMock->expects($this->any())
->method('getImage')
->willReturn($imageBody);
+ $this->fileMock->expects($this->once())
+ ->method('fileGetContents')
+ ->willReturn($imageBody);
$this->loggerMock->expects($this->once())
->method('warning')
->with($exception);
@@ -238,6 +275,10 @@ public function testExecuteException()
->method('create')
->willReturn($this->rawMock);
+ $this->imageAdapterFactoryMock->expects($this->exactly(1))
+ ->method('create')
+ ->willReturn($this->imageAdapterMock);
+
$this->assertSame(
$this->rawMock,
$this->wysiwygDirective->execute()
@@ -297,6 +338,20 @@ public function testExecuteWithDeletedImage()
->method('warning')
->with($exception);
+ $this->rawMock->expects($this->once())
+ ->method('setHeader')
+ ->with('Content-Type', null)
+ ->willReturnSelf();
+
+ $this->rawMock->expects($this->once())
+ ->method('setContents')
+ ->with(null)
+ ->willReturnSelf();
+
+ $this->rawFactoryMock->expects($this->any())
+ ->method('create')
+ ->willReturn($this->rawMock);
+
$this->wysiwygDirective->execute();
}
}
diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php
index 53d8ee5220768..781d6d31246ca 100644
--- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/Column/PageActionsTest.php
@@ -3,91 +3,112 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Cms\Test\Unit\Ui\Component\Listing\Column;
use Magento\Cms\Ui\Component\Listing\Column\PageActions;
+use Magento\Cms\ViewModel\Page\Grid\UrlBuilder;
use Magento\Framework\Escaper;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Framework\UrlInterface;
+use Magento\Framework\View\Element\UiComponent\ContextInterface;
+use Magento\Framework\View\Element\UiComponent\Processor;
+use PHPUnit\Framework\TestCase;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
/**
* Test for Magento\Cms\Ui\Component\Listing\Column\PageActions class.
*/
-class PageActionsTest extends \PHPUnit\Framework\TestCase
+class PageActionsTest extends TestCase
{
- public function testPrepareItemsByPageId()
+
+ /**
+ * @var UrlInterface|MockObject
+ */
+ private $urlBuilderMock;
+
+ /**
+ * @var UrlBuilder|MockObject
+ */
+ private $scopeUrlBuilderMock;
+
+ /**
+ * @var ContextInterface|MockObject
+ */
+ private $contextMock;
+
+ /**
+ * @var Processor|MockObject
+ */
+ private $processorMock;
+
+ /**
+ * @var Escaper|MockObject
+ */
+ private $escaperMock;
+
+ /**
+ * @var PageActions
+ */
+ private $model;
+
+ /**
+ * @inheritDoc
+ */
+ public function setUp()
{
- $pageId = 1;
- // Create Mocks and SUT
- $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
- /** @var \PHPUnit_Framework_MockObject_MockObject $urlBuilderMock */
- $urlBuilderMock = $this->getMockBuilder(\Magento\Framework\UrlInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
- $contextMock = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\ContextInterface::class)
+ $this->urlBuilderMock = $this->createMock(UrlInterface::class);
+ $this->scopeUrlBuilderMock = $this->createMock(UrlBuilder::class);
+ $this->processorMock = $this->createMock(Processor::class);
+ $this->contextMock = $this->getMockBuilder(ContextInterface::class)
->getMockForAbstractClass();
- $processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class)
+ $this->escaperMock = $this->getMockBuilder(Escaper::class)
->disableOriginalConstructor()
+ ->setMethods(['escapeHtml'])
->getMock();
- $contextMock->expects($this->never())->method('getProcessor')->willReturn($processor);
- /** @var \Magento\Cms\Ui\Component\Listing\Column\PageActions $model */
- $model = $objectManager->getObject(
- \Magento\Cms\Ui\Component\Listing\Column\PageActions::class,
+ $objectManager = new ObjectManager($this);
+
+ $this->model = $objectManager->getObject(
+ PageActions::class,
[
- 'urlBuilder' => $urlBuilderMock,
- 'context' => $contextMock,
+ 'urlBuilder' => $this->urlBuilderMock,
+ 'context' => $this->contextMock,
+ 'scopeUrlBuilder' => $this->scopeUrlBuilderMock
]
);
- $escaper = $this->getMockBuilder(Escaper::class)
- ->disableOriginalConstructor()
- ->setMethods(['escapeHtml'])
- ->getMock();
- $objectManager->setBackwardCompatibleProperty($model, 'escaper', $escaper);
-
- // Define test input and expectations
- $title = 'page title';
- $items = [
- 'data' => [
- 'items' => [
- [
- 'page_id' => $pageId,
- 'title' => $title
- ]
- ]
- ]
- ];
- $name = 'item_name';
- $expectedItems = [
- [
- 'page_id' => $pageId,
- 'title' => $title,
- $name => [
- 'edit' => [
- 'href' => 'test/url/edit',
- 'label' => __('Edit'),
- '__disableTmpl' => true,
- ],
- 'delete' => [
- 'href' => 'test/url/delete',
- 'label' => __('Delete'),
- 'confirm' => [
- 'title' => __('Delete %1', $title),
- 'message' => __('Are you sure you want to delete a %1 record?', $title),
- '__disableTmpl' => true,
- ],
- 'post' => true,
- '__disableTmpl' => true,
- ],
- ],
- ],
- ];
+ $objectManager->setBackwardCompatibleProperty($this->model, 'escaper', $this->escaperMock);
+ }
- $escaper->expects(static::once())
+ /**
+ * Verify Prepare Items by page Id.
+ *
+ * @dataProvider configDataProvider
+ * @param int $pageId
+ * @param string $title
+ * @param string $name
+ * @param array $items
+ * @param array $expectedItems
+ * @return void
+ */
+ public function testPrepareItemsByPageId(
+ int $pageId,
+ string $title,
+ string $name,
+ array $items,
+ array $expectedItems
+ ):void {
+ $this->contextMock->expects($this->never())
+ ->method('getProcessor')
+ ->willReturn($this->processorMock);
+ $this->escaperMock->expects(static::once())
->method('escapeHtml')
->with($title)
->willReturn($title);
// Configure mocks and object data
- $urlBuilderMock->expects($this->any())
+ $this->urlBuilderMock->expects($this->any())
->method('getUrl')
->willReturnMap(
[
@@ -107,9 +128,77 @@ public function testPrepareItemsByPageId()
],
]
);
- $model->setName($name);
- $items = $model->prepareDataSource($items);
+
+ $this->scopeUrlBuilderMock->expects($this->any())
+ ->method('getUrl')
+ ->willReturn('test/url/view');
+
+ $this->model->setName($name);
+ $items = $this->model->prepareDataSource($items);
// Run test
$this->assertEquals($expectedItems, $items['data']['items']);
}
+
+ /**
+ * Data provider for testPrepareItemsByPageId
+ *
+ * @return array
+ */
+ public function configDataProvider():array
+ {
+ $pageId = 1;
+ $title = 'page title';
+ $identifier = 'page_identifier';
+ $name = 'item_name';
+
+ return [
+ [
+ 'pageId' => $pageId,
+ 'title' => $title,
+ 'name' => $name,
+ 'items' => [
+ 'data' => [
+ 'items' => [
+ [
+ 'page_id' => $pageId,
+ 'title' => $title,
+ 'identifier' => $identifier
+ ]
+ ]
+ ]
+ ],
+ 'expectedItems' => [
+ [
+ 'page_id' => $pageId,
+ 'title' => $title,
+ 'identifier' => $identifier,
+ $name => [
+ 'edit' => [
+ 'href' => 'test/url/edit',
+ 'label' => __('Edit'),
+ '__disableTmpl' => true,
+ ],
+ 'delete' => [
+ 'href' => 'test/url/delete',
+ 'label' => __('Delete'),
+ 'confirm' => [
+ 'title' => __('Delete %1', $title),
+ 'message' => __('Are you sure you want to delete a %1 record?', $title),
+ '__disableTmpl' => true,
+ ],
+ 'post' => true,
+ '__disableTmpl' => true,
+ ],
+ 'preview' => [
+ 'href' => 'test/url/view',
+ 'label' => __('View'),
+ '__disableTmpl' => true,
+ 'target' => '_blank'
+ ]
+ ],
+ ],
+ ]
+ ]
+ ];
+ }
}
diff --git a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php
index fa3756abfded4..0c6000bdbab84 100644
--- a/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php
+++ b/app/code/Magento/Cms/Ui/Component/Listing/Column/PageActions.php
@@ -14,7 +14,7 @@
use Magento\Ui\Component\Listing\Columns\Column;
/**
- * Class PageActions
+ * Class prepare Page Actions
*/
class PageActions extends Column
{
@@ -111,6 +111,7 @@ public function prepareDataSource(array $dataSource)
),
'label' => __('View'),
'__disableTmpl' => true,
+ 'target' => '_blank'
];
}
}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
index 3d797e62c806a..847ee728d5e78 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
@@ -61,6 +61,8 @@
+
+
diff --git a/app/code/Magento/Customer/Model/Indexer/Processor.php b/app/code/Magento/Customer/Model/Indexer/Processor.php
new file mode 100644
index 0000000000000..6b44b674b405a
--- /dev/null
+++ b/app/code/Magento/Customer/Model/Indexer/Processor.php
@@ -0,0 +1,16 @@
+
-
+
-
+
+
diff --git a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php
index 0f4c5f82bfe1d..4a22dc83a1f31 100644
--- a/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php
+++ b/app/code/Magento/CustomerImportExport/Model/Import/CustomerComposite.php
@@ -6,6 +6,7 @@
namespace Magento\CustomerImportExport\Model\Import;
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
+use Magento\Customer\Model\Indexer\Processor;
/**
* Import entity customer combined model
@@ -148,6 +149,11 @@ class CustomerComposite extends \Magento\ImportExport\Model\Import\AbstractEntit
*/
protected $masterAttributeCode = 'email';
+ /**
+ * @var Processor
+ */
+ private $indexerProcessor;
+
/**
* @param \Magento\Framework\Stdlib\StringUtils $string
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
@@ -158,6 +164,7 @@ class CustomerComposite extends \Magento\ImportExport\Model\Import\AbstractEntit
* @param \Magento\CustomerImportExport\Model\ResourceModel\Import\CustomerComposite\DataFactory $dataFactory
* @param \Magento\CustomerImportExport\Model\Import\CustomerFactory $customerFactory
* @param \Magento\CustomerImportExport\Model\Import\AddressFactory $addressFactory
+ * @param Processor $indexerProcessor
* @param array $data
* @throws \Magento\Framework\Exception\LocalizedException
*
@@ -173,6 +180,7 @@ public function __construct(
\Magento\CustomerImportExport\Model\ResourceModel\Import\CustomerComposite\DataFactory $dataFactory,
\Magento\CustomerImportExport\Model\Import\CustomerFactory $customerFactory,
\Magento\CustomerImportExport\Model\Import\AddressFactory $addressFactory,
+ Processor $indexerProcessor,
array $data = []
) {
parent::__construct($string, $scopeConfig, $importFactory, $resourceHelper, $resource, $errorAggregator, $data);
@@ -230,6 +238,7 @@ public function __construct(
} else {
$this->_nextCustomerId = $resourceHelper->getNextAutoincrement($this->_customerEntity->getEntityTable());
}
+ $this->indexerProcessor = $indexerProcessor;
}
/**
@@ -273,11 +282,12 @@ protected function _importData()
$this->countItemsCreated += $this->_customerEntity->getCreatedItemsCount();
$this->countItemsUpdated += $this->_customerEntity->getUpdatedItemsCount();
$this->countItemsDeleted += $this->_customerEntity->getDeletedItemsCount();
-
if ($this->getBehavior() != \Magento\ImportExport\Model\Import::BEHAVIOR_DELETE) {
- return $result && $this->_addressEntity->setCustomerAttributes($this->_customerAttributes)->importData();
+ $result = $result && $this->_addressEntity->setCustomerAttributes($this->_customerAttributes)->importData();
+ }
+ if ($result) {
+ $this->indexerProcessor->markIndexerAsInvalid();
}
-
return $result;
}
diff --git a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php
index 1b900c2139588..7aff0f911c2b0 100644
--- a/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php
+++ b/app/code/Magento/CustomerImportExport/Test/Unit/Model/Import/CustomerCompositeTest.php
@@ -15,7 +15,7 @@
use Magento\ImportExport\Model\Import\Source\Csv;
/**
- * Customer composite test
+ * The test for Customer composite model
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
@@ -88,6 +88,12 @@ class CustomerCompositeTest extends \PHPUnit\Framework\TestCase
*/
protected $errorFactory;
+ /**
+ * @var \Magento\Customer\Model\Indexer\Processor
+ * |\PHPUnit\Framework\MockObject\MockObject
+ */
+ private $indexerProcessor;
+
/**
* Expected prepared data after method CustomerComposite::_prepareRowForDb
*
@@ -141,6 +147,7 @@ protected function setUp()
->getMock();
$this->_scopeConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
+ $this->indexerProcessor = $this->createMock(\Magento\Customer\Model\Indexer\Processor::class);
}
/**
@@ -159,6 +166,7 @@ protected function _createModelMock($data)
$this->_dataFactory,
$this->_customerFactory,
$this->_addressFactory,
+ $this->indexerProcessor,
$data
);
}
diff --git a/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php b/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php
index 732bdee314f7c..bb225a5c46228 100644
--- a/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php
+++ b/app/code/Magento/Multishipping/Model/Cart/CartTotalRepositoryPlugin.php
@@ -33,21 +33,21 @@ public function __construct(
}
/**
- * Overwrite the CartTotalRepository quoteTotal and update the shipping price
+ * Check multishipping update shipping price after get cart total
*
- * @param CartTotalRepository $subject
- * @param Totals $quoteTotals
- * @param String $cartId
- * @return Totals
- * @throws \Magento\Framework\Exception\NoSuchEntityException
+ * @param CartTotalRepository $subject
+ * @param Totals $quoteTotals
+ * @param int $cartId
+ * @return Totals
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function afterGet(
CartTotalRepository $subject,
Totals $quoteTotals,
- String $cartId
- ) {
+ $cartId
+ ) : Totals {
$quote = $this->quoteRepository->getActive($cartId);
if ($quote->getIsMultiShipping()) {
$shippingMethod = $quote->getShippingAddress()->getShippingMethod();
diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php
index 73b0b9ef3ca7a..8362699efbd45 100644
--- a/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php
+++ b/app/code/Magento/Multishipping/Test/Unit/Model/Cart/CartTotalRepositoryPluginTest.php
@@ -6,76 +6,145 @@
namespace Magento\Multishipping\Test\Unit\Model\Cart;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Quote\Api\CartRepositoryInterface;
+use Magento\Quote\Model\Cart\CartTotalRepository;
+use Magento\Quote\Model\Cart\Totals as QuoteTotals;
+use Magento\Quote\Model\Quote\Address as QuoteAddress;
+use Magento\Quote\Model\Quote\Address\Rate as QuoteAddressRate;
+use Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin;
+use Magento\Store\Model\Store;
+use PHPUnit\Framework\MockObject\MockObject;
+
class CartTotalRepositoryPluginTest extends \PHPUnit\Framework\TestCase
{
/**
- * @var \Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin
+ * Stub cart id
+ */
+ private const STUB_CART_ID = 10;
+
+ /**
+ * Stub shipping method
+ */
+ private const STUB_SHIPPING_METHOD = 'flatrate_flatrate';
+
+ /**
+ * Stub shipping price
+ */
+ private const STUB_SHIPPING_PRICE = '10.00';
+
+ /**
+ * @var CartTotalRepositoryPlugin
*/
private $modelRepository;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var CartTotalRepository|MockObject
+ */
+ private $quoteTotalRepositoryMock;
+
+ /**
+ * @var CartRepositoryInterface|MockObject
*/
private $quoteRepositoryMock;
- protected function setUp()
- {
- $this->quoteRepositoryMock = $this->createMock(\Magento\Quote\Api\CartRepositoryInterface::class);
- $this->modelRepository = new \Magento\Multishipping\Model\Cart\CartTotalRepositoryPlugin(
- $this->quoteRepositoryMock
- );
- }
+ /**
+ * @var QuoteTotals|MockObject
+ */
+ private $quoteTotalsMock;
/**
- * Test quotTotal from cartRepository after get($cartId) function is called
+ * @var QuoteAddress|MockObject
*/
- public function testAfterGet()
+ private $shippingAddressMock;
+
+ /**
+ * @var QuoteAddressRate|MockObject
+ */
+ private $shippingRateMock;
+
+ /**
+ * @var Store|MockObject
+ */
+ private $storeMock;
+
+ protected function setUp()
{
- $cartId = "10";
- $shippingMethod = 'flatrate_flatrate';
- $shippingPrice = '10.00';
- $quoteMock = $this->createPartialMock(
- \Magento\Quote\Model\Cart\Totals::class,
+ $objectManager = new ObjectManager($this);
+ $this->quoteTotalsMock = $this->createPartialMock(
+ QuoteTotals::class,
[
- 'getStore',
- 'getShippingAddress',
- 'getIsMultiShipping'
+ 'getStore',
+ 'getShippingAddress',
+ 'getIsMultiShipping'
]
);
- $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId)->willReturn($quoteMock);
- $quoteMock->expects($this->once())->method('getIsMultiShipping')->willReturn(true);
- $shippingAddressMock = $this->createPartialMock(
- \Magento\Quote\Model\Quote\Address::class,
+ $this->shippingAddressMock = $this->createPartialMock(
+ QuoteAddress::class,
[
- 'getShippingMethod',
- 'getShippingRateByCode',
- 'getShippingAmount'
+ 'getShippingMethod',
+ 'getShippingRateByCode',
+ 'getShippingAmount'
]
);
- $quoteMock->expects($this->any())->method('getShippingAddress')->willReturn($shippingAddressMock);
-
- $shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod);
- $shippingAddressMock->expects($this->any())->method('getShippingAmount')->willReturn($shippingPrice);
- $shippingRateMock = $this->createPartialMock(
- \Magento\Quote\Model\Quote\Address\Rate::class,
+ $this->shippingRateMock = $this->createPartialMock(
+ QuoteAddressRate::class,
[
- 'getPrice'
+ 'getPrice'
]
);
- $shippingAddressMock->expects($this->once())->method('getShippingRateByCode')->willReturn($shippingRateMock);
-
- $shippingRateMock->expects($this->once())->method('getPrice')->willReturn($shippingPrice);
-
- $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class)
+ $this->storeMock = $this->getMockBuilder(Store::class)
->disableOriginalConstructor()
->getMock();
- $quoteMock->expects($this->any())->method('getStore')->willReturn($storeMock);
- $storeMock->expects($this->any())->method('getBaseCurrency')->willReturnSelf();
+ $this->quoteRepositoryMock = $this->createMock(CartRepositoryInterface::class);
+ $this->quoteTotalRepositoryMock = $this->createMock(CartTotalRepository::class);
+ $this->modelRepository = $objectManager->getObject(CartTotalRepositoryPlugin::class, [
+ 'quoteRepository' => $this->quoteRepositoryMock
+ ]);
+ }
+
+ /**
+ * Test quoteTotal from cartRepository after get($cartId) function is called
+ */
+ public function testAfterGetQuoteTotalAddedShippingPrice()
+ {
+ $this->quoteRepositoryMock->expects($this->once())
+ ->method('getActive')
+ ->with(self::STUB_CART_ID)
+ ->willReturn($this->quoteTotalsMock);
+ $this->quoteTotalsMock->expects($this->once())
+ ->method('getIsMultiShipping')
+ ->willReturn(true);
+ $this->quoteTotalsMock->expects($this->any())
+ ->method('getShippingAddress')
+ ->willReturn($this->shippingAddressMock);
+
+ $this->shippingAddressMock->expects($this->once())
+ ->method('getShippingMethod')
+ ->willReturn(self::STUB_SHIPPING_METHOD);
+ $this->shippingAddressMock->expects($this->any())
+ ->method('getShippingAmount')
+ ->willReturn(self::STUB_SHIPPING_PRICE);
+
+ $this->shippingAddressMock->expects($this->once())
+ ->method('getShippingRateByCode')
+ ->willReturn($this->shippingRateMock);
+
+ $this->shippingRateMock->expects($this->once())
+ ->method('getPrice')
+ ->willReturn(self::STUB_SHIPPING_PRICE);
+
+ $this->quoteTotalsMock->expects($this->any())
+ ->method('getStore')
+ ->willReturn($this->storeMock);
+ $this->storeMock->expects($this->any())
+ ->method('getBaseCurrency')
+ ->willReturnSelf();
$this->modelRepository->afterGet(
- $this->createMock(\Magento\Quote\Model\Cart\CartTotalRepository::class),
- $quoteMock,
- $cartId
+ $this->quoteTotalRepositoryMock,
+ $this->quoteTotalsMock,
+ self::STUB_CART_ID
);
}
}
diff --git a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml
index fee3cb790a522..449f5feeafd9c 100644
--- a/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml
+++ b/app/code/Magento/Multishipping/view/frontend/layout/multishipping_checkout_customer_address.xml
@@ -13,6 +13,7 @@
Magento\Customer\Block\DataProviders\AddressAttributeData
Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData
+ Magento\Customer\ViewModel\Address
diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php b/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php
index 4d5165db68736..d05657c727192 100644
--- a/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php
+++ b/app/code/Magento/Newsletter/Block/Adminhtml/Subscriber.php
@@ -11,13 +11,18 @@
*/
namespace Magento\Newsletter\Block\Adminhtml;
+use Magento\Backend\Block\Template;
+use Magento\Backend\Block\Template\Context;
use Magento\Newsletter\Model\ResourceModel\Queue\Collection;
+use Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory;
/**
+ * Newsletter Subscriber block
+ *
* @api
* @since 100.0.2
*/
-class Subscriber extends \Magento\Backend\Block\Template
+class Subscriber extends Template
{
/**
* Queue collection
@@ -32,34 +37,24 @@ class Subscriber extends \Magento\Backend\Block\Template
protected $_template = 'Magento_Newsletter::subscriber/list.phtml';
/**
- * @var \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory
+ * @var CollectionFactory
*/
protected $_collectionFactory;
/**
- * @param \Magento\Backend\Block\Template\Context $context
- * @param \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory
+ * @param Context $context
+ * @param CollectionFactory $collectionFactory
* @param array $data
*/
public function __construct(
- \Magento\Backend\Block\Template\Context $context,
- \Magento\Newsletter\Model\ResourceModel\Queue\CollectionFactory $collectionFactory,
+ Context $context,
+ CollectionFactory $collectionFactory,
array $data = []
) {
$this->_collectionFactory = $collectionFactory;
parent::__construct($context, $data);
}
- /**
- * Prepares block to render
- *
- * @return $this
- */
- protected function _beforeToHtml()
- {
- return parent::_beforeToHtml();
- }
-
/**
* Return queue collection with loaded neversent queues
*
@@ -68,7 +63,7 @@ protected function _beforeToHtml()
public function getQueueCollection()
{
if ($this->_queueCollection === null) {
- /** @var $this->_queueCollection \Magento\Newsletter\Model\ResourceModel\Queue\Collection */
+ /** @var $this->_queueCollection Collection */
$this->_queueCollection = $this
->_collectionFactory
->create()
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorActionGroup.xml
new file mode 100644
index 0000000000000..a2f35b9c5fca8
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Assert that item in "Item Ordered" grid has an error/notice
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorNotVisibleActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorNotVisibleActionGroup.xml
new file mode 100644
index 0000000000000..83bac652d7dff
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AssertAdminItemOrderedErrorNotVisibleActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Assert that item in "Item Ordered" grid does not have an error/notice
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml
index e3417e7c662b9..4437f6e6775f2 100644
--- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml
+++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml
@@ -18,5 +18,6 @@
+
diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml
new file mode 100644
index 0000000000000..ca74eca88308a
--- /dev/null
+++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminAddSelectedProductToOrderTest.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml
index fbad9ab271bda..df9b724783372 100644
--- a/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml
+++ b/app/code/Magento/Widget/Test/Mftf/Test/ProductsListWidgetTest.xml
@@ -6,8 +6,7 @@
*/
-->
-
+
@@ -61,8 +60,10 @@
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php
index f9636890e61f6..4b581a5cbf5d6 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($bundleProduct->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Checkbox Options')
->setSku('bundle-product-checkbox-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php
index 453b531f75b2d..1cc9ede5d71e4 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php
@@ -33,7 +33,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($bundleProduct->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Checkbox Required Option')
->setSku('bundle-product-checkbox-required-option')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php
index 9b84d1236c5c9..5bb6fe6973287 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Checkbox Required Options')
->setSku('bundle-product-checkbox-required-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php
index 06f6473802ee2..62c80abc6415f 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Dropdown options')
->setSku('bundle-product-dropdown-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php
index 1789f472f968d..4093c9ff057e7 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Dropdown Required options')
->setSku('bundle-product-dropdown-required-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php
index a5667b89f8bf4..29b7710c47040 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Multiselect Options')
->setSku('bundle-product-multiselect-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php
index 7789045f6f7ef..0cde1e65c9e54 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php
@@ -33,7 +33,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Multiselect Required Option')
->setSku('bundle-product-multiselect-required-option')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php
index 65bb49f3b6122..f18494d08215c 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Multiselect Required Options')
->setSku('bundle-product-multiselect-required-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php
index def31b48b2172..23a5713e46eb0 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Radio Options')
->setSku('bundle-product-radio-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php
index c659387e09dcc..1472986023e21 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php
@@ -33,7 +33,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Radio Required Option')
->setSku('bundle-product-radio-required-option')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php
index ec28bf556b69c..cb703902a0b7a 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php
@@ -34,7 +34,7 @@
$bundleProduct->setTypeId(Type::TYPE_BUNDLE)
->setAttributeSetId($product->getDefaultAttributeSetId())
->setWebsiteIds([$baseWebsiteId])
- ->setName('Bundle Product')
+ ->setName('Bundle Product Radio Required Options')
->setSku('bundle-product-radio-required-options')
->setVisibility(Visibility::VISIBILITY_BOTH)
->setStatus(Status::STATUS_ENABLED)
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php
new file mode 100644
index 0000000000000..9a6cae37fbe25
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php
@@ -0,0 +1,23 @@
+create(CategoryLinkManagementInterface::class);
+/** @var DefaultCategory $categoryHelper */
+$categoryHelper = $objectManager->get(DefaultCategory::class);
+$categoryLinkManagement->assignProductToCategories('bundle-product', [2, $category->getId()]);
+$categoryLinkManagement->assignProductToCategories(
+ 'bundle-product-dropdown-options',
+ [$categoryHelper->getId(), $category->getId()]
+);
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category_rollback.php
new file mode 100644
index 0000000000000..58eb8cacde815
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category_rollback.php
@@ -0,0 +1,10 @@
+productRepository->get('simple');
$product2 = $this->productRepository->get('simple2');
$product1Id = $product1->getId();
$product2Id = $product2->getId();
$productsData = [
- $product1Id => [
+ $productScope . '-' . $scopeId . '-' . $product1Id => [
'added_at' => '1576582660',
'product_id' => $product1Id,
],
- $product2Id => [
+ $productScope . '-' . $scopeId . '-' . $product2Id => [
'added_at' => '1576587153',
'product_id' => $product2Id,
],
@@ -71,8 +75,9 @@ public function testSyncActions(): void
);
foreach ($synchronizedCollection as $item) {
- $this->assertArrayHasKey($item->getProductId(), $productsData);
- $this->assertEquals($productsData[$item->getProductId()]['added_at'], $item->getAddedAt());
+ $productScopeId = $productScope . '-' . $scopeId . '-' . $item->getProductId();
+ $this->assertArrayHasKey($productScopeId, $productsData);
+ $this->assertEquals($productsData[$productScopeId]['added_at'], $item->getAddedAt());
}
}
@@ -81,6 +86,8 @@ public function testSyncActions(): void
* @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
*
* @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function testSyncActionsWithoutActionsType(): void
{
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 302534679d073..93684b1e7a070 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -2416,6 +2416,16 @@ public function validateRowDataProvider()
'behavior' => Import::BEHAVIOR_REPLACE,
'expectedResult' => true,
],
+ [
+ 'row' => ['sku' => 'sku with whitespace ',
+ 'name' => 'Test',
+ 'product_type' => 'simple',
+ '_attribute_set' => 'Default',
+ 'price' => 10.20,
+ ],
+ 'behavior' => Import::BEHAVIOR_ADD_UPDATE,
+ 'expectedResult' => false,
+ ],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php
index f11083dd2ba91..8bcd0001b8119 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogWidget/Block/Product/ProductListTest.php
@@ -256,4 +256,80 @@ public function testCreateAnchorCollection()
"Anchor root category does not contain products of it's children."
);
}
+
+ /**
+ * Test that price rule condition works correctly
+ *
+ * @magentoDbIsolation disabled
+ * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+ * @param string $operator
+ * @param int $value
+ * @param array $matches
+ * @dataProvider priceFilterDataProvider
+ */
+ public function testPriceFilter(string $operator, int $value, array $matches)
+ {
+ $encodedConditions = '^[`1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Combine`,
+ `aggregator`:`all`,`value`:`1`,`new_child`:``^],
+ `1--1`:^[`type`:`Magento||CatalogWidget||Model||Rule||Condition||Product`,
+ `attribute`:`price`,
+ `operator`:`' . $operator . '`,`value`:`' . $value . '`^]^]';
+
+ $this->block->setData('conditions_encoded', $encodedConditions);
+
+ $productCollection = $this->block->createCollection();
+ $productCollection->load();
+ $skus = array_map(
+ function ($item) {
+ return $item['sku'];
+ },
+ $productCollection->getItems()
+ );
+ $this->assertEquals($matches, $skus, '', 0.0, 10, true);
+ }
+
+ public function priceFilterDataProvider(): array
+ {
+ return [
+ [
+ '>',
+ 10,
+ [
+ 'simple1001',
+ ]
+ ],
+ [
+ '>=',
+ 10,
+ [
+ 'simple1000',
+ 'simple1001',
+ 'configurable',
+ ]
+ ],
+ [
+ '<',
+ 10,
+ []
+ ],
+ [
+ '<',
+ 20,
+ [
+ 'simple1000',
+ 'configurable',
+ ]
+ ],
+ [
+ '<=',
+ 20,
+ [
+ 'simple1000',
+ 'simple1001',
+ 'configurable',
+ ]
+ ],
+ ];
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders/RenderOrdersTabTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders/RenderOrdersTabTest.php
new file mode 100644
index 0000000000000..1d18814487bf8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders/RenderOrdersTabTest.php
@@ -0,0 +1,433 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->store = $this->objectManager->get(Store::class);
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->currency = $this->objectManager->get(CurrencyInterface::class);
+ $this->timezone = $this->objectManager->get(TimezoneInterface::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->scopeConfig = $this->objectManager->get(ScopeConfigInterface::class);
+ parent::setUp();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister(RegistryConstants::CURRENT_CUSTOMER_ID);
+ parent::tearDown();
+ }
+
+ /**
+ * Assert that customer orders tab renders with message "We couldn't find any records."
+ * when customer doesn't have any orders.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testRenderBlockWithoutOrders(): void
+ {
+ $this->processCheckOrdersGridByCustomerId(1, 0);
+ }
+
+ /**
+ * Assert that customer orders tab renders without message "We couldn't find any records."
+ * and contains rendered order item when customer has one order.
+ *
+ * @magentoDataFixture Magento/Sales/_files/order_with_customer.php
+ *
+ * @return void
+ */
+ public function testRenderBlockWithOneOrder(): void
+ {
+ $this->processCheckOrdersGridByCustomerId(1, 1);
+ }
+
+ /**
+ * Assert that customer orders tab renders without message "We couldn't find any records."
+ * and contains rendered orders items when customer has few orders.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Sales/_files/orders_with_customer.php
+ *
+ * @return void
+ */
+ public function testRenderBlockWithFewOrders(): void
+ {
+ $this->processCheckOrdersGridByCustomerId(1, 5);
+ }
+
+ /**
+ * Render orders grid and assert that all data rendered as expected.
+ *
+ * @param int $customerId
+ * @param int $expectedOrderCount
+ * @return void
+ */
+ private function processCheckOrdersGridByCustomerId(int $customerId, int $expectedOrderCount): void
+ {
+ $this->registerCustomerId($customerId);
+ $ordersGridHtml = $this->getOrdersGridHtml();
+ $orderItemsData = $this->getOrderGridItemsData();
+ $this->assertOrdersCount($expectedOrderCount, $ordersGridHtml);
+ $this->assertIsEmptyGridMessageArrears($ordersGridHtml, $expectedOrderCount === 0);
+ $this->checkOrderItemsFields($orderItemsData, $ordersGridHtml);
+ }
+
+ /**
+ * Add customer id to registry.
+ *
+ * @param int $customerId
+ * @return void
+ */
+ private function registerCustomerId(int $customerId): void
+ {
+ $this->registry->unregister(RegistryConstants::CURRENT_CUSTOMER_ID);
+ $this->registry->register(RegistryConstants::CURRENT_CUSTOMER_ID, $customerId);
+ }
+
+ /**
+ * Render customer orders tab.
+ *
+ * @return string
+ */
+ private function getOrdersGridHtml(): string
+ {
+ $this->ordersGridBlock = $this->layout->createBlock(Orders::class);
+
+ return $this->ordersGridBlock->toHtml();
+ }
+
+ /**
+ * Check that rendered html contains all provided order items.
+ *
+ * @param array $orderItemsData
+ * @param string $html
+ * @return void
+ */
+ private function checkOrderItemsFields(array $orderItemsData, string $html): void
+ {
+ foreach ($orderItemsData as $itemOrder => $orderItemData) {
+ $this->assertViewOrderUrl($itemOrder, $orderItemData['order_id'], $html);
+ $this->assertReorderUrl($itemOrder, $orderItemData['order_id'], $html);
+ $this->assertStoreViewLabels($itemOrder, $orderItemData['store_view_labels'], $html);
+ unset($orderItemData['order_id'], $orderItemData['store_view_labels']);
+ $this->assertColumnsValues($itemOrder, $orderItemData, $html);
+ }
+ }
+
+ /**
+ * Assert that field store_id contains all provided store codes.
+ *
+ * @param int $itemOrder
+ * @param array $storeViewLabels
+ * @param string $html
+ * @return void
+ */
+ private function assertStoreViewLabels(int $itemOrder, array $storeViewLabels, string $html): void
+ {
+ if (empty($storeViewLabels)) {
+ return;
+ }
+
+ $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [
+ "//tr[{$itemOrder}]",
+ "//td[contains(@class, 'store_id') and %s]",
+ ]);
+ $storeLabelsPaths = [];
+ foreach ($storeViewLabels as $labelIndex => $storeViewLabel) {
+ $storeLabelsPaths[] = "contains(text()[{$labelIndex}], '{$storeViewLabel}')";
+ }
+ $checkStoreViewsXPath = sprintf(implode('', $elementPaths), implode(' and ', $storeLabelsPaths));
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($checkStoreViewsXPath, $html),
+ sprintf("Some store view label not found. Labels: %s. Html: %s", implode(', ', $storeViewLabels), $html)
+ );
+ }
+
+ /**
+ * Assert that columns values as expected.
+ *
+ * @param int $itemOrder
+ * @param array $columnsData
+ * @param string $html
+ * @return void
+ */
+ private function assertColumnsValues(int $itemOrder, array $columnsData, string $html): void
+ {
+ $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [
+ "//tr[{$itemOrder}]",
+ "//td[contains(@class, '%s') and contains(text(), '%s')]",
+ ]);
+ $elementXPathTemplate = implode('', $elementPaths);
+ foreach ($columnsData as $columnName => $columnValue) {
+ $preparedXPath = sprintf($elementXPathTemplate, $columnName, $columnValue);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($preparedXPath, $html),
+ sprintf("Column %s doesn't have value %s. Html: %s", $columnName, $columnValue, $html)
+ );
+ }
+ }
+
+ /**
+ * Assert that rendered html contains URL to reorder by order id.
+ *
+ * @param int $itemOrder
+ * @param int $orderId
+ * @param string $html
+ * @return void
+ */
+ private function assertReorderUrl(int $itemOrder, int $orderId, string $html): void
+ {
+ $urlLabel = (string)__('Reorder');
+ $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [
+ "//tr[{$itemOrder}]",
+ "//td[contains(@class, 'action')]",
+ "//a[contains(@href, 'sales/order_create/reorder/order_id/$orderId') and contains(text(), '{$urlLabel}')]",
+ ]);
+ $reorderUrlXPath = implode('', $elementPaths);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($reorderUrlXPath, $html),
+ sprintf('Reorder URL is not as expected. Html: %s', $html)
+ );
+ }
+
+ /**
+ * Assert that rendered html contains URL to order view by order id.
+ *
+ * @param int $itemOrder
+ * @param int $orderId
+ * @param string $html
+ * @return void
+ */
+ private function assertViewOrderUrl(int $itemOrder, int $orderId, string $html): void
+ {
+ $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [
+ "//tr[{$itemOrder}][contains(@title, 'sales/order/view/order_id/{$orderId}')]",
+ ]);
+ $viewOrderUrlXPath = implode('', $elementPaths);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($viewOrderUrlXPath, $html),
+ sprintf('URL to view order is not as expected. Html: %s', $html)
+ );
+ }
+
+ /**
+ * Assert that provided orders count and count in html are equals.
+ *
+ * @param int $expectedOrdersCount
+ * @param string $html
+ * @return void
+ */
+ private function assertOrdersCount(int $expectedOrdersCount, string $html): void
+ {
+ $elementPaths = [
+ "//div[contains(@data-grid-id, 'customer_orders_grid')]",
+ "//div[contains(@class, 'grid-header-row')]",
+ "//div[contains(@class, 'control-support-text')]",
+ sprintf("//span[contains(@id, 'grid-total-count') and contains(text(), '%s')]", $expectedOrdersCount),
+ ];
+ $ordersCountXPath = implode('', $elementPaths);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($ordersCountXPath, $html),
+ sprintf('Provided count and count in html are not equals. Html: %s', $html)
+ );
+ }
+
+ /**
+ * Assert that grid contains or not contains message "We couldn't find any records.".
+ *
+ * @param string $html
+ * @param bool $isMessageAppears
+ * @return void
+ */
+ private function assertIsEmptyGridMessageArrears(string $html, bool $isMessageAppears = false): void
+ {
+ $gridText = (string)__("We couldn't find any records.");
+ $elementPaths = array_merge(self::PATHS_TO_TABLE_BODY, [
+ "//tr[contains(@class, 'tr-no-data')]",
+ "//td[contains(@class, 'empty-text') and contains(text(), \"{$gridText}\")]",
+ ]);
+ $emptyTextXPath = implode('', $elementPaths);
+ $this->assertEquals(
+ $isMessageAppears ? 1 : 0,
+ Xpath::getElementsCountForXpath($emptyTextXPath, $html),
+ sprintf('Message "We couldn\'t find any records." not found in html. Html: %s', $html)
+ );
+ }
+
+ /**
+ * Build array with rendered orders for check that all contained data appears.
+ *
+ * @return array
+ */
+ private function getOrderGridItemsData(): array
+ {
+ $orders = [];
+ $orderNumber = 1;
+ /** @var Document $order */
+ foreach ($this->ordersGridBlock->getCollection() as $order) {
+ $orderGrandTotal = $this->prepareGrandTotal(
+ $order->getData('grand_total'),
+ $order->getData('order_currency_code')
+ );
+ $orders[$orderNumber] = [
+ 'order_id' => (int)$order->getData(OrderInterface::ENTITY_ID),
+ 'increment_id' => $order->getData(OrderInterface::INCREMENT_ID),
+ 'created_at' => $this->prepareCreatedAtDate($order->getData(OrderInterface::CREATED_AT)),
+ 'billing_name' => $order->getData('billing_name'),
+ 'shipping_name' => $order->getData('shipping_name'),
+ 'grand_total' => $orderGrandTotal,
+ 'store_view_labels' => $this->prepareStoreViewLabels([$order->getData(OrderInterface::STORE_ID)]),
+ ];
+ $orderNumber++;
+ }
+
+ return $orders;
+ }
+
+ /**
+ * Normalize created at date.
+ *
+ * @param string $createdAt
+ * @return string
+ */
+ private function prepareCreatedAtDate(string $createdAt): string
+ {
+ $date = new \DateTime($createdAt);
+
+ return $this->timezone->formatDateTime($date, \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM);
+ }
+
+ /**
+ * Normalize grand total.
+ *
+ * @param string $grandTotal
+ * @param string|null $orderCurrencyCode
+ * @return string
+ */
+ private function prepareGrandTotal(string $grandTotal, ?string $orderCurrencyCode = null): string
+ {
+ $resultGrandTotal = sprintf("%f", (float)$grandTotal * 1.0);
+ $orderCurrencyCode = $orderCurrencyCode ?:
+ $this->scopeConfig->getValue(Currency::XML_PATH_CURRENCY_BASE, 'default');
+
+ return $this->currency->getCurrency($orderCurrencyCode)->toCurrency($resultGrandTotal);
+ }
+
+ /**
+ * Normalize store ids.
+ *
+ * @param array $orderStoreIds
+ * @return array
+ */
+ private function prepareStoreViewLabels(array $orderStoreIds): array
+ {
+ $result = [];
+ $storeStructure = $this->store->getStoresStructure(false, $orderStoreIds);
+ $textIndex = 0;
+ foreach ($storeStructure as $website) {
+ $textIndex++;
+ foreach ($website['children'] as $group) {
+ $textIndex++;
+ foreach ($group['children'] as $store) {
+ $textIndex++;
+ $result[$textIndex] = $store['label'];
+ }
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/NewsletterTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/NewsletterTest.php
new file mode 100644
index 0000000000000..8778a00e81499
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Block/NewsletterTest.php
@@ -0,0 +1,89 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Newsletter::class);
+ $this->customerSession = $this->objectManager->get(Session::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testSubscriptionCheckbox(): void
+ {
+ $this->customerSession->loginById(1);
+ $html = $this->block->toHtml();
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::LABEL_XPATH, $html),
+ 'Subscription label is not present on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::CHECKBOX_XPATH, $html),
+ 'Subscription checkbox is not present on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::CHECKBOX_TITLE_XPATH, $html),
+ 'Subscription checkbox label is not present on the page'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::SAVE_BUTTON_XPATH, $html),
+ 'Subscription save button is not present on the page'
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/Delete/DeleteAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/Delete/DeleteAddressTest.php
new file mode 100644
index 0000000000000..86a443d7aa3e1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/Delete/DeleteAddressTest.php
@@ -0,0 +1,217 @@
+escaper = $this->_objectManager->get(Escaper::class);
+ $this->customerSession = $this->_objectManager->get(Session::class);
+ $this->addressRepository = $this->_objectManager->get(AddressRepositoryInterface::class);
+ $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+ }
+
+ /**
+ * Assert that customer address deleted successfully.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ *
+ * @return void
+ */
+ public function testSuccessDeleteExistCustomerAddress(): void
+ {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $customerAddresses = $customer->getAddresses() ?? [];
+ $this->assertCount(1, $customerAddresses);
+ /** @var AddressInterface $currentCustomerAddress */
+ $currentCustomerAddress = reset($customerAddresses);
+ $this->customerSession->setCustomerId($customer->getId());
+ $this->performAddressDeleteRequest((int)$currentCustomerAddress->getId());
+ $this->checkRequestPerformedSuccessfully();
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertCount(0, $customer->getAddresses() ?? []);
+ try {
+ $this->addressRepository->getById((int)$currentCustomerAddress->getId());
+ $this->fail('Customer address is not deleted.');
+ } catch (LocalizedException $e) {
+ //Do nothing, this block mean that address deleted successfully from DB.
+ }
+ }
+
+ /**
+ * Check that customer address will not be deleted if we don't pass address ID parameter.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ *
+ * @return void
+ */
+ public function testDeleteWithoutParam(): void
+ {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $customerAddresses = $customer->getAddresses() ?? [];
+ $this->assertCount(1, $customerAddresses);
+ /** @var AddressInterface $currentCustomerAddress */
+ $currentCustomerAddress = reset($customerAddresses);
+ $this->customerSession->setCustomerId($customer->getId());
+ $this->performAddressDeleteRequest();
+ $this->assertRedirect($this->stringContains('customer/address/index'));
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertCount(1, $customer->getAddresses() ?? []);
+ $this->checkAddressWasntDeleted((int)$currentCustomerAddress->getId());
+ }
+
+ /**
+ * Check that customer address will not be deleted if customer id in address and in session are not equals.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address.php
+ * @magentoDataFixture Magento/Customer/_files/customer_with_uk_address.php
+ *
+ * @return void
+ */
+ public function testDeleteDifferentCustomerAddress(): void
+ {
+ $firstCustomer = $this->customerRepository->get('customer@example.com');
+ $customerAddresses = $firstCustomer->getAddresses() ?? [];
+ $this->assertCount(1, $customerAddresses);
+ /** @var AddressInterface $currentCustomerAddress */
+ $currentCustomerAddress = reset($customerAddresses);
+ $this->customerSession->setCustomerId('1');
+ $secondCustomer = $this->customerRepository->get('customer_uk_address@test.com');
+ $secondCustomerAddresses = $secondCustomer->getAddresses() ?? [];
+ /** @var AddressInterface $secondCustomerAddress */
+ $secondCustomerAddress = reset($secondCustomerAddresses);
+ $this->performAddressDeleteRequest((int)$secondCustomerAddress->getId());
+ $this->checkRequestPerformedWithError(true);
+ $firstCustomer = $this->customerRepository->get('customer@example.com');
+ $this->assertCount(1, $firstCustomer->getAddresses() ?? []);
+ $this->checkAddressWasntDeleted((int)$currentCustomerAddress->getId());
+ }
+
+ /**
+ * Check that error message appear if we try to delete non-exits address.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testDeleteNonExistAddress(): void
+ {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->customerSession->setCustomerId($customer->getId());
+ $this->performAddressDeleteRequest(999);
+ $this->checkRequestPerformedWithError();
+ }
+
+ /**
+ * Perform delete request by provided address id.
+ *
+ * @param int|null $processAddressId
+ * @return void
+ */
+ private function performAddressDeleteRequest(?int $processAddressId = null): void
+ {
+ $this->getRequest()->setMethod(Http::METHOD_POST);
+ if (null !== $processAddressId) {
+ $this->getRequest()->setPostValue(['id' => $processAddressId]);
+ }
+ $this->dispatch('customer/address/delete');
+ }
+
+ /**
+ * Check that delete address request performed successfully
+ * (proper success message and redirect to customer/address/index are appear).
+ *
+ * @return void
+ */
+ private function checkRequestPerformedSuccessfully(): void
+ {
+ $this->assertRedirect($this->stringContains('customer/address/index'));
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You deleted the address.')]),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * Check that delete address request performed with error.
+ * (proper error messages and redirect to customer/address/edit are appear).
+ *
+ * @param bool $isNeedEscapeMessage
+ * @return void
+ */
+ private function checkRequestPerformedWithError(bool $isNeedEscapeMessage = false): void
+ {
+ $message = (string)__("We can't delete the address right now.");
+ if ($isNeedEscapeMessage) {
+ $message = $this->escaper->escapeHtml($message);
+ }
+ $this->assertSessionMessages($this->contains($message), MessageInterface::TYPE_ERROR);
+ }
+
+ /**
+ * Assert that customer address wasn't deleted.
+ *
+ * @param int $addressId
+ * @return void
+ */
+ private function checkAddressWasntDeleted(int $addressId): void
+ {
+ try {
+ $this->addressRepository->getById($addressId);
+ } catch (LocalizedException $e) {
+ $this->fail('Expects that customer address will not be deleted.');
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/CreateAddressTest.php
new file mode 100644
index 0000000000000..1e152008043a7
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/CreateAddressTest.php
@@ -0,0 +1,391 @@
+ '+380505282812',
+ AddressInterface::POSTCODE => 75477,
+ AddressInterface::COUNTRY_ID => 'US',
+ 'custom_region_name' => 'Alabama',
+ AddressInterface::CITY => 'CityM',
+ AddressInterface::STREET => [
+ 'Green str, 67',
+ ],
+ AddressInterface::FIRSTNAME => 'John',
+ AddressInterface::LASTNAME => 'Smith',
+ ];
+
+ /**
+ * @var Escaper
+ */
+ private $escaper;
+
+ /**
+ * @var Session
+ */
+ private $customerSession;
+
+ /**
+ * @var CustomerRegistry
+ */
+ private $customerRegistry;
+
+ /**
+ * @var GetRegionIdByName
+ */
+ private $getRegionIdByName;
+
+ /**
+ * @var CustomerRepositoryInterface
+ */
+ private $customerRepository;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ $this->customerSession = $this->_objectManager->get(Session::class);
+ $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class);
+ $this->getRegionIdByName = $this->_objectManager->get(GetRegionIdByName::class);
+ $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+ $this->customerSession->setCustomerId('5');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerSession->setCustomerId(null);
+ $this->customerRegistry->removeByEmail('customer5@example.com');
+ parent::tearDown();
+ }
+
+ /**
+ * Assert that default or non-default customer address successfully created via controller on frontend.
+ *
+ * @dataProvider postDataForSuccessCreateDefaultAddressDataProvider
+ *
+ * @param array $postData
+ * @param bool $isShippingDefault
+ * @param bool $isBillingDefault
+ * @return void
+ */
+ public function testAddressSuccessfullyCreatedAsDefaultForCustomer(
+ array $postData,
+ bool $isShippingDefault,
+ bool $isBillingDefault
+ ): void {
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->assertNull($customer->getDefaultShipping(), 'Customer already have default shipping address');
+ $this->assertNull($customer->getDefaultBilling(), 'Customer already have default billing address');
+ $this->assertEmpty($customer->getAddresses(), 'Customer already has address');
+ $this->performRequestWithData($postData);
+ $this->checkRequestPerformedSuccessfully();
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $customerAddresses = $customer->getAddresses();
+ $this->assertCount(1, $customerAddresses);
+ /** @var AddressInterface $address */
+ $address = reset($customerAddresses);
+ $expectedShippingId = $isShippingDefault ? $address->getId() : null;
+ $expectedBillingId = $isBillingDefault ? $address->getId() : null;
+ $this->assertEquals($expectedShippingId, $customer->getDefaultShipping());
+ $this->assertEquals($expectedBillingId, $customer->getDefaultBilling());
+ }
+
+ /**
+ * Data provider which contain proper POST data for create default or non-default customer address.
+ *
+ * @return array
+ */
+ public function postDataForSuccessCreateDefaultAddressDataProvider(): array
+ {
+ return [
+ 'any_addresses_are_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_SHIPPING => 0, AddressInterface::DEFAULT_BILLING => 0]
+ ),
+ false,
+ false,
+ ],
+ 'shipping_address_is_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_SHIPPING => 1, AddressInterface::DEFAULT_BILLING => 0]
+ ),
+ true,
+ false,
+ ],
+ 'billing_address_is_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_SHIPPING => 0, AddressInterface::DEFAULT_BILLING => 1]
+ ),
+ false,
+ true,
+ ],
+ 'all_addresses_are_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_SHIPPING => 1, AddressInterface::DEFAULT_BILLING => 1]
+ ),
+ true,
+ true,
+ ],
+ ];
+ }
+
+ /**
+ * Assert that customer address successfully created via controller on frontend.
+ *
+ * @dataProvider postDataForSuccessCreateAddressDataProvider
+ *
+ * @param array $postData
+ * @param array $expectedData
+ * @return void
+ */
+ public function testAddressSuccessfullyCreatedForCustomer(array $postData, array $expectedData): void
+ {
+ if (isset($expectedData['custom_region_name'])) {
+ $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute(
+ $expectedData['custom_region_name'],
+ $expectedData[AddressInterface::COUNTRY_ID]
+ );
+ unset($expectedData['custom_region_name']);
+ }
+ $this->performRequestWithData($postData);
+ $this->checkRequestPerformedSuccessfully();
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $customerAddresses = $customer->getAddresses();
+ $this->assertCount(1, $customerAddresses);
+ /** @var AddressInterface $address */
+ $address = reset($customerAddresses);
+ $createdAddressData = $address->__toArray();
+ foreach ($expectedData as $fieldCode => $expectedValue) {
+ $this->assertArrayHasKey($fieldCode, $createdAddressData, "Field $fieldCode wasn't found.");
+ $this->assertEquals($expectedValue, $createdAddressData[$fieldCode]);
+ }
+ }
+
+ /**
+ * Data provider which contain proper POST data for create customer address.
+ *
+ * @return array
+ */
+ public function postDataForSuccessCreateAddressDataProvider(): array
+ {
+ return [
+ 'required_fields_valid_data' => [
+ self::STATIC_POST_ADDRESS_DATA,
+ [
+ AddressInterface::TELEPHONE => '+380505282812',
+ AddressInterface::COUNTRY_ID => 'US',
+ AddressInterface::POSTCODE => 75477,
+ 'custom_region_name' => 'Alabama',
+ AddressInterface::FIRSTNAME => 'John',
+ AddressInterface::LASTNAME => 'Smith',
+ AddressInterface::STREET => ['Green str, 67'],
+ AddressInterface::CITY => 'CityM',
+ ],
+ ],
+ 'required_field_empty_postcode_for_uk' => [
+ array_replace(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB']
+ ),
+ [
+ AddressInterface::COUNTRY_ID => 'GB',
+ AddressInterface::POSTCODE => null,
+ ],
+ ],
+ 'required_field_empty_region_id_for_ua' => [
+ array_replace(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA']
+ ),
+ [
+ AddressInterface::COUNTRY_ID => 'UA',
+ AddressInterface::REGION => [
+ RegionInterface::REGION => null,
+ RegionInterface::REGION_CODE => null,
+ RegionInterface::REGION_ID => 0,
+ ],
+ ],
+ ],
+ 'required_field_street_as_array' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]),
+ [AddressInterface::STREET => ['Green str, 67']],
+ ],
+ 'field_company_name' => [
+ array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']),
+ [AddressInterface::COMPANY => 'My company'],
+ ],
+ 'field_vat_number' => [
+ array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']),
+ [AddressInterface::VAT_ID => 'My VAT number'],
+ ],
+ ];
+ }
+
+ /**
+ * Assert that customer address wasn't created via controller on frontend
+ * when POST data broken.
+ *
+ * @dataProvider postDataForCreateAddressWithErrorDataProvider
+ *
+ * @param array $postData
+ * @param array $expectedSessionMessages
+ * @return void
+ */
+ public function testAddressWasntCreatedForCustomer(array $postData, array $expectedSessionMessages): void
+ {
+ $this->performRequestWithData($postData);
+ $this->checkRequestPerformedWithInputValidationErrors($expectedSessionMessages);
+ }
+
+ /**
+ * Data provider which contain broken POST data for create customer address with error.
+ *
+ * @return array
+ */
+ public function postDataForCreateAddressWithErrorDataProvider(): array
+ {
+ return [
+ 'empty_post_data' => [
+ [],
+ [
+ 'One or more input exceptions have occurred.',
+ '"firstname" is required. Enter and try again.',
+ '"lastname" is required. Enter and try again.',
+ '"street" is required. Enter and try again.',
+ '"city" is required. Enter and try again.',
+ '"telephone" is required. Enter and try again.',
+ '"postcode" is required. Enter and try again.',
+ '"countryId" is required. Enter and try again.',
+ ]
+ ],
+ 'required_field_empty_telephone' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']),
+ ['"telephone" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_postcode_for_us' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::POSTCODE => '']),
+ ['"postcode" is required. Enter and try again.'],
+ ],
+// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031
+// 'required_field_empty_region_id_for_us' => [
+// array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::REGION_ID => '']),
+// ['"regionId" is required. Enter and try again.'],
+// ],
+ 'required_field_empty_firstname' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']),
+ ['"firstname" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_lastname' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::LASTNAME => '']),
+ ['"lastname" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_street_as_string' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => '']),
+ ['"street" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_street_as_array' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => []]),
+ ['"street" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_city' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::CITY => '']),
+ ['"city" is required. Enter and try again.'],
+ ],
+ ];
+ }
+
+ /**
+ * Perform request with provided POST data.
+ *
+ * @param array $postData
+ * @return void
+ */
+ private function performRequestWithData(array $postData): void
+ {
+ if (isset($postData['custom_region_name'])) {
+ $postData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute(
+ $postData['custom_region_name'],
+ $postData[AddressInterface::COUNTRY_ID]
+ );
+ unset($postData['custom_region_name']);
+ }
+
+ $this->getRequest()->setPostValue($postData)->setMethod(Http::METHOD_POST);
+ $this->dispatch('customer/address/formPost');
+ }
+
+ /**
+ * Check that save address request performed successfully
+ * (proper success message and redirect to customer/address/index are appear).
+ *
+ * @return void
+ */
+ private function checkRequestPerformedSuccessfully(): void
+ {
+ $this->assertRedirect($this->stringContains('customer/address/index'));
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You saved the address.')]),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * Check that save address request performed with input validation errors
+ * (proper error messages and redirect to customer/address/edit are appear).
+ *
+ * @param array $expectedSessionMessages
+ * @return void
+ */
+ private function checkRequestPerformedWithInputValidationErrors(array $expectedSessionMessages): void
+ {
+ $this->assertRedirect($this->stringContains('customer/address/edit'));
+ foreach ($expectedSessionMessages as $expectedMessage) {
+ $this->assertSessionMessages(
+ $this->contains($this->escaper->escapeHtml((string)__($expectedMessage))),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/UpdateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/UpdateAddressTest.php
new file mode 100644
index 0000000000000..fb18cc45511c8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Address/FormPost/UpdateAddressTest.php
@@ -0,0 +1,403 @@
+ 9548642,
+ AddressInterface::POSTCODE => 95556,
+ AddressInterface::COUNTRY_ID => 'US',
+ 'custom_region_name' => 'Arkansas',
+ AddressInterface::CITY => 'Mukachevo',
+ AddressInterface::STREET => [
+ 'Yellow str, 228',
+ ],
+ AddressInterface::FIRSTNAME => 'Foma',
+ AddressInterface::LASTNAME => 'Kiniaev',
+ ];
+
+ /**
+ * @var Escaper
+ */
+ private $escaper;
+
+ /**
+ * @var Session
+ */
+ private $customerSession;
+
+ /**
+ * @var AddressRegistry
+ */
+ private $addressRegistry;
+
+ /**
+ * @var CustomerRegistry
+ */
+ private $customerRegistry;
+
+ /**
+ * @var GetRegionIdByName
+ */
+ private $getRegionIdByName;
+
+ /**
+ * @var CustomerRepositoryInterface
+ */
+ private $customerRepository;
+
+ /**
+ * @var array
+ */
+ private $processedAddressesIds = [];
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ $this->customerSession = $this->_objectManager->get(Session::class);
+ $this->addressRegistry = $this->_objectManager->get(AddressRegistry::class);
+ $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class);
+ $this->getRegionIdByName = $this->_objectManager->get(GetRegionIdByName::class);
+ $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+ $this->customerSession->setCustomerId('1');
+ $this->processedAddressesIds[] = 1;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerSession->setCustomerId(null);
+ $this->customerRegistry->removeByEmail('customer@example.com');
+ foreach ($this->processedAddressesIds as $addressesId) {
+ $this->addressRegistry->remove($addressesId);
+ }
+ parent::tearDown();
+ }
+
+ /**
+ * Assert that default customer address successfully changed via controller on frontend.
+ *
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php
+ *
+ * @dataProvider postDataForSuccessCreateDefaultAddressDataProvider
+ *
+ * @param array $postData
+ * @param int $expectedShippingId
+ * @param int $expectedBillingId
+ * @return void
+ */
+ public function testAddressSuccessfullyCreatedAsDefaultForCustomer(
+ array $postData,
+ int $expectedShippingId,
+ int $expectedBillingId
+ ): void {
+ $this->processedAddressesIds = [1, 2];
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertEquals(1, $customer->getDefaultShipping(), "Customer doesn't have shipping address");
+ $this->assertEquals(1, $customer->getDefaultBilling(), "Customer doesn't have billing address");
+ $this->performRequestWithData($postData, 2);
+ $this->checkRequestPerformedSuccessfully();
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->assertEquals($expectedShippingId, $customer->getDefaultShipping());
+ $this->assertEquals($expectedBillingId, $customer->getDefaultBilling());
+ }
+
+ /**
+ * Data provider which contain proper POST data for change default customer address.
+ *
+ * @return array
+ */
+ public function postDataForSuccessCreateDefaultAddressDataProvider(): array
+ {
+ return [
+ 'any_addresses_are_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_SHIPPING => 2, AddressInterface::DEFAULT_BILLING => 2]
+ ),
+ 2,
+ 2,
+ ],
+ 'shipping_address_is_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_BILLING => 2]
+ ),
+ 1,
+ 2,
+ ],
+ 'billing_address_is_default' => [
+ array_merge(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::DEFAULT_SHIPPING => 2]
+ ),
+ 2,
+ 1,
+ ],
+ ];
+ }
+
+ /**
+ * Assert that customer address successfully updated via controller on frontend.
+ *
+ * @dataProvider postDataForSuccessUpdateAddressDataProvider
+ *
+ * @param array $postData
+ * @param array $expectedData
+ * @return void
+ */
+ public function testAddressSuccessfullyUpdatedForCustomer(array $postData, array $expectedData): void
+ {
+ if (isset($expectedData['custom_region_name'])) {
+ $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute(
+ $expectedData['custom_region_name'],
+ $expectedData[AddressInterface::COUNTRY_ID]
+ );
+ unset($expectedData['custom_region_name']);
+ }
+ $this->performRequestWithData($postData, 1);
+ $this->checkRequestPerformedSuccessfully();
+ $customer = $this->customerRepository->get('customer@example.com');
+ $customerAddresses = $customer->getAddresses();
+ $this->assertCount(1, $customerAddresses);
+ /** @var AddressInterface $address */
+ $address = reset($customerAddresses);
+ $createdAddressData = $address->__toArray();
+ foreach ($expectedData as $fieldCode => $expectedValue) {
+ if (null === $expectedValue) {
+ $this->assertArrayNotHasKey($fieldCode, $createdAddressData);
+ continue;
+ }
+ $this->assertArrayHasKey($fieldCode, $createdAddressData, "Field $fieldCode wasn't found.");
+ $this->assertEquals($expectedValue, $createdAddressData[$fieldCode]);
+ }
+ }
+
+ /**
+ * Data provider which contain proper POST data for update customer address.
+ *
+ * @return array
+ */
+ public function postDataForSuccessUpdateAddressDataProvider(): array
+ {
+ return [
+ 'required_fields_valid_data' => [
+ self::STATIC_POST_ADDRESS_DATA,
+ [
+ AddressInterface::TELEPHONE => 9548642,
+ AddressInterface::COUNTRY_ID => 'US',
+ AddressInterface::POSTCODE => 95556,
+ 'custom_region_name' => 'Arkansas',
+ AddressInterface::FIRSTNAME => 'Foma',
+ AddressInterface::LASTNAME => 'Kiniaev',
+ AddressInterface::STREET => ['Yellow str, 228'],
+ AddressInterface::CITY => 'Mukachevo',
+ ],
+ ],
+ 'required_field_empty_postcode_for_uk' => [
+ array_replace(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB']
+ ),
+ [
+ AddressInterface::COUNTRY_ID => 'GB',
+ AddressInterface::POSTCODE => null,
+ ],
+ ],
+ 'required_field_empty_region_id_for_ua' => [
+ array_replace(
+ self::STATIC_POST_ADDRESS_DATA,
+ [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA']
+ ),
+ [
+ AddressInterface::COUNTRY_ID => 'UA',
+ AddressInterface::REGION => [
+ RegionInterface::REGION => null,
+ RegionInterface::REGION_CODE => null,
+ RegionInterface::REGION_ID => 0,
+ ],
+ ],
+ ],
+ 'required_field_street_as_array' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]),
+ [AddressInterface::STREET => ['Green str, 67']],
+ ],
+ 'field_company_name' => [
+ array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']),
+ [AddressInterface::COMPANY => 'My company'],
+ ],
+ 'field_vat_number' => [
+ array_merge(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']),
+ [AddressInterface::VAT_ID => 'My VAT number'],
+ ],
+ ];
+ }
+
+ /**
+ * Assert that customer address wasn't updated via controller on frontend
+ * when POST data broken.
+ *
+ * @dataProvider postDataForUpdateAddressWithErrorDataProvider
+ *
+ * @param array $postData
+ * @param array $expectedSessionMessages
+ * @return void
+ */
+ public function testAddressWasntUpdatedForCustomer(array $postData, array $expectedSessionMessages): void
+ {
+ $this->performRequestWithData($postData, 1);
+ $this->checkRequestPerformedWithInputValidationErrors($expectedSessionMessages);
+ }
+
+ /**
+ * Data provider which contain broken POST data for update customer address with error.
+ *
+ * @return array
+ */
+ public function postDataForUpdateAddressWithErrorDataProvider(): array
+ {
+ return [
+ 'empty_post_data' => [
+ [],
+ [
+ 'One or more input exceptions have occurred.',
+ '"firstname" is required. Enter and try again.',
+ '"lastname" is required. Enter and try again.',
+ '"street" is required. Enter and try again.',
+ '"city" is required. Enter and try again.',
+ '"telephone" is required. Enter and try again.',
+ '"postcode" is required. Enter and try again.',
+ '"countryId" is required. Enter and try again.',
+ ]
+ ],
+ 'required_field_empty_telephone' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']),
+ ['"telephone" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_postcode_for_us' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::POSTCODE => '']),
+ ['"postcode" is required. Enter and try again.'],
+ ],
+// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031
+// 'required_field_empty_region_id_for_us' => [
+// array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::REGION_ID => '']),
+// ['"regionId" is required. Enter and try again.'],
+// ],
+ 'required_field_empty_firstname' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']),
+ ['"firstname" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_lastname' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::LASTNAME => '']),
+ ['"lastname" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_street_as_string' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => '']),
+ ['"street" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_street_as_array' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::STREET => []]),
+ ['"street" is required. Enter and try again.'],
+ ],
+ 'required_field_empty_city' => [
+ array_replace(self::STATIC_POST_ADDRESS_DATA, [AddressInterface::CITY => '']),
+ ['"city" is required. Enter and try again.'],
+ ],
+ ];
+ }
+
+ /**
+ * Perform request with provided POST data.
+ *
+ * @param array $postData
+ * @param int $processAddressId
+ * @return void
+ */
+ private function performRequestWithData(array $postData, int $processAddressId): void
+ {
+ $postData[AddressInterface::ID] = $processAddressId;
+ if (isset($postData['custom_region_name'])) {
+ $postData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute(
+ $postData['custom_region_name'],
+ $postData[AddressInterface::COUNTRY_ID]
+ );
+ unset($postData['custom_region_name']);
+ }
+
+ $this->getRequest()->setPostValue($postData)->setMethod(Http::METHOD_POST);
+ $this->dispatch('customer/address/formPost');
+ }
+
+ /**
+ * Check that save address request performed successfully
+ * (proper success message and redirect to customer/address/index are appear).
+ *
+ * @return void
+ */
+ private function checkRequestPerformedSuccessfully(): void
+ {
+ $this->assertRedirect($this->stringContains('customer/address/index'));
+ $this->assertSessionMessages(
+ $this->equalTo([(string)__('You saved the address.')]),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * Check that save address request performed with input validation errors
+ * (proper error messages and redirect to customer/address/edit are appear).
+ *
+ * @param array $expectedSessionMessages
+ * @return void
+ */
+ private function checkRequestPerformedWithInputValidationErrors(array $expectedSessionMessages): void
+ {
+ $this->assertRedirect($this->stringContains('customer/address/edit'));
+ foreach ($expectedSessionMessages as $expectedMessage) {
+ $this->assertSessionMessages(
+ $this->contains($this->escaper->escapeHtml((string)__($expectedMessage))),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php
index 3db22b8379850..0869832091fec 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Section/LoadTest.php
@@ -3,19 +3,104 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Customer\Controller\Section;
-class LoadTest extends \Magento\TestFramework\TestCase\AbstractController
+use Magento\Customer\Model\Session;
+use Magento\Framework\App\Request\Http as HttpRequest;
+use Magento\Framework\Serialize\SerializerInterface;
+use Magento\TestFramework\TestCase\AbstractController;
+use Magento\Framework\Escaper;
+
+/**
+ * Load customer data test class.
+ *
+ * @magentoDbIsolation enabled
+ * @magentoAppArea frontend
+ */
+class LoadTest extends AbstractController
{
- public function testLoadInvalidSection()
+ /** @var Session */
+ private $customerSession;
+
+ /** @var SerializerInterface */
+ private $json;
+
+ /** @var Escaper */
+ private $escaper;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->customerSession = $this->_objectManager->get(Session::class);
+ $this->json = $this->_objectManager->get(SerializerInterface::class);
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @return void
+ */
+ public function testLoadInvalidSection(): void
{
- $expected = [
- 'message' => 'The "section<invalid" section source isn't supported.',
- ];
+ $message = $this->escaper->escapeHtml('The "section $message];
$this->dispatch(
'/customer/section/load/?sections=sectiongetResponse()->getBody());
+ $this->assertEquals($this->json->serialize($expected), $this->getResponse()->getBody());
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 1
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php
+ *
+ * @return void
+ */
+ public function testWishListCounterUseQty(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $response = $this->performWishListSectionRequest();
+ $this->assertEquals('3 items', $response['wishlist']['counter']);
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 0
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php
+ *
+ * @return void
+ */
+ public function testWishListCounterNotUseQty(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $response = $this->performWishListSectionRequest();
+ $this->assertEquals('1 item', $response['wishlist']['counter']);
+ }
+
+ /**
+ * Perform wish list section request.
+ *
+ * @return array
+ */
+ private function performWishListSectionRequest(): array
+ {
+ $this->getRequest()->setParam('sections', 'wishlist')->setMethod(HttpRequest::METHOD_GET);
+ $this->dispatch('customer/section/load');
+
+ return $this->json->unserialize($this->getResponse()->getBody());
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer.php b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer.php
new file mode 100644
index 0000000000000..2e298a7e508a3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer.php
@@ -0,0 +1,44 @@
+get(AccountManagementInterface::class);
+$customerFactory = $objectManager->get(CustomerFactory::class);
+$customerFactory->create();
+/** @var StoreManagerInterface $storeManager */
+$storeManager = $objectManager->get(StoreManagerInterface::class);
+$website = $storeManager->getWebsite('base');
+/** @var GroupManagement $groupManagement */
+$groupManagement = $objectManager->get(GroupManagement::class);
+$defaultStoreId = $website->getDefaultStore()->getId();
+/** @var AttributeRepository $attributeRepository */
+$attributeRepository = $objectManager->get(AttributeRepository::class);
+$gender = $attributeRepository->get(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'gender')
+ ->getSource()->getOptionId('Male');
+$customer = $customerFactory->create();
+$customer->setWebsiteId($website->getId())
+ ->setEmail('new_customer@example.com')
+ ->setGroupId($groupManagement->getDefaultGroup($defaultStoreId)->getId())
+ ->setStoreId($defaultStoreId)
+ ->setPrefix('Mr.')
+ ->setFirstname('John')
+ ->setMiddlename('A')
+ ->setLastname('Smith')
+ ->setSuffix('Esq.')
+ ->setDefaultBilling(1)
+ ->setDefaultShipping(1)
+ ->setGender($gender);
+$accountManagement->createAccount($customer, 'Qwert12345');
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer_rollback.php
new file mode 100644
index 0000000000000..48da309f6878f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/new_customer_rollback.php
@@ -0,0 +1,33 @@
+get(Registry::class);
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = $objectManager->get(CustomerRepositoryInterface::class);
+/** @var WebsiteRepositoryInterface $websiteRepository */
+$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
+$websiteId = $websiteRepository->get('base')->getId();
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $customer = $customerRepository->get('new_customer@example.com', $websiteId);
+ $customerRepository->delete($customer);
+} catch (NoSuchEntityException $e) {
+ //customer already deleted
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php b/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php
index 3aad592ad34ef..e27010dc042ca 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/unconfirmed_customer.php
@@ -7,7 +7,7 @@
use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerMetadataInterface;
-use \Magento\Customer\Model\Data\CustomerFactory;
+use Magento\Customer\Model\Data\CustomerFactory;
use Magento\Eav\Model\AttributeRepository;
use Magento\Framework\Math\Random;
use Magento\Store\Api\WebsiteRepositoryInterface;
diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php
index cf39757cb8264..fc0c23b346fe2 100644
--- a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php
+++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/AbstractFiltersTest.php
@@ -140,7 +140,7 @@ protected function getSearchFiltersAndAssert(
$this->updateAttribute($attributeData);
$this->updateProducts($products, $this->getAttributeCode());
$this->clearInstanceAndReindexSearch();
- $this->navigationBlock->getRequest()->setParams(['q' => 'Simple Product']);
+ $this->navigationBlock->getRequest()->setParams(['q' => $this->getSearchString()]);
$this->navigationBlock->setLayout($this->layout);
$filter = $this->getFilterByCode($this->navigationBlock->getFilters(), $this->getAttributeCode());
@@ -293,4 +293,14 @@ protected function createNavigationBlockInstance(): void
$this->navigationBlock = $this->objectManager->create(CategoryNavigationBlock::class);
}
}
+
+ /**
+ * Returns search query for filters on search page.
+ *
+ * @return string
+ */
+ protected function getSearchString(): string
+ {
+ return 'Simple Product';
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/MultiselectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/MultiselectFilterTest.php
new file mode 100644
index 0000000000000..518a0a19f857f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/MultiselectFilterTest.php
@@ -0,0 +1,109 @@
+moduleManager = $this->objectManager->get(Manager::class);
+ //This check is needed because LayeredNavigation independent of Magento_Bundle
+ if (!$this->moduleManager->isEnabled('Magento_Bundle')) {
+ $this->markTestSkipped('Magento_Bundle module disabled.');
+ }
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/multiselect_attribute.php
+ * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php
+ * @dataProvider getFiltersWithCustomAttributeDataProvider
+ * @param array $products
+ * @param array $attributeData
+ * @param array $expectation
+ * @return void
+ */
+ public function testGetFiltersWithCustomAttribute(array $products, array $attributeData, array $expectation): void
+ {
+ $this->getCategoryFiltersAndAssert($products, $attributeData, $expectation, 'Category 1');
+ }
+
+ /**
+ * @return array
+ */
+ public function getFiltersWithCustomAttributeDataProvider(): array
+ {
+ return [
+ 'not_used_in_navigation' => [
+ 'products_data' => [],
+ 'attribute_data' => ['is_filterable' => 0],
+ 'expectation' => [],
+ ],
+ 'used_in_navigation_with_results' => [
+ 'products_data' => [
+ 'bundle-product' => 'Option 1',
+ 'bundle-product-dropdown-options' => 'Option 2',
+ ],
+ 'attribute_data' => ['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS],
+ 'expectation' => [
+ ['label' => 'Option 1', 'count' => 1],
+ ['label' => 'Option 2', 'count' => 1],
+ ],
+ ],
+ 'used_in_navigation_without_results' => [
+ 'products_data' => [
+ 'bundle-product' => 'Option 1',
+ 'bundle-product-dropdown-options' => 'Option 2',
+ ],
+ 'attribute_data' => ['is_filterable' => 2],
+ 'expectation' => [
+ ['label' => 'Option 1', 'count' => 1],
+ ['label' => 'Option 2', 'count' => 1],
+ ['label' => 'Option 3', 'count' => 0],
+ ['label' => 'Option 4 "!@#$%^&*', 'count' => 0],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getLayerType(): string
+ {
+ return Resolver::CATALOG_LAYER_CATEGORY;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributeCode(): string
+ {
+ return 'multiselect_attribute';
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php
new file mode 100644
index 0000000000000..3817c5efc86fc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/PriceFilterTest.php
@@ -0,0 +1,96 @@
+moduleManager = $this->objectManager->get(Manager::class);
+ //This check is needed because LayeredNavigation independent of Magento_Bundle
+ if (!$this->moduleManager->isEnabled('Magento_Bundle')) {
+ $this->markTestSkipped('Magento_Bundle module disabled.');
+ }
+ }
+
+ /**
+ * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php
+ * @magentoConfigFixture current_store catalog/layered_navigation/price_range_calculation manual
+ * @magentoConfigFixture current_store catalog/layered_navigation/price_range_step 10
+ * @return void
+ */
+ public function testGetFilters(): void
+ {
+ $this->getCategoryFiltersAndAssert(
+ ['bundle-product' => 20.00],
+ ['is_filterable' => '1'],
+ [
+ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1],
+ ['label' => '$20.00 and above', 'value' => '20-', 'count' => 1],
+ ],
+ 'Category 1'
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getLayerType(): string
+ {
+ return Resolver::CATALOG_LAYER_CATEGORY;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributeCode(): string
+ {
+ return 'price';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function prepareFilterItems(AbstractFilter $filter): array
+ {
+ $items = [];
+ /** @var Item $item */
+ foreach ($filter->getItems() as $item) {
+ $item = [
+ 'label' => strip_tags(__($item->getData('label'))->render()),
+ 'value' => $item->getData('value'),
+ 'count' => $item->getData('count'),
+ ];
+ $items[] = $item;
+ }
+
+ return $items;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/SelectFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/SelectFilterTest.php
new file mode 100644
index 0000000000000..7b3be2afbdbdb
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Category/Bundle/SelectFilterTest.php
@@ -0,0 +1,108 @@
+moduleManager = $this->objectManager->get(Manager::class);
+ //This check is needed because LayeredNavigation independent of Magento_Bundle
+ if (!$this->moduleManager->isEnabled('Magento_Bundle')) {
+ $this->markTestSkipped('Magento_Bundle module disabled.');
+ }
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_dropdown_attribute.php
+ * @magentoDataFixture Magento/Bundle/_files/dynamic_and_fixed_bundle_products_in_category.php
+ * @dataProvider getFiltersWithCustomAttributeDataProvider
+ * @param array $products
+ * @param array $attributeData
+ * @param array $expectation
+ * @return void
+ */
+ public function testGetFiltersWithCustomAttribute(array $products, array $attributeData, array $expectation): void
+ {
+ $this->getCategoryFiltersAndAssert($products, $attributeData, $expectation, 'Category 1');
+ }
+
+ /**
+ * @return array
+ */
+ public function getFiltersWithCustomAttributeDataProvider(): array
+ {
+ return [
+ 'not_used_in_navigation' => [
+ 'products_data' => [],
+ 'attribute_data' => ['is_filterable' => 0],
+ 'expectation' => [],
+ ],
+ 'used_in_navigation_with_results' => [
+ 'products_data' => [
+ 'bundle-product' => 'Option 1',
+ 'bundle-product-dropdown-options' => 'Option 2',
+ ],
+ 'attribute_data' => ['is_filterable' => AbstractFilter::ATTRIBUTE_OPTIONS_ONLY_WITH_RESULTS],
+ 'expectation' => [
+ ['label' => 'Option 1', 'count' => 1],
+ ['label' => 'Option 2', 'count' => 1],
+ ],
+ ],
+ 'used_in_navigation_without_results' => [
+ 'products_data' => [
+ 'bundle-product' => 'Option 1',
+ 'bundle-product-dropdown-options' => 'Option 2',
+ ],
+ 'attribute_data' => ['is_filterable' => 2],
+ 'expectation' => [
+ ['label' => 'Option 1', 'count' => 1],
+ ['label' => 'Option 2', 'count' => 1],
+ ['label' => 'Option 3', 'count' => 0],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getLayerType(): string
+ {
+ return Resolver::CATALOG_LAYER_CATEGORY;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getAttributeCode(): string
+ {
+ return 'dropdown_attribute';
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php
new file mode 100644
index 0000000000000..435dd29e16dfa
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/LayeredNavigation/Block/Navigation/Search/Bundle/PriceFilterTest.php
@@ -0,0 +1,55 @@
+getSearchFiltersAndAssert(
+ ['bundle-product' => 20.00],
+ ['is_filterable_in_search' => 1],
+ [
+ ['label' => '$10.00 - $19.99', 'value' => '10-20', 'count' => 1],
+ ['label' => '$20.00 and above', 'value' => '20-', 'count' => 1],
+ ]
+ );
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getLayerType(): string
+ {
+ return Resolver::CATALOG_LAYER_SEARCH;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getSearchString(): string
+ {
+ return 'Bundle';
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Block/Account/LinkTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Account/LinkTest.php
new file mode 100644
index 0000000000000..6dac22bd49384
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Block/Account/LinkTest.php
@@ -0,0 +1,79 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->page = $this->objectManager->create(Page::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testNewsletterLink(): void
+ {
+ $this->preparePage();
+ $block = $this->page->getLayout()->getBlock('customer-account-navigation-newsletter-subscriptions-link');
+ $this->assertNotFalse($block);
+ $html = $block->toHtml();
+ $this->assertContains('newsletter/manage/', $html);
+ $this->assertEquals('Newsletter Subscriptions', strip_tags($html));
+ }
+
+ /**
+ * @magentoConfigFixture current_store newsletter/general/active 0
+ *
+ * @return void
+ */
+ public function testNewsletterLinkDisabled(): void
+ {
+ $this->preparePage();
+ $block = $this->page->getLayout()->getBlock('customer-account-navigation-newsletter-subscriptions-link');
+ $this->assertFalse($block);
+ }
+
+ /**
+ * Prepare page before render
+ *
+ * @return void
+ */
+ private function preparePage(): void
+ {
+ $this->page->addHandle([
+ 'default',
+ 'customer_account',
+ ]);
+ $this->page->getLayout()->generateXml();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Manage/SaveTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Manage/SaveTest.php
new file mode 100644
index 0000000000000..87b022c942318
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Manage/SaveTest.php
@@ -0,0 +1,157 @@
+customerSession = $this->_objectManager->get(Session::class);
+ $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+ $this->formKey = $this->_objectManager->get(FormKey::class);
+ $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerSession->logout();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/new_customer.php
+ *
+ * @dataProvider subscriptionDataProvider
+ *
+ * @param bool $isSubscribed
+ * @param string $expectedMessage
+ * @return void
+ */
+ public function testSaveAction(bool $isSubscribed, string $expectedMessage): void
+ {
+ $this->loginCustomer('new_customer@example.com');
+ $this->_objectManager->removeSharedInstance(CustomerPlugin::class);
+ $this->dispatchSaveAction($isSubscribed);
+ $this->assertSuccessSubscription($expectedMessage);
+ }
+
+ /**
+ * @return array
+ */
+ public function subscriptionDataProvider(): array
+ {
+ return [
+ 'subscribe_customer' => [
+ 'is_subscribed' => true,
+ 'expected_message' => 'We have saved your subscription.',
+ ],
+ 'unsubscribe_customer' => [
+ 'is_subscribed' => false,
+ 'expected_message' => 'We have updated your subscription.',
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php
+ * @magentoDataFixture Magento/Customer/_files/new_customer.php
+ *
+ * @return void
+ */
+ public function testSubscribeWithEnabledConfirmation(): void
+ {
+ $this->loginCustomer('new_customer@example.com');
+ $this->dispatchSaveAction(true);
+ $this->assertSuccessSubscription('A confirmation request has been sent.');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Newsletter/_files/customer_with_subscription.php
+ *
+ * @return void
+ */
+ public function testUnsubscribeSubscribedCustomer(): void
+ {
+ $this->loginCustomer('new_customer@example.com');
+ $this->_objectManager->removeSharedInstance(CustomerPlugin::class);
+ $this->dispatchSaveAction(false);
+ $this->assertSuccessSubscription('We have removed your newsletter subscription.');
+ }
+
+ /**
+ * Dispatch save action with parameters
+ *
+ * @param string $isSubscribed
+ * @return void
+ */
+ private function dispatchSaveAction(bool $isSubscribed): void
+ {
+ $this->_objectManager->removeSharedInstance(CustomerPlugin::class);
+ $this->getRequest()->setParam('form_key', $this->formKey->getFormKey())
+ ->setParam('is_subscribed', $isSubscribed);
+ $this->dispatch('newsletter/manage/save');
+ }
+
+ /**
+ * Login customer by email
+ *
+ * @param string $email
+ * @return void
+ */
+ private function loginCustomer(string $email): void
+ {
+ $customer = $this->customerRepository->get($email);
+ $this->customerSession->loginById($customer->getId());
+ }
+
+ /**
+ * Assert that action was successfully done
+ *
+ * @param string $expectedMessage
+ * @return void
+ */
+ private function assertSuccessSubscription(string $expectedMessage): void
+ {
+ $this->assertRedirect($this->stringContains('customer/account/'));
+ $this->assertSessionMessages($this->equalTo([(string)__($expectedMessage)]), MessageInterface::TYPE_SUCCESS);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php
deleted file mode 100644
index 175c1c7c6c668..0000000000000
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/ManageTest.php
+++ /dev/null
@@ -1,98 +0,0 @@
-customerSession = $objectManager->get(\Magento\Customer\Model\Session::class);
- $this->customerSession->setCustomerId(1);
- $this->coreSession = $objectManager->get(\Magento\Framework\Session\Generic::class);
- $this->coreSession->setData('_form_key', 'formKey');
- }
-
- /**
- * test tearDown
- */
- protected function tearDown()
- {
- $this->customerSession->setCustomerId(null);
- $this->coreSession->unsetData('_form_key');
- }
-
- /**
- * @magentoDataFixture Magento/Customer/_files/customer.php
- */
- public function testSaveAction()
- {
- $this->getRequest()
- ->setParam('form_key', 'formKey')
- ->setParam('is_subscribed', '1');
- $this->dispatch('newsletter/manage/save');
-
- $this->assertRedirect($this->stringContains('customer/account/'));
-
- /**
- * Check that errors
- */
- $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR);
-
- /**
- * Check that success message
- */
- $this->assertSessionMessages(
- $this->equalTo(['We have saved your subscription.']),
- \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
- );
- }
-
- /**
- * @magentoDataFixture Magento/Customer/_files/customer.php
- */
- public function testSaveActionRemoveSubscription()
- {
-
- $this->getRequest()
- ->setParam('form_key', 'formKey')
- ->setParam('is_subscribed', '0');
- $this->dispatch('newsletter/manage/save');
-
- $this->assertRedirect($this->stringContains('customer/account/'));
-
- /**
- * Check that errors
- */
- $this->assertSessionMessages($this->isEmpty(), \Magento\Framework\Message\MessageInterface::TYPE_ERROR);
-
- /**
- * Check that success message
- */
- $this->assertSessionMessages(
- $this->equalTo(['We have updated your subscription.']),
- \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
- );
- }
-}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php
new file mode 100644
index 0000000000000..0f07d8b31d13b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/Subscriber/NewActionTest.php
@@ -0,0 +1,255 @@
+session = $this->_objectManager->get(Session::class);
+ $this->subscriberCollectionFactory = $this->_objectManager->get(CollectionFactory::class);
+ $this->subscriberResource = $this->_objectManager->get(SubscriberResource::class);
+ $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+ $this->customerUrl = $this->_objectManager->get(Url::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ if ($this->subscriberToDelete) {
+ $this->deleteSubscriber($this->subscriberToDelete);
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider subscribersDataProvider
+ *
+ * @param string $email
+ * @param string $expectedMessage
+ * @return void
+ */
+ public function testNewAction(string $email, string $expectedMessage): void
+ {
+ $this->subscriberToDelete = $email ? $email : null;
+ $this->prepareRequest($email);
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts($expectedMessage);
+ }
+
+ /**
+ * @return array
+ */
+ public function subscribersDataProvider(): array
+ {
+ return [
+ 'without_email' => [
+ 'email' => '',
+ 'message' => '',
+ ],
+ 'with_unused_email' => [
+ 'email' => 'not_used@example.com',
+ 'message' => 'Thank you for your subscription.',
+ ],
+ 'with_invalid_email' => [
+ 'email' => 'invalid_email.com',
+ 'message' => 'Please enter a valid email address.'
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/new_customer.php
+ *
+ * @return void
+ */
+ public function testNewActionUsedEmail(): void
+ {
+ $this->prepareRequest('new_customer@example.com');
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts('Thank you for your subscription.');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/new_customer.php
+ *
+ * @return void
+ */
+ public function testNewActionOwnerEmail(): void
+ {
+ $this->prepareRequest('new_customer@example.com');
+ $this->session->loginById(1);
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts('Thank you for your subscription.');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Newsletter/_files/customer_with_subscription.php
+ *
+ * @return void
+ */
+ public function testAlreadyExistEmail(): void
+ {
+ $this->prepareRequest('new_customer@example.com');
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts('This email address is already subscribed.');
+ }
+
+ /**
+ * @magentoConfigFixture current_store newsletter/subscription/allow_guest_subscribe 0
+ *
+ * @return void
+ */
+ public function testWithNotAllowedGuestSubscription(): void
+ {
+ $message = sprintf(
+ 'Sorry, but the administrator denied subscription for guests. Please register.',
+ $this->customerUrl->getRegisterUrl()
+ );
+ $this->subscriberToDelete = 'guest@example.com';
+ $this->prepareRequest('guest@example.com');
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts($message);
+ }
+
+ /**
+ * @magentoConfigFixture current_store newsletter/subscription/allow_guest_subscribe 0
+ *
+ * @magentoDataFixture Magento/Customer/_files/new_customer.php
+ *
+ * @return void
+ */
+ public function testCustomerSubscribeUnrelatedEmailWithNotAllowedGuestSubscription(): void
+ {
+ $this->markTestSkipped('Blocked by MC-31662');
+ $this->subscriberToDelete = 'guest@example.com';
+ $this->session->loginById($this->customerRepository->get('new_customer@example.com')->getId());
+ $this->prepareRequest('guest@example.com');
+ $this->dispatch('newsletter/subscriber/new');
+ //ToDo message need to be specified after bug MC-31662 fixing
+ $this->performAsserts('');
+ }
+
+ /**
+ * @magentoConfigFixture current_store newsletter/subscription/confirm 1
+ *
+ * @return void
+ */
+ public function testWithRequiredConfirmation(): void
+ {
+ $this->subscriberToDelete = 'guest@example.com';
+ $this->prepareRequest('guest@example.com');
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts('The confirmation request has been sent.');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Newsletter/_files/three_subscribers.php
+ *
+ * @return void
+ */
+ public function testWithEmailAssignedToAnotherCustomer(): void
+ {
+ $this->session->loginById(1);
+ $this->prepareRequest('customer2@search.example.com');
+ $this->dispatch('newsletter/subscriber/new');
+
+ $this->performAsserts('This email address is already assigned to another user.');
+ }
+
+ /**
+ * Prepare request
+ *
+ * @param string $email
+ * @return void
+ */
+ private function prepareRequest(string $email): void
+ {
+ $parameters = $this->_objectManager->create(Parameters::class);
+ $parameters->set('HTTP_REFERER', 'http://localhost/testRedirect');
+ $this->getRequest()->setServer($parameters);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setPostValue(['email' => $email]);
+ }
+
+ /**
+ * Assert session message and expected redirect
+ *
+ * @param string $message
+ * @return void
+ */
+ private function performAsserts(string $message): void
+ {
+ if ($message) {
+ $this->assertSessionMessages($this->equalTo([(string)__($message)]));
+ }
+ $this->assertRedirect($this->equalTo('http://localhost/testRedirect'));
+ }
+
+ /**
+ * Delete subscribers by email
+ *
+ * @param string $email
+ * @return void
+ */
+ private function deleteSubscriber(string $email): void
+ {
+ $collection = $this->subscriberCollectionFactory->create();
+ $item = $collection->addFieldToFilter('subscriber_email', $email)->setPageSize(1)->getFirstItem();
+ if ($item->getId()) {
+ $this->subscriberResource->delete($item);
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php
index bf19d6ddefc36..ec6ae77c385fd 100644
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Controller/SubscriberTest.php
@@ -23,59 +23,6 @@
*/
class SubscriberTest extends AbstractController
{
- public function testNewAction()
- {
- $this->getRequest()->setMethod('POST');
-
- $this->dispatch('newsletter/subscriber/new');
-
- $this->assertSessionMessages($this->isEmpty());
- $this->assertRedirect($this->anything());
- }
-
- /**
- * @magentoDbIsolation enabled
- */
- public function testNewActionUnusedEmail()
- {
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setPostValue(['email' => 'not_used@example.com']);
-
- $this->dispatch('newsletter/subscriber/new');
-
- $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.']));
- $this->assertRedirect($this->anything());
- }
-
- /**
- * @magentoDataFixture Magento/Customer/_files/customer.php
- */
- public function testNewActionUsedEmail()
- {
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setPostValue(['email' => 'customer@example.com']);
-
- $this->dispatch('newsletter/subscriber/new');
-
- $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.']));
- $this->assertRedirect($this->anything());
- }
-
- /**
- * @magentoDataFixture Magento/Customer/_files/customer.php
- */
- public function testNewActionOwnerEmail()
- {
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setPostValue(['email' => 'customer@example.com']);
- $this->login(1);
-
- $this->dispatch('newsletter/subscriber/new');
-
- $this->assertSessionMessages($this->equalTo(['Thank you for your subscription.']));
- $this->assertRedirect($this->anything());
- }
-
/**
* Check that Customer still subscribed for newsletters emails after registration.
*
@@ -149,18 +96,4 @@ private function fillRequestWithAccountDataAndFormKey($email)
->setPostValue('create_address', true)
->setParam('form_key', Bootstrap::getObjectManager()->get(FormKey::class)->getFormKey());
}
-
- /**
- * Login the user
- *
- * @param string $customerId Customer to mark as logged in for the session
- * @return void
- */
- protected function login($customerId)
- {
- /** @var \Magento\Customer\Model\Session $session */
- $session = Bootstrap::getObjectManager()
- ->get(\Magento\Customer\Model\Session::class);
- $session->loginById($customerId);
- }
}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php
index bdcbdc035d2b0..06c8902f45897 100644
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php
@@ -3,104 +3,175 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Newsletter\Model;
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Mail\Template\TransportBuilderMock;
+use PHPUnit\Framework\TestCase;
/**
- * \Magento\Newsletter\Model\Subscriber tests
+ * Class checks subscription behavior.
+ *
+ * @see \Magento\Newsletter\Model\Subscriber
*/
-class SubscriberTest extends \PHPUnit\Framework\TestCase
+class SubscriberTest extends TestCase
{
+ /** @var ObjectManagerInterface */
+ private $objectManager;
+
+ /** @var SubscriberFactory */
+ private $subscriberFactory;
+
+ /** @var TransportBuilderMock */
+ private $transportBuilder;
+
+ /** @var CustomerRepositoryInterface */
+ private $customerRepository;
+
/**
- * @var Subscriber
+ * @inheritdoc
*/
- private $model;
-
protected function setUp()
{
- $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Newsletter\Model\Subscriber::class
- );
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->subscriberFactory = $this->objectManager->get(SubscriberFactory::class);
+ $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class);
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
}
/**
- * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
* @magentoConfigFixture current_store newsletter/subscription/confirm 1
+ *
+ * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
+ *
+ * @return void
*/
- public function testEmailConfirmation()
+ public function testEmailConfirmation(): void
{
- $this->model->subscribe('customer_confirm@example.com');
- /** @var TransportBuilderMock $transportBuilder */
- $transportBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
- ->get(\Magento\TestFramework\Mail\Template\TransportBuilderMock::class);
+ $subscriber = $this->subscriberFactory->create();
+ $subscriber->subscribe('customer_confirm@example.com');
// confirmationCode 'ysayquyajua23iq29gxwu2eax2qb6gvy' is taken from fixture
$this->assertContains(
- '/newsletter/subscriber/confirm/id/' . $this->model->getSubscriberId()
+ '/newsletter/subscriber/confirm/id/' . $subscriber->getSubscriberId()
. '/code/ysayquyajua23iq29gxwu2eax2qb6gvy',
- $transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent()
+ $this->transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent()
);
- $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $this->model->getSubscriberStatus());
+ $this->assertEquals(Subscriber::STATUS_NOT_ACTIVE, $subscriber->getSubscriberStatus());
}
/**
* @magentoDataFixture Magento/Newsletter/_files/subscribers.php
+ *
+ * @return void
*/
- public function testLoadByCustomerId()
+ public function testLoadByCustomerId(): void
{
- $this->assertSame($this->model, $this->model->loadByCustomerId(1));
- $this->assertEquals('customer@example.com', $this->model->getSubscriberEmail());
+ $subscriber = $this->subscriberFactory->create();
+ $this->assertSame($subscriber, $subscriber->loadByCustomerId(1));
+ $this->assertEquals('customer@example.com', $subscriber->getSubscriberEmail());
}
/**
* @magentoDataFixture Magento/Newsletter/_files/subscribers.php
- * @magentoAppArea frontend
+ *
+ * @magentoAppArea frontend
+ *
+ * @return void
*/
- public function testUnsubscribeSubscribe()
+ public function testUnsubscribeSubscribe(): void
{
- // Unsubscribe and verify
- $this->assertSame($this->model, $this->model->loadByCustomerId(1));
- $this->assertEquals($this->model, $this->model->unsubscribe());
- $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $this->model->getSubscriberStatus());
-
+ $subscriber = $this->subscriberFactory->create();
+ $this->assertSame($subscriber, $subscriber->loadByCustomerId(1));
+ $this->assertEquals($subscriber, $subscriber->unsubscribe());
+ $this->assertContains(
+ 'You have been unsubscribed from the newsletter.',
+ $this->transportBuilder->getSentMessage()->getRawMessage()
+ );
+ $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getSubscriberStatus());
// Subscribe and verify
- $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->model->subscribe('customer@example.com'));
- $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->model->getSubscriberStatus());
+ $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->subscribe('customer@example.com'));
+ $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getSubscriberStatus());
+ $this->assertContains(
+ 'You have been successfully subscribed to our newsletter.',
+ $this->transportBuilder->getSentMessage()->getRawMessage()
+ );
}
/**
* @magentoDataFixture Magento/Newsletter/_files/subscribers.php
- * @magentoAppArea frontend
+ *
+ * @magentoAppArea frontend
+ *
+ * @return void
*/
- public function testUnsubscribeSubscribeByCustomerId()
+ public function testUnsubscribeSubscribeByCustomerId(): void
{
+ $subscriber = $this->subscriberFactory->create();
// Unsubscribe and verify
- $this->assertSame($this->model, $this->model->unsubscribeCustomerById(1));
- $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $this->model->getSubscriberStatus());
-
+ $this->assertSame($subscriber, $subscriber->unsubscribeCustomerById(1));
+ $this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getSubscriberStatus());
+ $this->assertContains(
+ 'You have been unsubscribed from the newsletter.',
+ $this->transportBuilder->getSentMessage()->getRawMessage()
+ );
// Subscribe and verify
- $this->assertSame($this->model, $this->model->subscribeCustomerById(1));
- $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $this->model->getSubscriberStatus());
+ $this->assertSame($subscriber, $subscriber->subscribeCustomerById(1));
+ $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getSubscriberStatus());
+ $this->assertContains(
+ 'You have been successfully subscribed to our newsletter.',
+ $this->transportBuilder->getSentMessage()->getRawMessage()
+ );
}
/**
- * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
* @magentoConfigFixture current_store newsletter/subscription/confirm 1
+ *
+ * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
+ *
+ * @return void
*/
- public function testConfirm()
+ public function testConfirm(): void
{
+ $subscriber = $this->subscriberFactory->create();
$customerEmail = 'customer_confirm@example.com';
- $this->model->subscribe($customerEmail);
- $this->model->loadByEmail($customerEmail);
- $this->model->confirm($this->model->getSubscriberConfirmCode());
-
- $transportBuilder = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\TestFramework\Mail\Template\TransportBuilderMock::class
- );
-
+ $subscriber->subscribe($customerEmail);
+ $subscriber->loadByEmail($customerEmail);
+ $subscriber->confirm($subscriber->getSubscriberConfirmCode());
$this->assertContains(
'You have been successfully subscribed to our newsletter.',
- $transportBuilder->getSentMessage()->getBody()->getParts()[0]->getRawContent()
+ $this->transportBuilder->getSentMessage()->getRawMessage()
);
}
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php
+ * @magentoDataFixture Magento/Newsletter/_files/newsletter_unconfirmed_customer.php
+ *
+ * @return void
+ */
+ public function testSubscribeUnconfirmedCustomerWithSubscription(): void
+ {
+ $customer = $this->customerRepository->get('unconfirmedcustomer@example.com');
+ $subscriber = $this->subscriberFactory->create();
+ $subscriber->subscribeCustomerById($customer->getId());
+ $this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getStatus());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php
+ * @magentoDataFixture Magento/Customer/_files/unconfirmed_customer.php
+ *
+ * @return void
+ */
+ public function testSubscribeUnconfirmedCustomerWithoutSubscription(): void
+ {
+ $customer = $this->customerRepository->get('unconfirmedcustomer@example.com');
+ $subscriber = $this->subscriberFactory->create();
+ $subscriber->subscribeCustomerById($customer->getId());
+ $this->assertEquals(Subscriber::STATUS_UNCONFIRMED, $subscriber->getStatus());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription.php
new file mode 100644
index 0000000000000..f700889881e8d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription.php
@@ -0,0 +1,13 @@
+get(SubscriberFactory::class);
+$subscriberFactory->create()->subscribe('new_customer@example.com');
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription_rollback.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription_rollback.php
new file mode 100644
index 0000000000000..145f6a9b43bbb
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/customer_with_subscription_rollback.php
@@ -0,0 +1,8 @@
+get(SubscriberFactory::class);
+$subscriberFactory->create()->subscribe('unconfirmedcustomer@example.com');
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer_rollback.php
new file mode 100644
index 0000000000000..5742526988187
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/newsletter_unconfirmed_customer_rollback.php
@@ -0,0 +1,8 @@
+ 120.00,
'store_id' => 1,
'website_id' => 1,
- 'payment' => $payment
],
[
'increment_id' => '100000003',
@@ -36,7 +35,6 @@
'total_paid' => 130.00,
'store_id' => 0,
'website_id' => 0,
- 'payment' => $payment
],
[
'increment_id' => '100000004',
@@ -47,7 +45,6 @@
'subtotal' => 140.00,
'store_id' => 1,
'website_id' => 1,
- 'payment' => $payment
],
[
'increment_id' => '100000005',
@@ -59,7 +56,6 @@
'total_paid' => 150.00,
'store_id' => 1,
'website_id' => 1,
- 'payment' => $payment
],
[
'increment_id' => '100000006',
@@ -71,7 +67,6 @@
'total_paid' => 160.00,
'store_id' => 1,
'website_id' => 1,
- 'payment' => $payment
],
];
@@ -79,6 +74,8 @@
$orderRepository = $objectManager->create(OrderRepositoryInterface::class);
/** @var array $orderData */
foreach ($orders as $orderData) {
+ $newPayment = clone $payment;
+ $newPayment->setId(null);
/** @var $order \Magento\Sales\Model\Order */
$order = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
\Magento\Sales\Model\Order::class
@@ -108,7 +105,8 @@
->setCustomerId(1)
->setCustomerEmail('customer@example.com')
->setBillingAddress($billingAddress)
- ->setShippingAddress($shippingAddress);
+ ->setShippingAddress($shippingAddress)
+ ->setPayment($newPayment);
$orderRepository->save($order);
}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Account/LinkTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Account/LinkTest.php
new file mode 100644
index 0000000000000..f4d66b2ff8cde
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Account/LinkTest.php
@@ -0,0 +1,79 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->page = $this->objectManager->create(Page::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testNewsletterLink(): void
+ {
+ $this->preparePage();
+ $block = $this->page->getLayout()->getBlock('customer-account-navigation-wish-list-link');
+ $this->assertNotFalse($block);
+ $html = $block->toHtml();
+ $this->assertContains('wishlist/', $html);
+ $this->assertEquals('My Wish List', strip_tags($html));
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/general/active 0
+ *
+ * @return void
+ */
+ public function testNewsletterLinkDisabled(): void
+ {
+ $this->preparePage();
+ $block = $this->page->getLayout()->getBlock('customer-account-navigation-wish-list-link');
+ $this->assertFalse($block);
+ }
+
+ /**
+ * Prepare page before render
+ *
+ * @return void
+ */
+ private function preparePage(): void
+ {
+ $this->page->addHandle([
+ 'default',
+ 'customer_account',
+ ]);
+ $this->page->getLayout()->generateXml();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/ProductList/Item/AddTo/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/ProductList/Item/AddTo/WishlistTest.php
new file mode 100644
index 0000000000000..36bd4dd4f312e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/ProductList/Item/AddTo/WishlistTest.php
@@ -0,0 +1,69 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->block = $this->objectManager->get(LayoutInterface::class)
+ ->createBlock(Wishlist::class)->setTemplate('Magento_Wishlist::catalog/product/list/addto/wishlist.phtml');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
+ *
+ * @return void
+ */
+ public function testAddToWishListVisible(): void
+ {
+ $product = $this->productRepository->get('simple2');
+ $html = $this->block->setProduct($product)->toHtml();
+ $this->assertEquals('Add to Wish List', trim(strip_tags($html)));
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/general/active 0
+ * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
+ *
+ * @return void
+ */
+ public function testAddToWishListNotVisible(): void
+ {
+ $product = $this->productRepository->get('simple2');
+ $this->assertEmpty($this->block->setProduct($product)->toHtml());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/View/AddTo/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/View/AddTo/WishlistTest.php
new file mode 100644
index 0000000000000..8d0226bfe9a2b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Catalog/Product/View/AddTo/WishlistTest.php
@@ -0,0 +1,108 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->block = $this->objectManager->get(LayoutInterface::class)
+ ->createBlock(Wishlist::class)->setTemplate('Magento_Wishlist::catalog/product/view/addto/wishlist.phtml');
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('product');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
+ *
+ * @return void
+ */
+ public function testAddToWishListVisible(): void
+ {
+ $product = $this->productRepository->get('simple2');
+ $this->registerProduct($product);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(self::ADD_TO_WISHLIST_XPATH, $this->block->toHtml())
+ );
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/general/active 0
+ * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
+ *
+ * @return void
+ */
+ public function testAddToWishListNotVisible(): void
+ {
+ $product = $this->productRepository->get('simple2');
+ $this->registerProduct($product);
+ $this->assertEquals(
+ 0,
+ Xpath::getElementsCountForXpath(self::ADD_TO_WISHLIST_XPATH, $this->block->toHtml())
+ );
+ }
+
+ /**
+ * Register the product.
+ *
+ * @param ProductInterface $product
+ * @return void
+ */
+ private function registerProduct(ProductInterface $product): void
+ {
+ $this->registry->unregister('product');
+ $this->registry->register('product', $product);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SidebarTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SidebarTest.php
new file mode 100644
index 0000000000000..6e6e88ab73019
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/SidebarTest.php
@@ -0,0 +1,94 @@
+objectManager = Bootstrap::getObjectManager();
+ $productMetadataInterface = $this->objectManager->get(ProductMetadataInterface::class);
+ if ($productMetadataInterface->getEdition() !== ProductMetadata::EDITION_NAME) {
+ $this->markTestSkipped('Skipped, because this logic is rewritten on EE.');
+ }
+ $this->page = $this->objectManager->create(Page::class);
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/general/show_in_sidebar 1
+ *
+ * @return void
+ */
+ public function testSidebarWishListVisible(): void
+ {
+ $this->preparePageLayout();
+ $block = $this->page->getLayout()->getBlock(self::BLOCK_NAME);
+ $this->assertNotFalse($block);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ "//div[contains(@class, 'block-wishlist')]//strong[contains(text(), 'My Wish List')]",
+ $block->toHtml()
+ )
+ );
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/general/show_in_sidebar 0
+ *
+ * @return void
+ */
+ public function testSidebarWishListNotVisible(): void
+ {
+ $this->preparePageLayout();
+ $this->assertFalse(
+ $this->page->getLayout()->getBlock(self::BLOCK_NAME),
+ 'Sidebar wish list should not be visible.'
+ );
+ }
+
+ /**
+ * Prepare category page.
+ *
+ * @return void
+ */
+ private function preparePageLayout(): void
+ {
+ $this->page->addHandle([
+ 'default',
+ 'catalog_category_view',
+ ]);
+ $this->page->getLayout()->generateXml();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php
new file mode 100644
index 0000000000000..944d2ac6faada
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/Customer/WishlistTest.php
@@ -0,0 +1,108 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->page = $this->objectManager->create(Page::class);
+ $this->customerSession = $this->objectManager->get(Session::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 0
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php
+ *
+ * @return void
+ */
+ public function testDisplayNumberOfItemsInWishList(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 1), $this->getWishListPagerBlockHtml())
+ );
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/wishlist_link/use_qty 1
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_product_qty_three.php
+ *
+ * @return void
+ */
+ public function testDisplayItemQuantitiesInWishList(): void
+ {
+ $this->markTestSkipped('Test is blocked by issue MC-31595');
+ $this->customerSession->setCustomerId(1);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf(self::ITEMS_COUNT_XPATH, 3), $this->getWishListPagerBlockHtml())
+ );
+ }
+
+ /**
+ * Get wish list pager block html.
+ *
+ * @return string
+ */
+ private function getWishListPagerBlockHtml(): string
+ {
+ $this->page->addHandle([
+ 'default',
+ 'wishlist_index_index',
+ ]);
+ $this->page->getLayout()->generateXml();
+ /** @var Wishlist $customerWishlistBlock */
+ $customerWishlistBlock = $this->page->getLayout()->getBlock('customer.wishlist');
+
+ return $customerWishlistBlock->getChildBlock('wishlist_item_pager')->toHtml();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Block/LinkTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Block/LinkTest.php
new file mode 100644
index 0000000000000..b7f124f7c1f6d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Block/LinkTest.php
@@ -0,0 +1,56 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Link::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testWishListLinkVisible(): void
+ {
+ $this->assertContains('My Wish List', strip_tags($this->block->toHtml()));
+ }
+
+ /**
+ * @magentoConfigFixture current_store wishlist/general/active 0
+ *
+ * @return void
+ */
+ public function testWishListLinkNotVisible(): void
+ {
+ $this->assertEmpty($this->block->toHtml());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three.php
new file mode 100644
index 0000000000000..11752d5ba39f3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three.php
@@ -0,0 +1,18 @@
+get(SerializerInterface::class);
+$wishlistFactory = $objectManager->get(WishlistFactory::class);
+$wishlist = $wishlistFactory->create();
+$wishlist->loadByCustomerId($customer->getId(), true);
+$wishlist->addNewItem($product, $json->serialize(['qty' => 3]));
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three_rollback.php
new file mode 100644
index 0000000000000..24bbccd5739f4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_product_qty_three_rollback.php
@@ -0,0 +1,9 @@
+_getDependencies($module, self::TYPE_HARD, self::MAP_TYPE_REDUNDANT);
+ if (isset(self::$redundantDependenciesWhitelist[$module])) {
+ $redundant = array_diff($redundant, self::$redundantDependenciesWhitelist[$module]);
+ }
if (!empty($redundant)) {
$result[] = sprintf(
"\r\nModule %s: %s [%s]",
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php
index 242e4ebb22a54..48eb64ffea27e 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/copyright/blacklist.php
@@ -10,4 +10,5 @@
'/setup\/src\/Zend\/Mvc\/Controller\/LazyControllerAbstractFactory\.php/',
'/app\/code\/(?!Magento)[^\/]*/',
'#dev/tests/setup-integration/testsuite/Magento/Developer/_files/\S*\.xml$#',
+ '/lib\/internal\/Magento\/Framework\/File\/Test\/Unit\/_files\/blank.html$/'
];
diff --git a/lib/internal/Magento/Framework/File/Mime.php b/lib/internal/Magento/Framework/File/Mime.php
index ed370b1beae54..148f43d47cfd4 100644
--- a/lib/internal/Magento/Framework/File/Mime.php
+++ b/lib/internal/Magento/Framework/File/Mime.php
@@ -78,6 +78,18 @@ class Mime
'svg' => 'image/svg+xml',
];
+ /**
+ * List of generic MIME types
+ *
+ * The file mime type should be detected by the file's extension if the native mime type is one of the listed below.
+ *
+ * @var array
+ */
+ private $genericMimeTypes = [
+ 'application/x-empty',
+ 'inode/x-empty',
+ ];
+
/**
* Get mime type of a file
*
@@ -120,7 +132,11 @@ private function getNativeMimeType(string $file): string
$extension = $this->getFileExtension($file);
$result = mime_content_type($file);
if (isset($this->mimeTypes[$extension], $this->defineByExtensionList[$extension])
- && (strpos($result, 'text/') === 0 || strpos($result, 'image/svg') === 0)
+ && (
+ strpos($result, 'text/') === 0
+ || strpos($result, 'image/svg') === 0
+ || in_array($result, $this->genericMimeTypes, true)
+ )
) {
$result = $this->mimeTypes[$extension];
}
diff --git a/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php b/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php
index 1a964c141dd34..0ae1542a2c0e1 100644
--- a/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php
+++ b/lib/internal/Magento/Framework/File/Test/Unit/MimeTest.php
@@ -5,6 +5,9 @@
*/
namespace Magento\Framework\File\Test\Unit;
+/**
+ * Test mime type utility for correct
+ */
class MimeTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -12,6 +15,9 @@ class MimeTest extends \PHPUnit\Framework\TestCase
*/
private $object;
+ /**
+ * @inheritDoc
+ */
protected function setUp()
{
$this->object = new \Magento\Framework\File\Mime();
@@ -42,12 +48,13 @@ public function testGetMimeType($file, $expectedType)
/**
* @return array
*/
- public function getMimeTypeDataProvider()
+ public function getMimeTypeDataProvider(): array
{
return [
'javascript' => [__DIR__ . '/_files/javascript.js', 'application/javascript'],
'weird extension' => [__DIR__ . '/_files/file.weird', 'application/octet-stream'],
'weird uppercase extension' => [__DIR__ . '/_files/UPPERCASE.WEIRD', 'application/octet-stream'],
+ 'generic mime type' => [__DIR__ . '/_files/blank.html', 'text/html'],
];
}
}
diff --git a/lib/internal/Magento/Framework/File/Test/Unit/_files/blank.html b/lib/internal/Magento/Framework/File/Test/Unit/_files/blank.html
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php
index caa080c02e255..df236faf8173b 100644
--- a/lib/internal/Magento/Framework/Image/Adapter/Gd2.php
+++ b/lib/internal/Magento/Framework/Image/Adapter/Gd2.php
@@ -75,16 +75,6 @@ public function open($filename)
$this->_getCallback('create', null, sprintf('Unsupported image format. File: %s', $this->_fileName)),
$this->_fileName
);
- $fileType = $this->getImageType();
- if (in_array($fileType, [IMAGETYPE_PNG, IMAGETYPE_GIF])) {
- $this->_keepTransparency = true;
- if ($this->_imageHandler) {
- $isAlpha = $this->checkAlpha($this->_fileName);
- if ($isAlpha) {
- $this->_fillBackgroundColor($this->_imageHandler);
- }
- }
- }
}
/**