diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml
index 9716791985970..36347e59a3d6b 100644
--- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml
+++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteABundleProductTest.xml
@@ -68,14 +68,9 @@
-
-
-
-
-
-
+
diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml
index ef84ebd6fafea..24ac3db27175c 100644
--- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml
+++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml
@@ -118,12 +118,7 @@
-
-
-
-
-
-
+
diff --git a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php
index aeef8d00f720a..a3ae616dfcf4f 100644
--- a/app/code/Magento/CacheInvalidate/Model/PurgeCache.php
+++ b/app/code/Magento/CacheInvalidate/Model/PurgeCache.php
@@ -6,21 +6,24 @@
namespace Magento\CacheInvalidate\Model;
use Magento\Framework\Cache\InvalidateLogger;
+use Magento\PageCache\Model\Cache\Server;
+use Laminas\Http\Client\Adapter\Socket;
+use Laminas\Uri\Uri;
/**
- * PurgeCache model
+ * Invalidate external HTTP cache(s) based on tag pattern
*/
class PurgeCache
{
const HEADER_X_MAGENTO_TAGS_PATTERN = 'X-Magento-Tags-Pattern';
/**
- * @var \Magento\PageCache\Model\Cache\Server
+ * @var Server
*/
protected $cacheServer;
/**
- * @var \Magento\CacheInvalidate\Model\SocketFactory
+ * @var SocketFactory
*/
protected $socketAdapterFactory;
@@ -39,39 +42,46 @@ class PurgeCache
*
* @var int
*/
- private $requestSize = 7680;
+ private $maxHeaderSize;
/**
* Constructor
*
- * @param \Magento\PageCache\Model\Cache\Server $cacheServer
- * @param \Magento\CacheInvalidate\Model\SocketFactory $socketAdapterFactory
+ * @param Server $cacheServer
+ * @param SocketFactory $socketAdapterFactory
* @param InvalidateLogger $logger
+ * @param int $maxHeaderSize
*/
public function __construct(
- \Magento\PageCache\Model\Cache\Server $cacheServer,
- \Magento\CacheInvalidate\Model\SocketFactory $socketAdapterFactory,
- InvalidateLogger $logger
+ Server $cacheServer,
+ SocketFactory $socketAdapterFactory,
+ InvalidateLogger $logger,
+ int $maxHeaderSize = 7680
) {
$this->cacheServer = $cacheServer;
$this->socketAdapterFactory = $socketAdapterFactory;
$this->logger = $logger;
+ $this->maxHeaderSize = $maxHeaderSize;
}
/**
* Send curl purge request to invalidate cache by tags pattern
*
- * @param string $tagsPattern
+ * @param array|string $tags
* @return bool Return true if successful; otherwise return false
*/
- public function sendPurgeRequest($tagsPattern)
+ public function sendPurgeRequest($tags)
{
+ if (is_string($tags)) {
+ $tags = [$tags];
+ }
+
$successful = true;
$socketAdapter = $this->socketAdapterFactory->create();
$servers = $this->cacheServer->getUris();
$socketAdapter->setOptions(['timeout' => 10]);
- $formattedTagsChunks = $this->splitTags($tagsPattern);
+ $formattedTagsChunks = $this->chunkTags($tags);
foreach ($formattedTagsChunks as $formattedTagsChunk) {
if (!$this->sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)) {
$successful = false;
@@ -82,24 +92,24 @@ public function sendPurgeRequest($tagsPattern)
}
/**
- * Split tags by batches
+ * Split tags into batches to suit Varnish max. header size
*
- * @param string $tagsPattern
+ * @param array $tags
* @return \Generator
*/
- private function splitTags($tagsPattern)
+ private function chunkTags(array $tags): \Generator
{
- $tagsBatchSize = 0;
+ $currentBatchSize = 0;
$formattedTagsChunk = [];
- $formattedTags = explode('|', $tagsPattern);
- foreach ($formattedTags as $formattedTag) {
- if ($tagsBatchSize + strlen($formattedTag) > $this->requestSize - count($formattedTagsChunk) - 1) {
+ foreach ($tags as $formattedTag) {
+ // Check if (currentBatchSize + length of next tag + number of pipe delimiters) would exceed header size.
+ if ($currentBatchSize + strlen($formattedTag) + count($formattedTagsChunk) > $this->maxHeaderSize) {
yield implode('|', $formattedTagsChunk);
$formattedTagsChunk = [];
- $tagsBatchSize = 0;
+ $currentBatchSize = 0;
}
- $tagsBatchSize += strlen($formattedTag);
+ $currentBatchSize += strlen($formattedTag);
$formattedTagsChunk[] = $formattedTag;
}
if (!empty($formattedTagsChunk)) {
@@ -110,12 +120,12 @@ private function splitTags($tagsPattern)
/**
* Send curl purge request to servers to invalidate cache by tags pattern
*
- * @param \Laminas\Http\Client\Adapter\Socket $socketAdapter
- * @param \Laminas\Uri\Uri[] $servers
+ * @param Socket $socketAdapter
+ * @param Uri[] $servers
* @param string $formattedTagsChunk
* @return bool Return true if successful; otherwise return false
*/
- private function sendPurgeRequestToServers($socketAdapter, $servers, $formattedTagsChunk)
+ private function sendPurgeRequestToServers(Socket $socketAdapter, array $servers, string $formattedTagsChunk): bool
{
$headers = [self::HEADER_X_MAGENTO_TAGS_PATTERN => $formattedTagsChunk];
$unresponsiveServerError = [];
@@ -145,14 +155,14 @@ private function sendPurgeRequestToServers($socketAdapter, $servers, $formattedT
if ($errorCount == count($servers)) {
$this->logger->critical(
'No cache server(s) could be purged ' . $loggerMessage,
- compact('server', 'formattedTagsChunk')
+ compact('servers', 'formattedTagsChunk')
);
return false;
}
$this->logger->warning(
'Unresponsive cache server(s) hit' . $loggerMessage,
- compact('server', 'formattedTagsChunk')
+ compact('servers', 'formattedTagsChunk')
);
}
diff --git a/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php b/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php
index 97e11e8bd2f3f..574263ca1ee0c 100644
--- a/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php
+++ b/app/code/Magento/CacheInvalidate/Observer/FlushAllCacheObserver.php
@@ -7,6 +7,9 @@
use Magento\Framework\Event\ObserverInterface;
+/**
+ * Clear configured Varnish hosts when triggering a full cache flush (e.g. from the Cache Management admin dashboard)
+ */
class FlushAllCacheObserver implements ObserverInterface
{
/**
@@ -43,7 +46,7 @@ public function __construct(
public function execute(\Magento\Framework\Event\Observer $observer)
{
if ($this->config->getType() == \Magento\PageCache\Model\Config::VARNISH && $this->config->isEnabled()) {
- $this->purgeCache->sendPurgeRequest('.*');
+ $this->purgeCache->sendPurgeRequest(['.*']);
}
}
}
diff --git a/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php b/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php
index a3062ea9d6a76..7dba4393d3973 100644
--- a/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php
+++ b/app/code/Magento/CacheInvalidate/Observer/InvalidateVarnishObserver.php
@@ -77,7 +77,7 @@ public function execute(Observer $observer)
$tags[] = sprintf($pattern, $tag);
}
if (!empty($tags)) {
- $this->purgeCache->sendPurgeRequest(implode('|', array_unique($tags)));
+ $this->purgeCache->sendPurgeRequest(array_unique($tags));
}
}
}
diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php
index c88d8f5266fb8..97c3acc194842 100644
--- a/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php
+++ b/app/code/Magento/CacheInvalidate/Test/Unit/Model/PurgeCacheTest.php
@@ -53,6 +53,7 @@ protected function setUp(): void
'cacheServer' => $this->cacheServer,
'socketAdapterFactory' => $socketFactoryMock,
'logger' => $this->loggerMock,
+ 'maxHeaderSize' => 256
]
);
}
@@ -64,6 +65,7 @@ protected function setUp(): void
public function testSendPurgeRequest($hosts)
{
$uris = [];
+ /** @var array $host */
foreach ($hosts as $host) {
$port = isset($host['port']) ? $host['port'] : Server::DEFAULT_PORT;
$uris[] = UriFactory::factory('')->setHost($host['host'])
@@ -92,7 +94,50 @@ public function testSendPurgeRequest($hosts)
$this->loggerMock->expects($this->once())
->method('execute');
- $this->assertTrue($this->model->sendPurgeRequest('tags'));
+ $this->assertTrue($this->model->sendPurgeRequest(['tags']));
+ }
+
+ public function testSendMultiPurgeRequest()
+ {
+ $tags = [
+ '(^|,)cat_p_95(,|$)', '(^|,)cat_p_96(,|$)', '(^|,)cat_p_97(,|$)', '(^|,)cat_p_98(,|$)',
+ '(^|,)cat_p_99(,|$)', '(^|,)cat_p_100(,|$)', '(^|,)cat_p_10038(,|$)', '(^|,)cat_p_142985(,|$)',
+ '(^|,)cat_p_199(,|$)', '(^|,)cat_p_300(,|$)', '(^|,)cat_p_12038(,|$)', '(^|,)cat_p_152985(,|$)',
+ '(^|,)cat_p_299(,|$)', '(^|,)cat_p_400(,|$)', '(^|,)cat_p_13038(,|$)', '(^|,)cat_p_162985(,|$)',
+ ];
+
+ $tagsSplitA = array_slice($tags, 0, 12);
+ $tagsSplitB = array_slice($tags, 12, 4);
+
+ $uri = UriFactory::factory('')->setHost('localhost')
+ ->setPort(80)
+ ->setScheme('http');
+
+ $this->cacheServer->expects($this->once())
+ ->method('getUris')
+ ->willReturn([$uri]);
+
+ $this->socketAdapterMock->expects($this->exactly(2))
+ ->method('connect')
+ ->with($uri->getHost(), $uri->getPort());
+
+ $this->socketAdapterMock->expects($this->exactly(2))
+ ->method('write')
+ ->withConsecutive(
+ [
+ 'PURGE', $uri, '1.1',
+ ['X-Magento-Tags-Pattern' => implode('|', $tagsSplitA), 'Host' => $uri->getHost()]
+ ],
+ [
+ 'PURGE', $uri, '1.1',
+ ['X-Magento-Tags-Pattern' => implode('|', $tagsSplitB), 'Host' => $uri->getHost()]
+ ]
+ );
+
+ $this->socketAdapterMock->expects($this->exactly(2))
+ ->method('close');
+
+ $this->assertTrue($this->model->sendPurgeRequest($tags));
}
/**
@@ -130,6 +175,6 @@ public function testSendPurgeRequestWithException()
$this->loggerMock->expects($this->once())
->method('critical');
- $this->assertFalse($this->model->sendPurgeRequest('tags'));
+ $this->assertFalse($this->model->sendPurgeRequest(['tags']));
}
}
diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php
index 3db36e4c0e3e9..544e23288d720 100644
--- a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php
+++ b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/FlushAllCacheObserverTest.php
@@ -56,7 +56,7 @@ public function testFlushAllCache()
Config::VARNISH
);
- $this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with('.*');
+ $this->purgeCache->expects($this->once())->method('sendPurgeRequest')->with(['.*']);
$this->model->execute($this->observerMock);
}
}
diff --git a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php
index d550cd7ea9487..38319dcc724ca 100644
--- a/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php
+++ b/app/code/Magento/CacheInvalidate/Test/Unit/Observer/InvalidateVarnishObserverTest.php
@@ -82,7 +82,7 @@ protected function setUp(): void
public function testInvalidateVarnish()
{
$tags = ['cache_1', 'cache_group'];
- $pattern = '((^|,)cache_1(,|$))|((^|,)cache_group(,|$))';
+ $pattern = ['((^|,)cache_1(,|$))', '((^|,)cache_group(,|$))'];
$this->configMock->expects($this->once())->method('isEnabled')->willReturn(true);
$this->configMock->expects(
diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductsFromGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductsFromGridActionGroup.xml
new file mode 100644
index 0000000000000..d5c4f97c88da2
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteAllProductsFromGridActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Select and delete products in product grid.
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml
index 0565f2d08cc1f..fea4436446da2 100644
--- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml
+++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml
@@ -18,19 +18,24 @@
+
-
+
+
-
+
+
-
+
+
-
+
+
-
+
diff --git a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php
index bf1d3772b92a0..1ad82497119ba 100644
--- a/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php
+++ b/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php
@@ -8,12 +8,14 @@
namespace Magento\CatalogImportExport\Test\Unit\Model\Export;
use Magento\Catalog\Model\Product\LinkTypeProvider;
+use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\CatalogImportExport\Model\Export\Product;
use Magento\CatalogImportExport\Model\Export\Product\Type\Factory;
use Magento\CatalogImportExport\Model\Export\RowCustomizer\Composite;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
use Magento\Eav\Model\Entity\Type;
+use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Logger\Monolog;
@@ -83,7 +85,7 @@ class ProductTest extends TestCase
protected $attrSetColFactory;
/**
- * @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory|MockObject
+ * @var CategoryCollectionFactory|MockObject
*/
protected $categoryColFactory;
@@ -174,15 +176,14 @@ protected function setUp(): void
->onlyMethods(['create'])
->getMock();
- $this->attrSetColFactory = $this->getMockBuilder(
- \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory::class
- )->addMethods(['setEntityTypeFilter'])
+ $this->attrSetColFactory = $this->getMockBuilder(AttributeSetCollectionFactory::class)
+ ->disableOriginalConstructor()
+ ->addMethods(['setEntityTypeFilter'])
->onlyMethods(['create'])
->getMock();
- $this->categoryColFactory = $this->getMockBuilder(
- \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory::class
- )->addMethods(['addNameToResult'])
+ $this->categoryColFactory = $this->getMockBuilder(CategoryCollectionFactory::class)
+ ->disableOriginalConstructor()->addMethods(['addNameToResult'])
->onlyMethods(['create'])
->getMock();
diff --git a/app/code/Magento/CatalogInventory/Model/StockState.php b/app/code/Magento/CatalogInventory/Model/StockState.php
index ac6dc16366798..e28a2096942d4 100644
--- a/app/code/Magento/CatalogInventory/Model/StockState.php
+++ b/app/code/Magento/CatalogInventory/Model/StockState.php
@@ -9,8 +9,11 @@
use Magento\CatalogInventory\Api\StockStateInterface;
use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface;
+use Magento\Framework\DataObject;
/**
+ * Provides functionality for stock state information
+ *
* Interface StockState
*/
class StockState implements StockStateInterface
@@ -31,6 +34,8 @@ class StockState implements StockStateInterface
protected $stockConfiguration;
/**
+ * StockState constructor
+ *
* @param StockStateProviderInterface $stockStateProvider
* @param StockRegistryProviderInterface $stockRegistryProvider
* @param StockConfigurationInterface $stockConfiguration
@@ -46,30 +51,32 @@ public function __construct(
}
/**
+ * Verify stock by product id
+ *
* @param int $productId
* @param int $scopeId
* @return bool
*/
public function verifyStock($productId, $scopeId = null)
{
- // if ($scopeId === null) {
- $scopeId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $scopeId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
+
return $this->stockStateProvider->verifyStock($stockItem);
}
/**
+ * Verify notification by product id
+ *
* @param int $productId
* @param int $scopeId
* @return bool
*/
public function verifyNotification($productId, $scopeId = null)
{
- // if ($scopeId === null) {
- $scopeId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $scopeId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
+
return $this->stockStateProvider->verifyNotification($stockItem);
}
@@ -84,16 +91,14 @@ public function verifyNotification($productId, $scopeId = null)
*/
public function checkQty($productId, $qty, $scopeId = null)
{
- // if ($scopeId === null) {
- $scopeId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $scopeId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
+
return $this->stockStateProvider->checkQty($stockItem, $qty);
}
/**
- * Returns suggested qty that satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions
- * or original qty if such value does not exist
+ * Returns suggested qty that satisfies qty increments/minQty/maxQty/minSaleQty/maxSaleQty else returns original qty
*
* @param int $productId
* @param float $qty
@@ -102,10 +107,9 @@ public function checkQty($productId, $qty, $scopeId = null)
*/
public function suggestQty($productId, $qty, $scopeId = null)
{
- // if ($scopeId === null) {
- $scopeId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $scopeId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
+
return $this->stockStateProvider->suggestQty($stockItem, $qty);
}
@@ -118,29 +122,31 @@ public function suggestQty($productId, $qty, $scopeId = null)
*/
public function getStockQty($productId, $scopeId = null)
{
- // if ($scopeId === null) {
- $scopeId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $scopeId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
+
return $this->stockStateProvider->getStockQty($stockItem);
}
/**
+ * Check qty increments by product id
+ *
* @param int $productId
* @param float $qty
* @param int $websiteId
- * @return \Magento\Framework\DataObject
+ * @return DataObject
*/
public function checkQtyIncrements($productId, $qty, $websiteId = null)
{
- // if ($websiteId === null) {
- $websiteId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $websiteId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId);
+
return $this->stockStateProvider->checkQtyIncrements($stockItem, $qty);
}
/**
+ * Check quote item qty
+ *
* @param int $productId
* @param float $itemQty
* @param float $qtyToCheck
@@ -150,10 +156,9 @@ public function checkQtyIncrements($productId, $qty, $websiteId = null)
*/
public function checkQuoteItemQty($productId, $itemQty, $qtyToCheck, $origQty, $scopeId = null)
{
- // if ($scopeId === null) {
- $scopeId = $this->stockConfiguration->getDefaultScopeId();
- // }
+ $scopeId = $this->stockConfiguration->getDefaultScopeId();
$stockItem = $this->stockRegistryProvider->getStockItem($productId, $scopeId);
+
return $this->stockStateProvider->checkQuoteItemQty($stockItem, $itemQty, $qtyToCheck, $origQty);
}
}
diff --git a/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php b/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php
index 7fa0fbfa44b0a..0e918c23857ac 100644
--- a/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php
+++ b/app/code/Magento/Config/Block/System/Config/Form/Fieldset.php
@@ -96,6 +96,8 @@ protected function _getChildrenElementsHtml(AbstractElement $element)
. '
' . $field->toHtml() . ' | ';
} else {
$elements .= $field->toHtml();
+ $styleTag = $this->addVisibilityTag($field);
+ $elements .= $styleTag;
}
}
@@ -168,11 +170,13 @@ protected function _getFrontendClass($element)
*/
protected function _getHeaderTitleHtml($element)
{
+ $styleTag = $this->addVisibilityTag($element);
return '' . $element->getLegend() . '' .
+ $styleTag .
/* @noEscape */ $this->secureRenderer->renderEventListenerAsTag(
'onclick',
'event.preventDefault();' .
@@ -270,10 +274,70 @@ protected function _isCollapseState($element)
return true;
}
+ if ($this->isCollapseStateByDependentField($element)) {
+ return false;
+ }
+
$extra = $this->_authSession->getUser()->getExtra();
+
if (isset($extra['configState'][$element->getId()])) {
return $extra['configState'][$element->getId()];
}
return $this->isCollapsedDefault;
}
+
+ /**
+ * Check if element should be collapsed by dependent field value.
+ *
+ * @param AbstractElement $element
+ * @return bool
+ */
+ private function isCollapseStateByDependentField(AbstractElement $element): bool
+ {
+ if (!empty($element->getGroup()['depends']['fields'])) {
+ foreach ($element->getGroup()['depends']['fields'] as $dependFieldData) {
+ if (is_array($dependFieldData) && isset($dependFieldData['value'], $dependFieldData['id'])) {
+ $fieldSetForm = $this->getForm();
+ $dependentFieldConfigValue = $this->_scopeConfig->getValue(
+ $dependFieldData['id'],
+ $fieldSetForm->getScope(),
+ $fieldSetForm->getScopeCode()
+ );
+
+ if ($dependFieldData['value'] !== $dependentFieldConfigValue) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * If element or it's parent depends on other element we hide it during page load.
+ *
+ * @param AbstractElement $field
+ * @return string
+ */
+ private function addVisibilityTag(AbstractElement $field): string
+ {
+ $elementId = '';
+ $styleTag = '';
+
+ if (!empty($field->getFieldConfig()['depends']['fields'])) {
+ $elementId = '#row_' . $field->getHtmlId();
+ } elseif (!empty($field->getGroup()['depends']['fields'])) {
+ $elementId = '#' . $field->getHtmlId() . '-head';
+ }
+
+ if (!empty($elementId)) {
+ $styleTag .= $this->secureRenderer->renderStyleAsTag(
+ 'display: none;',
+ $elementId
+ );
+ }
+
+ return $styleTag;
+ }
}
diff --git a/app/code/Magento/Customer/Block/Account/Navigation.php b/app/code/Magento/Customer/Block/Account/Navigation.php
index dcbd637ac360e..9a8aa698eaa47 100644
--- a/app/code/Magento/Customer/Block/Account/Navigation.php
+++ b/app/code/Magento/Customer/Block/Account/Navigation.php
@@ -47,6 +47,6 @@ public function getLinks()
*/
private function compare(SortLinkInterface $firstLink, SortLinkInterface $secondLink): int
{
- return $firstLink->getSortOrder() <=> $secondLink->getSortOrder();
+ return $secondLink->getSortOrder() <=> $firstLink->getSortOrder();
}
}
diff --git a/app/code/Magento/Customer/Model/Options.php b/app/code/Magento/Customer/Model/Options.php
index c230353f2a284..4c9b9f97ad43a 100644
--- a/app/code/Magento/Customer/Model/Options.php
+++ b/app/code/Magento/Customer/Model/Options.php
@@ -97,14 +97,15 @@ private function prepareNamePrefixSuffixOptions($options, $isOptional = false)
if (empty($options)) {
return false;
}
+
$result = [];
- $options = array_filter(explode(';', $options));
+ $options = explode(';', $options);
foreach ($options as $value) {
- $value = $this->escaper->escapeHtml(trim($value));
- $result[$value] = $value;
+ $result[] = $this->escaper->escapeHtml(trim($value)) ?: ' ';
}
+
if ($isOptional && trim(current($options))) {
- $result = array_merge([' ' => ' '], $result);
+ $result = array_merge([' '], $result);
}
return $result;
diff --git a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php
index 0dc375908e561..c93e7110c5d96 100644
--- a/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Block/Account/NavigationTest.php
@@ -18,11 +18,6 @@
class NavigationTest extends TestCase
{
- /**
- * Stub name top links
- */
- private const STUB_TOP_LINKS_NAME_IN_LAYOUT = 'top.links';
-
/**
* @var ObjectManagerHelper
*/
@@ -67,7 +62,7 @@ protected function setUp(): void
*
* @return void
*/
- public function testGetLinksWithCustomerAndWishList(): void
+ public function testGetLinksWithCustomerAndWishList()
{
$wishListLinkMock = $this->getMockBuilder(WishListLink::class)
->disableOriginalConstructor()
@@ -81,30 +76,30 @@ public function testGetLinksWithCustomerAndWishList(): void
$wishListLinkMock->expects($this->any())
->method('getSortOrder')
- ->willReturn(30);
+ ->willReturn(100);
$customerAccountLinkMock->expects($this->any())
->method('getSortOrder')
- ->willReturn(0);
+ ->willReturn(20);
- $topLinksNameInLayout = self::STUB_TOP_LINKS_NAME_IN_LAYOUT;
+ $nameInLayout = 'top.links';
$blockChildren = [
- 'customerAccountLink' => $customerAccountLinkMock,
- 'wishListLink' => $wishListLinkMock
+ 'wishListLink' => $wishListLinkMock,
+ 'customerAccountLink' => $customerAccountLinkMock
];
- $this->navigation->setNameInLayout($topLinksNameInLayout);
+ $this->navigation->setNameInLayout($nameInLayout);
$this->layoutMock->expects($this->any())
->method('getChildBlocks')
- ->with($topLinksNameInLayout)
+ ->with($nameInLayout)
->willReturn($blockChildren);
/* Assertion */
$this->assertEquals(
[
- 0 => $customerAccountLinkMock,
- 1 => $wishListLinkMock
+ 0 => $wishListLinkMock,
+ 1 => $customerAccountLinkMock
],
$this->navigation->getLinks()
);
diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account.xml
index 260f35e3cf444..a2a15a4166b73 100644
--- a/app/code/Magento/Customer/view/frontend/layout/customer_account.xml
+++ b/app/code/Magento/Customer/view/frontend/layout/customer_account.xml
@@ -24,31 +24,31 @@
My Account
customer/account
- 1
+ 250
- 40
+ 200
Address Book
customer/address
- 50
+ 190
Account Information
customer/account/edit
- 60
+ 180
- 90
+ 130
diff --git a/app/code/Magento/Customer/view/frontend/layout/default.xml b/app/code/Magento/Customer/view/frontend/layout/default.xml
index 3c6be7a9ee5fe..3976fc6bd9090 100644
--- a/app/code/Magento/Customer/view/frontend/layout/default.xml
+++ b/app/code/Magento/Customer/view/frontend/layout/default.xml
@@ -8,10 +8,10 @@
-
+
My Account
- 1
+ 110
@@ -20,11 +20,7 @@
-
- 30
-
-
+ template="Magento_Customer::account/link/authorization.phtml"/>
diff --git a/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php b/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php
index f0f4eba026733..547be1de5a008 100644
--- a/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php
+++ b/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php
@@ -13,6 +13,8 @@
/**
* Pop-up for Login as Customer button then Login as Customer is not allowed.
+ *
+ * @api
*/
class NotAllowedPopup extends Template
{
diff --git a/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php b/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php
index 95a0cadd3abcc..eb6bd2aad236c 100644
--- a/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php
+++ b/app/code/Magento/MediaGallery/Model/ResourceModel/Keyword/SaveAssetLinks.php
@@ -24,6 +24,7 @@ class SaveAssetLinks
private const TABLE_ASSET_KEYWORD = 'media_gallery_asset_keyword';
private const FIELD_ASSET_ID = 'asset_id';
private const FIELD_KEYWORD_ID = 'keyword_id';
+ private const TABLE_MEDIA_ASSET = 'media_gallery_asset';
/**
* @var ResourceConnection
@@ -73,6 +74,10 @@ public function execute(int $assetId, array $keywordIds): void
$this->deleteAssetKeywords($assetId, $obsoleteKeywordIds);
$this->insertAssetKeywords($assetId, $newKeywordIds);
+
+ if ($obsoleteKeywordIds || $newKeywordIds) {
+ $this->setAssetUpdatedAt($assetId);
+ }
}
/**
@@ -179,4 +184,28 @@ function (KeywordInterface $keyword): int {
$keywordsData
);
}
+
+ /**
+ * Updates modified date of media asset
+ *
+ * @param int $assetId
+ * @throws CouldNotSaveException
+ */
+ private function setAssetUpdatedAt(int $assetId): void
+ {
+ try {
+ $connection = $this->resourceConnection->getConnection();
+ $connection->update(
+ $connection->getTableName(self::TABLE_MEDIA_ASSET),
+ ['updated_at' => null],
+ ['id =?' => $assetId]
+ );
+ } catch (\Exception $exception) {
+ $this->logger->critical($exception);
+ throw new CouldNotSaveException(
+ __('Could not update assets modified date'),
+ $exception
+ );
+ }
+ }
}
diff --git a/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php b/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php
index 0bc81fbedaa19..d027f0ed21b53 100644
--- a/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php
+++ b/app/code/Magento/MediaGallery/Test/Unit/Model/ResourceModel/Keyword/SaveAssetLinksTest.php
@@ -75,7 +75,7 @@ public function testAssetKeywordsSave(int $assetId, array $keywordIds, array $va
$expectedCalls = (int) (count($keywordIds));
if ($expectedCalls) {
- $this->resourceConnectionMock->expects($this->once())
+ $this->resourceConnectionMock->expects($this->exactly(2))
->method('getConnection')
->willReturn($this->connectionMock);
$this->resourceConnectionMock->expects($this->once())
diff --git a/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php
index 38569f5f698da..f780a116baf9e 100644
--- a/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php
+++ b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Columns/Path.php
@@ -6,6 +6,8 @@
namespace Magento\MediaGalleryCatalogUi\Ui\Component\Listing\Columns;
use Magento\Catalog\Api\CategoryRepositoryInterface;
+use Magento\Catalog\Model\Category;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
@@ -63,13 +65,15 @@ public function prepareDataSource(array $dataSource)
* Replace category path ids with category names
*
* @param string $pathWithIds
+ * @return string
+ * @throws NoSuchEntityException
*/
private function getCategoryPathWithNames(string $pathWithIds): string
{
$categoryPathWithName = '';
$categoryIds = explode('/', $pathWithIds);
foreach ($categoryIds as $id) {
- if ($id == 1) {
+ if ($id == Category::TREE_ROOT_ID) {
continue;
}
$categoryName = $this->categoryRepository->get($id)->getName();
diff --git a/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Filters/UsedInProducts.php b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Filters/UsedInProducts.php
new file mode 100644
index 0000000000000..254ebd047c954
--- /dev/null
+++ b/app/code/Magento/MediaGalleryCatalogUi/Ui/Component/Listing/Filters/UsedInProducts.php
@@ -0,0 +1,133 @@
+uiComponentFactory = $uiComponentFactory;
+ $this->filterBuilder = $filterBuilder;
+ parent::__construct(
+ $context,
+ $uiComponentFactory,
+ $filterBuilder,
+ $filterModifier,
+ $optionsProvider,
+ $components,
+ $data
+ );
+ $this->bookmarkManagement = $bookmarkManagement;
+ $this->productRepository = $productRepository;
+ }
+
+ /**
+ * Prepare component configuration
+ *
+ * @return void
+ */
+ public function prepare()
+ {
+ $options = [];
+ $productIds = [];
+ $bookmarks = $this->bookmarkManagement->loadByNamespace($this->context->getNameSpace())->getItems();
+ foreach ($bookmarks as $bookmark) {
+ if ($bookmark->getIdentifier() === 'current') {
+ $applied = $bookmark->getConfig()['current']['filters']['applied'];
+ if (isset($applied[$this->getName()])) {
+ $productIds = $applied[$this->getName()];
+ }
+ }
+ }
+
+ foreach ($productIds as $id) {
+ $product = $this->productRepository->getById($id);
+ $options[] = [
+ 'value' => $id,
+ 'label' => $product->getName(),
+ 'is_active' => $product->getStatus(),
+ 'path' => $product->getSku(),
+ 'optgroup' => false
+
+ ];
+ }
+
+ $this->wrappedComponent = $this->uiComponentFactory->create(
+ $this->getName(),
+ parent::COMPONENT,
+ [
+ 'context' => $this->getContext(),
+ 'options' => $options
+ ]
+ );
+
+ $this->wrappedComponent->prepare();
+ $productsFilterJsConfig = array_replace_recursive(
+ $this->getJsConfig($this->wrappedComponent),
+ $this->getJsConfig($this)
+ );
+ $this->setData('js_config', $productsFilterJsConfig);
+
+ $this->setData(
+ 'config',
+ array_replace_recursive(
+ (array)$this->wrappedComponent->getData('config'),
+ (array)$this->getData('config')
+ )
+ );
+
+ $this->applyFilter();
+
+ parent::prepare();
+ }
+}
diff --git a/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml b/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml
index 500ac10f4745a..ae01c29928b4a 100644
--- a/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml
+++ b/app/code/Magento/MediaGalleryCatalogUi/etc/adminhtml/di.xml
@@ -24,7 +24,7 @@
catalog_category
-
+
-
diff --git a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml
index 97743b458e8d7..2ca58b6020fa7 100644
--- a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml
+++ b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_listing.xml
@@ -13,6 +13,7 @@
name="product_id"
provider="${ $.parentName }"
sortOrder="110"
+ class="Magento\MediaGalleryCatalogUi\Ui\Component\Listing\Filters\UsedInProducts"
component="Magento_Catalog/js/components/product-ui-select"
template="ui/grid/filters/elements/ui-select">
diff --git a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml
index 97743b458e8d7..2ca58b6020fa7 100644
--- a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml
+++ b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml
@@ -13,6 +13,7 @@
name="product_id"
provider="${ $.parentName }"
sortOrder="110"
+ class="Magento\MediaGalleryCatalogUi\Ui\Component\Listing\Filters\UsedInProducts"
component="Magento_Catalog/js/components/product-ui-select"
template="ui/grid/filters/elements/ui-select">
diff --git a/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInBlocks.php b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInBlocks.php
new file mode 100644
index 0000000000000..09fea24c8a2a9
--- /dev/null
+++ b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInBlocks.php
@@ -0,0 +1,131 @@
+uiComponentFactory = $uiComponentFactory;
+ $this->filterBuilder = $filterBuilder;
+ parent::__construct(
+ $context,
+ $uiComponentFactory,
+ $filterBuilder,
+ $filterModifier,
+ $optionsProvider,
+ $components,
+ $data
+ );
+ $this->bookmarkManagement = $bookmarkManagement;
+ $this->blockRepository = $blockRepository;
+ }
+
+ /**
+ * Prepare component configuration
+ *
+ * @return void
+ */
+ public function prepare()
+ {
+ $options = [];
+ $blockIds = [];
+ $bookmarks = $this->bookmarkManagement->loadByNamespace($this->context->getNameSpace())->getItems();
+ foreach ($bookmarks as $bookmark) {
+ if ($bookmark->getIdentifier() === 'current') {
+ $applied = $bookmark->getConfig()['current']['filters']['applied'];
+ if (isset($applied[$this->getName()])) {
+ $blockIds = $applied[$this->getName()];
+ }
+ }
+ }
+
+ foreach ($blockIds as $id) {
+ $block = $this->blockRepository->getById($id);
+ $options[] = [
+ 'value' => $id,
+ 'label' => $block->getTitle(),
+ 'is_active' => $block->isActive(),
+ 'optgroup' => false
+ ];
+ }
+
+ $this->wrappedComponent = $this->uiComponentFactory->create(
+ $this->getName(),
+ parent::COMPONENT,
+ [
+ 'context' => $this->getContext(),
+ 'options' => $options
+ ]
+ );
+
+ $this->wrappedComponent->prepare();
+ $jsConfig = array_replace_recursive(
+ $this->getJsConfig($this->wrappedComponent),
+ $this->getJsConfig($this)
+ );
+ $this->setData('js_config', $jsConfig);
+
+ $this->setData(
+ 'config',
+ array_replace_recursive(
+ (array)$this->wrappedComponent->getData('config'),
+ (array)$this->getData('config')
+ )
+ );
+
+ $this->applyFilter();
+
+ parent::prepare();
+ }
+}
diff --git a/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInPages.php b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInPages.php
new file mode 100644
index 0000000000000..235a77cdcb8c5
--- /dev/null
+++ b/app/code/Magento/MediaGalleryCmsUi/Ui/Component/Listing/Filters/UsedInPages.php
@@ -0,0 +1,131 @@
+uiComponentFactory = $uiComponentFactory;
+ $this->filterBuilder = $filterBuilder;
+ parent::__construct(
+ $context,
+ $uiComponentFactory,
+ $filterBuilder,
+ $filterModifier,
+ $optionsProvider,
+ $components,
+ $data
+ );
+ $this->bookmarkManagement = $bookmarkManagement;
+ $this->pageRepository = $pageRepository;
+ }
+
+ /**
+ * Prepare component configuration
+ *
+ * @return void
+ */
+ public function prepare()
+ {
+ $options = [];
+ $pageIds = [];
+ $bookmarks = $this->bookmarkManagement->loadByNamespace($this->context->getNameSpace())->getItems();
+ foreach ($bookmarks as $bookmark) {
+ if ($bookmark->getIdentifier() === 'current') {
+ $applied = $bookmark->getConfig()['current']['filters']['applied'];
+ if (isset($applied[$this->getName()])) {
+ $pageIds = $applied[$this->getName()];
+ }
+ }
+ }
+
+ foreach ($pageIds as $id) {
+ $page = $this->pageRepository->getById($id);
+ $options[] = [
+ 'value' => $id,
+ 'label' => $page->getTitle(),
+ 'is_active' => $page->isActive(),
+ 'optgroup' => false
+ ];
+ }
+
+ $this->wrappedComponent = $this->uiComponentFactory->create(
+ $this->getName(),
+ parent::COMPONENT,
+ [
+ 'context' => $this->getContext(),
+ 'options' => $options
+ ]
+ );
+
+ $this->wrappedComponent->prepare();
+ $pagesFilterjsConfig = array_replace_recursive(
+ $this->getJsConfig($this->wrappedComponent),
+ $this->getJsConfig($this)
+ );
+ $this->setData('js_config', $pagesFilterjsConfig);
+
+ $this->setData(
+ 'config',
+ array_replace_recursive(
+ (array)$this->wrappedComponent->getData('config'),
+ (array)$this->getData('config')
+ )
+ );
+
+ $this->applyFilter();
+
+ parent::prepare();
+ }
+}
diff --git a/app/code/Magento/MediaGalleryCmsUi/composer.json b/app/code/Magento/MediaGalleryCmsUi/composer.json
index 1ecfb9a3c8855..73747a669c051 100644
--- a/app/code/Magento/MediaGalleryCmsUi/composer.json
+++ b/app/code/Magento/MediaGalleryCmsUi/composer.json
@@ -5,7 +5,8 @@
"php": "~7.3.0||~7.4.0",
"magento/framework": "*",
"magento/module-cms": "*",
- "magento/module-backend": "*"
+ "magento/module-backend": "*",
+ "magento/module-ui": "*"
},
"type": "magento2-module",
"license": [
diff --git a/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml b/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml
index b06ad0fff1df6..65ed3b7197f83 100644
--- a/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml
+++ b/app/code/Magento/MediaGalleryCmsUi/etc/adminhtml/di.xml
@@ -24,7 +24,7 @@
cms_block
-
+
-
diff --git a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml
index 509a7e6a53673..506a6cad5b68e 100644
--- a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml
+++ b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/media_gallery_listing.xml
@@ -13,6 +13,7 @@
name="page_id"
provider="${ $.parentName }"
sortOrder="120"
+ class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInPages"
component="Magento_Ui/js/form/element/ui-select"
template="ui/grid/filters/elements/ui-select">
@@ -37,6 +38,7 @@
name="block_id"
provider="${ $.parentName }"
sortOrder="130"
+ class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInBlocks"
component="Magento_Ui/js/form/element/ui-select"
template="ui/grid/filters/elements/ui-select">
diff --git a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml
index 509a7e6a53673..506a6cad5b68e 100644
--- a/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml
+++ b/app/code/Magento/MediaGalleryCmsUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml
@@ -13,6 +13,7 @@
name="page_id"
provider="${ $.parentName }"
sortOrder="120"
+ class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInPages"
component="Magento_Ui/js/form/element/ui-select"
template="ui/grid/filters/elements/ui-select">
@@ -37,6 +38,7 @@
name="block_id"
provider="${ $.parentName }"
sortOrder="130"
+ class="Magento\MediaGalleryCmsUi\Ui\Component\Listing\Filters\UsedInBlocks"
component="Magento_Ui/js/form/element/ui-select"
template="ui/grid/filters/elements/ui-select">
diff --git a/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php b/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php
index 992f77a1df319..339aca84ec68f 100644
--- a/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php
+++ b/app/code/Magento/MediaGallerySynchronization/Console/Command/Synchronize.php
@@ -7,8 +7,6 @@
namespace Magento\MediaGallerySynchronization\Console\Command;
-use Magento\Framework\App\Area;
-use Magento\Framework\App\State;
use Magento\Framework\Console\Cli;
use Magento\MediaGallerySynchronizationApi\Api\SynchronizeInterface;
use Symfony\Component\Console\Command\Command;
@@ -25,21 +23,13 @@ class Synchronize extends Command
*/
private $synchronizeAssets;
- /**
- * @var State $state
- */
- private $state;
-
/**
* @param SynchronizeInterface $synchronizeAssets
- * @param State $state
*/
public function __construct(
- SynchronizeInterface $synchronizeAssets,
- State $state
+ SynchronizeInterface $synchronizeAssets
) {
$this->synchronizeAssets = $synchronizeAssets;
- $this->state = $state;
parent::__construct();
}
@@ -60,10 +50,11 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Synchronizing assets information from media storage to database...');
- $this->state->emulateAreaCode(Area::AREA_ADMINHTML, function () {
- $this->synchronizeAssets->execute();
- });
+
+ $this->synchronizeAssets->execute();
+
$output->writeln('Completed assets synchronization.');
+
return Cli::RETURN_SUCCESS;
}
}
diff --git a/app/code/Magento/MediaGallerySynchronization/Test/Integration/Model/SynchronizeFilesTest.php b/app/code/Magento/MediaGallerySynchronization/Test/Integration/Model/SynchronizeFilesTest.php
new file mode 100644
index 0000000000000..6c4338c0935dc
--- /dev/null
+++ b/app/code/Magento/MediaGallerySynchronization/Test/Integration/Model/SynchronizeFilesTest.php
@@ -0,0 +1,151 @@
+driver = Bootstrap::getObjectManager()->get(DriverInterface::class);
+ $this->synchronizeFiles = Bootstrap::getObjectManager()->get(SynchronizeFilesInterface::class);
+ $this->getAssetsByPath = Bootstrap::getObjectManager()->get(GetAssetsByPathsInterface::class);
+ $this->getAssetKeywords = Bootstrap::getObjectManager()->get(GetAssetsKeywordsInterface::class);
+ $this->mediaDirectory = Bootstrap::getObjectManager()->get(Filesystem::class)
+ ->getDirectoryWrite(DirectoryList::MEDIA);
+ }
+
+ /**
+ * Test for SynchronizeFiles::execute
+ *
+ * @dataProvider filesProvider
+ * @param null|string $file
+ * @param null|string $title
+ * @param null|string $description
+ * @param null|array $keywords
+ * @throws FileSystemException
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function testExecute(
+ ?string $file,
+ ?string $title,
+ ?string $description,
+ ?array $keywords
+ ): void {
+ $path = realpath(__DIR__ . '/../_files/' . $file);
+ $modifiableFilePath = $this->mediaDirectory->getAbsolutePath($file);
+ $this->driver->copy(
+ $path,
+ $modifiableFilePath
+ );
+
+ $this->synchronizeFiles->execute([$file]);
+
+ $loadedAssets = $this->getAssetsByPath->execute([$file])[0];
+ $loadedKeywords = $this->getKeywords($loadedAssets) ?: null;
+
+ $this->assertEquals($title, $loadedAssets->getTitle());
+ $this->assertEquals($description, $loadedAssets->getDescription());
+ $this->assertEquals($keywords, $loadedKeywords);
+
+ $this->driver->deleteFile($modifiableFilePath);
+ }
+
+ /**
+ * Data provider for testExecute
+ *
+ * @return array[]
+ */
+ public function filesProvider(): array
+ {
+ return [
+ [
+ '/magento.jpg',
+ 'magento',
+ null,
+ null
+ ],
+ [
+ '/magento_metadata.jpg',
+ 'Title of the magento image',
+ 'Description of the magento image',
+ [
+ 'magento',
+ 'mediagallerymetadata'
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Key asset keywords
+ *
+ * @param AssetInterface $asset
+ * @return string[]
+ */
+ private function getKeywords(AssetInterface $asset): array
+ {
+ $assetKeywords = $this->getAssetKeywords->execute([$asset->getId()]);
+
+ if (empty($assetKeywords)) {
+ return [];
+ }
+
+ $keywords = current($assetKeywords)->getKeywords();
+
+ return array_map(
+ function (KeywordInterface $keyword) {
+ return $keyword->getKeyword();
+ },
+ $keywords
+ );
+ }
+}
diff --git a/app/code/Magento/MediaGallerySynchronization/Test/Integration/_files/magento_metadata.jpg b/app/code/Magento/MediaGallerySynchronization/Test/Integration/_files/magento_metadata.jpg
new file mode 100644
index 0000000000000..6dc8cd69e41c1
Binary files /dev/null and b/app/code/Magento/MediaGallerySynchronization/Test/Integration/_files/magento_metadata.jpg differ
diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php
index b4b6713f47065..df13250eacb5f 100644
--- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php
+++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php
@@ -137,7 +137,7 @@ public function execute()
if (!empty($assets)) {
foreach ($assets as $asset) {
$responseContent['options'][] = [
- 'value' => $asset->getId(),
+ 'value' => (string) $asset->getId(),
'label' => $asset->getTitle(),
'path' => $this->storage->getThumbnailUrl($this->images->getStorageRoot() . $asset->getPath())
];
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/CreatedAt.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/CreatedAt.php
deleted file mode 100644
index 7c3eccfea521f..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/CreatedAt.php
+++ /dev/null
@@ -1,59 +0,0 @@
-dateTime = $dateTime;
- }
-
- /**
- * Provide asset created at date time
- *
- * @param AssetInterface $asset
- * @return array
- * @throws \Exception
- */
- public function execute(AssetInterface $asset): array
- {
- return [
- 'title' => __('Created'),
- 'value' => $this->formatDate($asset->getCreatedAt())
- ];
- }
-
- /**
- * Format date to standard format
- *
- * @param string $date
- * @return string
- * @throws \Exception
- */
- private function formatDate(string $date): string
- {
- return $this->dateTime->formatDate($date, \IntlDateFormatter::SHORT, true);
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Height.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Height.php
deleted file mode 100644
index b2b0f389f6b9a..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Height.php
+++ /dev/null
@@ -1,33 +0,0 @@
- __('Height'),
- 'value' => sprintf('%spx', $asset->getHeight())
- ];
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Size.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Size.php
deleted file mode 100644
index 55841cc5abd3f..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Size.php
+++ /dev/null
@@ -1,49 +0,0 @@
- __('Size'),
- 'value' => $this->formatImageSize($asset->getSize())
- ];
- }
-
- /**
- * Format image size
- *
- * @param int $imageSize
- *
- * @return string
- */
- private function formatImageSize(int $imageSize): string
- {
- if ($imageSize === 0) {
- return '';
- }
-
- return sprintf('%sKb', $imageSize / 1000);
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Type.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Type.php
deleted file mode 100644
index 5b47616398ef7..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Type.php
+++ /dev/null
@@ -1,59 +0,0 @@
-types = $types;
- }
-
- /**
- * Provide asset type
- *
- * @param AssetInterface $asset
- * @return array
- * @throws IntegrationException
- */
- public function execute(AssetInterface $asset): array
- {
- return [
- 'title' => __('Type'),
- 'value' => $this->getImageTypeByContentType($asset->getContentType()),
- ];
- }
-
- /**
- * Return image type by content type
- *
- * @param string $contentType
- * @return string
- */
- private function getImageTypeByContentType(string $contentType): string
- {
- $type = current(explode('/', $contentType));
-
- return isset($this->types[$type]) ? $this->types[$type] : 'Asset';
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UpdatedAt.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UpdatedAt.php
deleted file mode 100644
index 2f50bd9a72208..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UpdatedAt.php
+++ /dev/null
@@ -1,59 +0,0 @@
-dateTime = $dateTime;
- }
-
- /**
- * Provide asset updated at date time
- *
- * @param AssetInterface $asset
- * @return array
- * @throws \Exception
- */
- public function execute(AssetInterface $asset): array
- {
- return [
- 'title' => __('Modified'),
- 'value' => $this->formatDate($asset->getUpdatedAt())
- ];
- }
-
- /**
- * Format date to standard format
- *
- * @param string $date
- * @return string
- * @throws \Exception
- */
- private function formatDate(string $date): string
- {
- return $this->dateTime->formatDate($date, \IntlDateFormatter::SHORT, true);
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UsedIn.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UsedIn.php
deleted file mode 100644
index ca3883d5c937c..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/UsedIn.php
+++ /dev/null
@@ -1,113 +0,0 @@
-getContent = $getContent;
- $this->url = $url;
- $this->contentTypes = $contentTypes;
- }
-
- /**
- * Provide information on which content asset is used in
- *
- * @param AssetInterface $asset
- * @return array
- * @throws IntegrationException
- */
- public function execute(AssetInterface $asset): array
- {
- return [
- 'title' => __('Used In'),
- 'value' => $this->getUsedIn($asset->getId())
- ];
- }
-
- /**
- * Retrieve assets used in the Content
- *
- * @param int $assetId
- * @return array
- * @throws IntegrationException
- */
- private function getUsedIn(int $assetId): array
- {
- $details = [];
-
- foreach ($this->getUsedInCounts($assetId) as $type => $number) {
- $details[$type] = $this->contentTypes[$type] ?? ['name' => $type, 'link' => null];
- $details[$type]['number'] = $number;
- $details[$type]['link'] = $details[$type]['link'] ? $this->url->getUrl($details[$type]['link']) : null;
- }
-
- return array_values($details);
- }
-
- /**
- * Get used in counts per type
- *
- * @param int $assetId
- * @return int[]
- * @throws IntegrationException
- */
- private function getUsedInCounts(int $assetId): array
- {
- $usedIn = [];
- $entityIds = [];
-
- $contentIdentities = $this->getContent->execute([$assetId]);
-
- foreach ($contentIdentities as $contentIdentity) {
- $entityId = $contentIdentity->getEntityId();
- $type = $contentIdentity->getEntityType();
-
- if (!isset($entityIds[$type])) {
- $usedIn[$type] = 1;
- } elseif ($entityIds[$type]['entity_id'] !== $entityId) {
- ++$usedIn[$type];
- }
- $entityIds[$type]['entity_id'] = $entityId;
- }
- return $usedIn;
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Width.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Width.php
deleted file mode 100644
index 64e9cf8ad1a8f..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProvider/Width.php
+++ /dev/null
@@ -1,33 +0,0 @@
- __('Width'),
- 'value' => sprintf('%spx', $asset->getWidth())
- ];
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProviderInterface.php b/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProviderInterface.php
deleted file mode 100644
index 92375adfdd4f2..0000000000000
--- a/app/code/Magento/MediaGalleryUi/Model/AssetDetailsProviderInterface.php
+++ /dev/null
@@ -1,24 +0,0 @@
-detailsProviders = $detailsProviders;
- }
-
- /**
- * Get a piece of asset details
- *
- * @param AssetInterface $asset
- * @return array
- */
- public function execute(AssetInterface $asset): array
- {
- $details = [];
- foreach ($this->detailsProviders as $detailsProvider) {
- if ($detailsProvider instanceof AssetDetailsProviderInterface) {
- $details[] = $detailsProvider->execute($asset);
- }
- }
- return $details;
- }
-}
diff --git a/app/code/Magento/MediaGalleryUi/Model/GetAssetDetails.php b/app/code/Magento/MediaGalleryUi/Model/GetAssetDetails.php
new file mode 100644
index 0000000000000..88bd5cf96e534
--- /dev/null
+++ b/app/code/Magento/MediaGalleryUi/Model/GetAssetDetails.php
@@ -0,0 +1,102 @@
+dateTime = $dateTime;
+ $this->getAssetUsageDetails = $getAssetUsageDetails;
+ }
+
+ /**
+ * Get a piece of asset details
+ *
+ * @param AssetInterface $asset
+ * @return array
+ */
+ public function execute(AssetInterface $asset): array
+ {
+ $details = [
+ [
+ 'title' => __('Type'),
+ 'value' => __('Image'),
+ ],
+ [
+ 'title' => __('Created'),
+ 'value' => $this->formatDate($asset->getCreatedAt())
+ ],
+ [
+ 'title' => __('Modified'),
+ 'value' => $this->formatDate($asset->getUpdatedAt())
+ ],
+ [
+ 'title' => __('Width'),
+ 'value' => sprintf('%spx', $asset->getWidth())
+ ],
+ [
+ 'title' => __('Height'),
+ 'value' => sprintf('%spx', $asset->getHeight())
+ ],
+ [
+ 'title' => __('Size'),
+ 'value' => $this->formatSize($asset->getSize())
+ ],
+ [
+ 'title' => __('Used In'),
+ 'value' => $this->getAssetUsageDetails->execute($asset->getId())
+ ]
+ ];
+ return $details;
+ }
+
+ /**
+ * Format image size
+ *
+ * @param int $size
+ * @return string
+ */
+ private function formatSize(int $size): string
+ {
+ return $size === 0 ? '' : sprintf('%.2f KB', $size / 1024);
+ }
+
+ /**
+ * Format date to standard format
+ *
+ * @param string $date
+ * @return string
+ */
+ private function formatDate(string $date): string
+ {
+ return $this->dateTime->formatDate($date, \IntlDateFormatter::SHORT, true);
+ }
+}
diff --git a/app/code/Magento/MediaGalleryUi/Model/GetAssetUsageDetails.php b/app/code/Magento/MediaGalleryUi/Model/GetAssetUsageDetails.php
new file mode 100644
index 0000000000000..1dd8b736a9c90
--- /dev/null
+++ b/app/code/Magento/MediaGalleryUi/Model/GetAssetUsageDetails.php
@@ -0,0 +1,118 @@
+getContent = $getContent;
+ $this->url = $url;
+ $this->contentTypes = $contentTypes;
+ }
+
+ /**
+ * Provide information on which content asset is used in
+ *
+ * @param int $id
+ * @return array
+ * @throws IntegrationException
+ */
+ public function execute(int $id): array
+ {
+ $details = [];
+
+ foreach ($this->getUsageByEntities($id) as $type => $entities) {
+ $details[] = [
+ 'name' => $this->getName($type),
+ 'number' => count($entities),
+ 'link' => $this->getLinkUrl($type)
+ ];
+ }
+
+ return $details;
+ }
+
+ /**
+ * Retrieve the type name from content types configuration
+ *
+ * @param string $type
+ * @return string
+ */
+ private function getName(string $type): string
+ {
+ if (isset($this->contentTypes[$type]) && !empty($this->contentTypes[$type]['name'])) {
+ return $this->contentTypes[$type]['name'];
+ }
+ return $type;
+ }
+
+ /**
+ * Retrieve the type link from content types configuration
+ *
+ * @param string $type
+ * @return string|null
+ */
+ private function getLinkUrl(string $type): ?string
+ {
+ if (isset($this->contentTypes[$type]) && !empty($this->contentTypes[$type]['link'])) {
+ return $this->url->getUrl($this->contentTypes[$type]['link']);
+ }
+ return null;
+ }
+
+ /**
+ * Get used in counts per type
+ *
+ * @param int $assetId
+ * @return int[]
+ * @throws IntegrationException
+ */
+ private function getUsageByEntities(int $assetId): array
+ {
+ $usage = [];
+
+ foreach ($this->getContent->execute([$assetId]) as $contentIdentity) {
+ $id = $contentIdentity->getEntityId();
+ $type = $contentIdentity->getEntityType();
+ $usage[$type][$id] = isset($usage[$type][$id]) ? $usage[$type][$id]++ : 0;
+ }
+
+ return $usage;
+ }
+}
diff --git a/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php b/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php
index b870082ea2aa1..f6972637b3610 100644
--- a/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php
+++ b/app/code/Magento/MediaGalleryUi/Model/GetDetailsByAssetId.php
@@ -44,25 +44,25 @@ class GetDetailsByAssetId
private $getAssetKeywords;
/**
- * @var AssetDetailsProviderPool
+ * @var GetAssetDetails
*/
- private $detailsProviderPool;
+ private $getAssetDetails;
/**
- * @param AssetDetailsProviderPool $detailsProviderPool
+ * @param GetAssetDetails $getAssetDetails
* @param GetAssetsByIdsInterface $getAssetById
* @param StoreManagerInterface $storeManager
* @param SourceIconProvider $sourceIconProvider
* @param GetAssetsKeywordsInterface $getAssetKeywords
*/
public function __construct(
- AssetDetailsProviderPool $detailsProviderPool,
+ GetAssetDetails $getAssetDetails,
GetAssetsByIdsInterface $getAssetById,
StoreManagerInterface $storeManager,
SourceIconProvider $sourceIconProvider,
GetAssetsKeywordsInterface $getAssetKeywords
) {
- $this->detailsProviderPool = $detailsProviderPool;
+ $this->getAssetDetails = $getAssetDetails;
$this->getAssetsById = $getAssetById;
$this->storeManager = $storeManager;
$this->sourceIconProvider = $sourceIconProvider;
@@ -89,7 +89,7 @@ public function execute(array $assetIds): array
'path' => $asset->getPath(),
'description' => $asset->getDescription(),
'id' => $asset->getId(),
- 'details' => $this->detailsProviderPool->execute($asset),
+ 'details' => $this->getAssetDetails->execute($asset),
'size' => $asset->getSize(),
'tags' => $this->getKeywords($asset),
'source' => $asset->getSource() ?
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml
index 4b0375088509b..5e5c89637c6a1 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryEnableMassActionModeActionGroup.xml
@@ -13,7 +13,7 @@
Activate massaction mode by click on Delete Selected..
-
-
+
+
diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml
index a40a70c5f160c..07f2dc23530e1 100644
--- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml
+++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryMassActionSection.xml
@@ -11,6 +11,7 @@
-
+
+
diff --git a/app/code/Magento/MediaGalleryUi/etc/di.xml b/app/code/Magento/MediaGalleryUi/etc/di.xml
index 56ccf7c1aa727..a8c4e2a8d8963 100644
--- a/app/code/Magento/MediaGalleryUi/etc/di.xml
+++ b/app/code/Magento/MediaGalleryUi/etc/di.xml
@@ -33,27 +33,7 @@
media
-
-
-
- - Image
-
-
-
-
-
-
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Type
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\CreatedAt
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\UpdatedAt
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Width
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Height
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\Size
- - Magento\MediaGalleryUi\Model\AssetDetailsProvider\UsedIn
-
-
-
diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less
index 671a82dce3f58..fc8bd49126d8e 100644
--- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less
+++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less
@@ -29,7 +29,8 @@
}
}
- .media-gallery-asset-ui-select-filter {
+ .media-gallery-asset-ui-select-filter,
+ .edit-image-details {
.admin__action-multiselect-crumb {
max-width: 70%;
@@ -47,7 +48,10 @@
position: absolute;
text-overflow: ellipsis;
}
+ }
+ .media-gallery-asset-ui-select-filter,
+ .edit-image-details {
.admin__action-multiselect-item-path {
float: right;
max-height: 70px;
@@ -307,7 +311,7 @@
}
}
- .adobe-stock-icon {
+ .media-gallery-source-icon {
margin-bottom: -6px;
width: 29px;
}
@@ -366,7 +370,7 @@
}
.image-type {
- .adobe-stock-icon {
+ .media-gallery-source-icon {
margin-bottom: -6px;
width: 29px;
}
@@ -412,12 +416,14 @@
& > * {
float: left;
+ margin-left: -1px;
width: 50%;
}
.value {
display: inline;
float: right;
+ margin-left: 1px;
}
.title {
diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js
index e7c05573a4f11..bf852d0ddae68 100644
--- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js
+++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js
@@ -103,6 +103,7 @@ define([
*/
updateSelected: function () {
this.selected({});
+ this.hideAddSelectedAndDeleteButon();
},
/**
diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js
index a49303669edc8..ddc5af0ab6296 100644
--- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js
+++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactionView.js
@@ -7,33 +7,23 @@ define([
'jquery',
'uiComponent',
'mage/translate',
- 'text!Magento_MediaGalleryUi/template/grid/massactions/cancelButton.html'
-], function ($, Component, $t, cancelMassActionButton) {
+ 'text!Magento_MediaGalleryUi/template/grid/massactions/massactionButtons.html'
+], function ($, Component, $t, massactionButtons) {
'use strict';
return Component.extend({
defaults: {
- pageActionsSelector: '.page-actions-buttons',
gridSelector: '[data-id="media-gallery-masonry-grid"]',
- originDeleteSelector: null,
- originCancelEvent: null,
- cancelMassactionButton: cancelMassActionButton,
- isCancelButtonInserted: false,
- deleteButtonSelector: '#delete_massaction',
- addSelectedButtonSelector: '#add_selected',
- cancelMassactionButtonSelector: '#cancel',
standAloneTitle: 'Manage Gallery',
slidePanelTitle: 'Media Gallery',
defaultTitle: null,
- contextButtonSelector: '.three-dots',
- buttonsIds: [
- '#delete_folder',
- '#create_folder',
- '#upload_image',
- '#search_adobe_stock',
- '.three-dots',
- '#add_selected'
- ],
+ are: null,
+ standAloneArea: 'standalone',
+ slidepanelArea: 'slidepanel',
+ massactionButtonsSelector: '.massaction-buttons',
+ buttonsSelectorStandalone: '.page-actions-buttons',
+ buttonsSelectorSlidePanel: '.page-actions.floating-header',
+ buttons: '.page-main-actions :button',
massactionModeTitle: $t('Select Images to Delete')
},
@@ -62,7 +52,6 @@ define([
* Hide or show buttons per active mode.
*/
switchButtons: function () {
-
if (this.massActionMode()) {
this.activateMassactionButtonView();
} else {
@@ -74,58 +63,24 @@ define([
* Sets buttons to default regular -mode view.
*/
revertButtonsToDefaultView: function () {
- $(this.deleteButtonSelector).replaceWith(this.originDeleteSelector);
-
- if (!this.isCancelButtonInserted) {
- $('#cancel_massaction').replaceWith(this.originCancelEvent);
- } else {
- $(this.cancelMassactionButtonSelector).addClass('no-display');
- $('#cancel_massaction').remove();
- }
-
- $.each(this.buttonsIds, function (key, value) {
- $(value).removeClass('no-display');
- });
-
- $(this.addSelectedButtonSelector).addClass('no-display');
- $(this.deleteButtonSelector)
- .addClass('media-gallery-actions-buttons')
- .removeClass('primary');
+ $(this.buttons).removeClass('no-display');
+ $(this.massactionButtonsSelector).remove();
},
/**
* Activate mass action buttons view
*/
activateMassactionButtonView: function () {
- this.originDeleteSelector = $(this.deleteButtonSelector).clone();
- $(this.originDeleteSelector).click(function () {
- $(window).trigger('massAction.MediaGallery');
- });
- this.originCancelEvent = $('#cancel').clone(true, true);
+ var buttonsContainer;
- $.each(this.buttonsIds, function (key, value) {
- $(value).addClass('no-display');
- });
+ $(this.buttons).addClass('no-display');
- $(this.deleteButtonSelector)
- .removeClass('media-gallery-actions-buttons')
- .text($t('Delete Selected'))
- .addClass('primary');
-
- if (!$(this.cancelMassactionButtonSelector).length) {
- $(this.pageActionsSelector).append(this.cancelMassactionButton);
- this.isCancelButtonInserted = true;
- } else {
- $(this.cancelMassactionButtonSelector).replaceWith(this.cancelMassactionButton);
- }
- $('#cancel_massaction').on('click', function () {
- $(window).trigger('terminateMassAction.MediaGallery');
- }).applyBindings();
-
- $(this.deleteButtonSelector).off('click').on('click', function () {
- $(this.deleteButtonSelector).trigger('massDelete');
- }.bind(this));
+ buttonsContainer = this.area === this.standAloneArea ?
+ this.buttonsSelectorStandalone :
+ this.buttonsSelectorSlidePanel;
+ $(buttonsContainer).append(massactionButtons);
+ $(this.massactionButtonsSelector).applyBindings();
},
/**
@@ -133,7 +88,15 @@ define([
*/
changePageTitle: function () {
var title = $('h1:contains(' + this.standAloneTitle + ')'),
- titleSelector = title.length === 1 ? title : $('h1:contains(' + this.slidePanelTitle + ')');
+ titleSelector;
+
+ if (title.length === 1) {
+ titleSelector = title;
+ this.area = this.standAloneArea;
+ } else {
+ titleSelector = $('h1:contains(' + this.slidePanelTitle + ')');
+ this.area = this.slidepanelArea;
+ }
if (this.massActionMode()) {
this.defaultTitle = titleSelector.text();
diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js
index 8114305a3b29c..4f09854005f23 100644
--- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js
+++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js
@@ -16,6 +16,7 @@ define([
return Component.extend({
defaults: {
+ deleteButtonSelector: '#delete_selected_massaction',
deleteImagesSelector: '#delete_massaction',
mediaGalleryImageDetailsName: 'mediaGalleryImageDetails',
modules: {
@@ -86,6 +87,7 @@ define([
this.massActionMode(false);
this.switchMode();
+ this.imageModel().updateSelected();
}.bind(this));
},
@@ -124,7 +126,7 @@ define([
*/
handleDeleteAction: function () {
if (this.massActionMode()) {
- $(this.massactionView().deleteButtonSelector).on('massDelete', function () {
+ $(this.deleteButtonSelector).on('massDelete.MediaGallery', function () {
if (this.getSelectedCount() < 1) {
uiAlert({
content: $t('You need to select at least one image')
diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js
index d0d37d49329e0..db42f155501c3 100644
--- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js
+++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/image/image-details.js
@@ -135,6 +135,16 @@ define([
return _.isArray(value);
},
+ /**
+ * Is value not empty
+ *
+ * @param {*} value
+ * @returns {Boolean}
+ */
+ notEmpty: function (value) {
+ return value.length > 0;
+ },
+
/**
* Get name and number text for used in link
*
@@ -151,7 +161,7 @@ define([
* @param {String} link
*/
getFilterUrl: function (link) {
- return link + '?filters[asset_id]=' + this.image().id;
+ return link + '?filters[asset_id]=[' + this.image().id + ']';
},
/**
diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html
index 4a8350231a0fd..3b88c58201be7 100644
--- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html
+++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image.html
@@ -29,7 +29,7 @@
+
+
+
diff --git a/app/code/Magento/Sales/etc/webapi_soap/di.xml b/app/code/Magento/Sales/etc/webapi_soap/di.xml
index 5d7838297a7c7..1a8478438b04a 100644
--- a/app/code/Magento/Sales/etc/webapi_soap/di.xml
+++ b/app/code/Magento/Sales/etc/webapi_soap/di.xml
@@ -19,4 +19,7 @@
+
+
+
diff --git a/app/code/Magento/Sales/view/frontend/layout/customer_account.xml b/app/code/Magento/Sales/view/frontend/layout/customer_account.xml
index e2a74fe09f718..84ed48e2566ac 100644
--- a/app/code/Magento/Sales/view/frontend/layout/customer_account.xml
+++ b/app/code/Magento/Sales/view/frontend/layout/customer_account.xml
@@ -12,7 +12,7 @@
sales/order/history
My Orders
- 10
+ 230
diff --git a/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php b/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php
index 70ea478004b9d..dfa83f1765041 100644
--- a/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php
+++ b/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php
@@ -10,6 +10,9 @@
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;
use Magento\Framework\App\Response\Http;
+use Magento\Framework\App\Response\HttpInterface as HttpResponseInterface;
+use Magento\Framework\App\ResponseInterface;
+use Magento\Framework\View\Result\Layout;
/**
* Plugin for asynchronous CSS loading.
@@ -32,48 +35,94 @@ public function __construct(ScopeConfigInterface $scopeConfig)
}
/**
- * Load CSS asynchronously if it is enabled in configuration.
+ * Extracts styles to head after critical css if critical path feature is enabled.
*
- * @param Http $subject
- * @return void
+ * @param Layout $subject
+ * @param Layout $result
+ * @param HttpResponseInterface|ResponseInterface $httpResponse
+ * @return Layout (That should be void, actually)
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function beforeSendResponse(Http $subject): void
+ public function afterRenderResult(Layout $subject, Layout $result, ResponseInterface $httpResponse)
{
- $content = $subject->getContent();
+ if (!$this->isCssCriticalEnabled()) {
+ return $result;
+ }
- if (\is_string($content) && strpos($content, 'scopeConfig->isSetFlag(
- self::XML_PATH_USE_CSS_CRITICAL_PATH,
- ScopeInterface::SCOPE_STORE
- )) {
- $cssMatches = [];
- // add link rel preload to style sheets
- $content = preg_replace_callback(
- '@@',
- function ($matches) use (&$cssMatches) {
- $cssMatches[] = $matches[0];
- preg_match('@href=("|\')(.*?)\1@', $matches[0], $hrefAttribute);
- $href = $hrefAttribute[2];
- if (preg_match('@media=("|\')(.*?)\1@', $matches[0], $mediaAttribute)) {
- $media = $mediaAttribute[2];
- }
- $media = $media ?? 'all';
- $loadCssAsync = sprintf(
- '',
- $media,
- $href
- );
-
- return $loadCssAsync;
- },
- $content
- );
+ $content = (string)$httpResponse->getContent();
+ $headCloseTag = '';
- if (!empty($cssMatches)) {
- $content = str_replace('Test Title
" .
- "" .
- "Test Content
Test Title
" .
- "" .
- "Test Content
" .
- "" .
- "\nTest Content
Test Content
Test Content
Test Content
setContent($content);
+ $headEndTagFound = strpos($content, $headCloseTag) !== false;
+
+ if ($headEndTagFound) {
+ $styles = $this->extractLinkTags($content);
+ if ($styles) {
+ $newHeadEndTagPosition = strrpos($content, $headCloseTag);
+ $content = substr_replace($content, $styles . "\n", $newHeadEndTagPosition, 0);
+ $httpResponse->setContent($content);
}
}
+
+ return $result;
+ }
+
+ /**
+ * Extracts link tags found in given content.
+ *
+ * @param string $content
+ */
+ private function extractLinkTags(string &$content): string
+ {
+ $styles = '';
+ $styleOpen = '';
+ $styleOpenPos = strpos($content, $styleOpen);
+
+ while ($styleOpenPos !== false) {
+ $styleClosePos = strpos($content, $styleClose, $styleOpenPos);
+ $style = substr($content, $styleOpenPos, $styleClosePos - $styleOpenPos + strlen($styleClose));
+
+ if (!preg_match('@rel=["\']stylesheet["\']@', $style)) {
+ // Link is not a stylesheet, search for another one after it.
+ $styleOpenPos = strpos($content, $styleOpen, $styleClosePos);
+ continue;
+ }
+ // Remove the link from HTML to add it before tag later.
+ $content = str_replace($style, '', $content);
+
+ if (!preg_match('@href=("|\')(.*?)\1@', $style, $hrefAttribute)) {
+ throw new \RuntimeException("Invalid link {$style} syntax provided");
+ }
+ $href = $hrefAttribute[2];
+
+ if (preg_match('@media=("|\')(.*?)\1@', $style, $mediaAttribute)) {
+ $media = $mediaAttribute[2];
+ }
+ $media = $media ?? 'all';
+
+ $style = sprintf(
+ '',
+ $media,
+ $href
+ );
+ $styles .= "\n" . $style;
+ // Link was cut out, search for the next one at its former position.
+ $styleOpenPos = strpos($content, $styleOpen, $styleOpenPos);
+ }
+
+ return $styles;
+ }
+
+ /**
+ * Returns information whether css critical path is enabled
+ *
+ * @return bool
+ */
+ private function isCssCriticalEnabled(): bool
+ {
+ return $this->scopeConfig->isSetFlag(
+ self::XML_PATH_USE_CSS_CRITICAL_PATH,
+ ScopeInterface::SCOPE_STORE
+ );
}
}
diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php
index d433f745400d0..eed3e05c4abdd 100644
--- a/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php
+++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php
@@ -7,13 +7,14 @@
namespace Magento\Theme\Test\Unit\Controller\Result;
-use Magento\Framework\App\Config\ScopeConfigInterface;
-use Magento\Framework\App\Response\Http;
-use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
-use Magento\Store\Model\ScopeInterface;
use Magento\Theme\Controller\Result\AsyncCssPlugin;
-use PHPUnit\Framework\MockObject\MockObject;
+use Magento\Framework\App\Response\Http;
use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Store\Model\ScopeInterface;
+use Magento\Framework\View\Result\Layout;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
/**
* Unit test for Magento\Theme\Test\Unit\Controller\Result\AsyncCssPlugin.
@@ -37,6 +38,9 @@ class AsyncCssPluginTest extends TestCase
*/
private $httpMock;
+ /** @var Layout|MockObject */
+ private $layoutMock;
+
/**
* @inheritdoc
*/
@@ -48,6 +52,7 @@ protected function setUp(): void
->getMockForAbstractClass();
$this->httpMock = $this->createMock(Http::class);
+ $this->layoutMock = $this->createMock(Layout::class);
$objectManager = new ObjectManagerHelper($this);
$this->plugin = $objectManager->getObject(
@@ -59,87 +64,134 @@ protected function setUp(): void
}
/**
- * Data Provider for before send response
+ * Data Provider for testAfterRenderResult
*
* @return array
*/
- public function sendResponseDataProvider(): array
+ public function renderResultDataProvider(): array
{
return [
[
- "content" => "
",
+ "content" => "
" .
+ "" .
+ "",
+ "flag" => true,
+ "result" => "
\n" .
+ "\n" .
+ "",
+ ],
+ [
+ "content" => "
" .
+ "" .
+ "",
"flag" => true,
- "result" => "
"
+ "result" => "
\n" .
+ "\n" .
+ "",
],
[
- "content" => "
",
+ "content" => "
" .
+ "" .
+ "",
"flag" => false,
- "result" => "
"
+ "result" => "
" .
+ "" .
+ "",
],
[
- "content" => "
",
+ "content" => "
" .
+ "" .
+ "" .
+ "",
"flag" => true,
- "result" => "
"
+ "result" => "
\n" .
+ "\n" .
+ "\n" .
+ "",
+ ],
+ [
+ "content" => "
",
+ "flag" => false,
+ "result" => "
"
+ ],
+ [
+ "content" => "
",
+ "flag" => true,
+ "result" => "
"
]
];
}
/**
- * Test beforeSendResponse
+ * Test after render result response
*
* @param string $content
* @param bool $isSetFlag
* @param string $result
* @return void
- * @dataProvider sendResponseDataProvider
+ * @dataProvider renderResultDataProvider
*/
- public function testBeforeSendResponse($content, $isSetFlag, $result): void
+ public function testAfterRenderResult(string $content, bool $isSetFlag, string $result): void
{
- $this->httpMock->expects($this->once())
- ->method('getContent')
+ // Given (context)
+ $this->httpMock->method('getContent')
->willReturn($content);
- $this->scopeConfigMock->expects($this->once())
- ->method('isSetFlag')
- ->with(
- self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH,
- ScopeInterface::SCOPE_STORE
- )
+ $this->scopeConfigMock->method('isSetFlag')
+ ->with(self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH, ScopeInterface::SCOPE_STORE)
->willReturn($isSetFlag);
+ // Expects
$this->httpMock->expects($this->any())
->method('setContent')
->with($result);
- $this->plugin->beforeSendResponse($this->httpMock);
+ // When
+ $this->plugin->afterRenderResult($this->layoutMock, $this->layoutMock, $this->httpMock);
}
/**
- * Test BeforeSendResponse if content is not a string
+ * Data Provider for testAfterRenderResultIfGetContentIsNotAString()
*
+ * @return array
+ */
+ public function ifGetContentIsNotAStringDataProvider(): array
+ {
+ return [
+ [
+ 'content' => null
+ ]
+ ];
+ }
+
+ /**
+ * Test AfterRenderResult if content is not a string
+ *
+ * @param $content
* @return void
+ * @dataProvider ifGetContentIsNotAStringDataProvider
*/
- public function testIfGetContentIsNotAString(): void
+ public function testAfterRenderResultIfGetContentIsNotAString($content): void
{
+ $this->scopeConfigMock->method('isSetFlag')
+ ->with(self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH, ScopeInterface::SCOPE_STORE)
+ ->willReturn(true);
+
$this->httpMock->expects($this->once())
->method('getContent')
- ->willReturn([]);
+ ->willReturn($content);
- $this->scopeConfigMock->expects($this->any())
- ->method('isSetFlag')
- ->with(
- self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH,
- ScopeInterface::SCOPE_STORE
- )
- ->willReturn(false);
+ $this->httpMock->expects($this->never())
+ ->method('setContent');
- $this->plugin->beforeSendResponse($this->httpMock);
+ $this->plugin->afterRenderResult($this->layoutMock, $this->layoutMock, $this->httpMock);
}
}
diff --git a/app/code/Magento/Theme/etc/frontend/di.xml b/app/code/Magento/Theme/etc/frontend/di.xml
index d3e5c07861c84..35eb9d4f8b53c 100644
--- a/app/code/Magento/Theme/etc/frontend/di.xml
+++ b/app/code/Magento/Theme/etc/frontend/di.xml
@@ -26,11 +26,9 @@
-
-
-
-
+
+
diff --git a/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml b/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml
index 96f8fbed4c041..1e5a4578602ca 100644
--- a/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml
+++ b/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml
@@ -18,6 +18,7 @@
Magento\Theme\Block\Html\Header\CriticalCss
+
diff --git a/app/code/Magento/Theme/view/frontend/templates/html/main_css_preloader.phtml b/app/code/Magento/Theme/view/frontend/templates/html/main_css_preloader.phtml
index 6c2f17b6ffb08..70c2a5f4538fa 100644
--- a/app/code/Magento/Theme/view/frontend/templates/html/main_css_preloader.phtml
+++ b/app/code/Magento/Theme/view/frontend/templates/html/main_css_preloader.phtml
@@ -4,12 +4,17 @@
* See COPYING.txt for license details.
*/
-/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
+/**
+ * @var \Magento\Framework\View\Element\Template $block
+ * @var \Magento\Framework\Escaper $escaper
+ * @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer
+ */
+
?>
-
+
= /* @noEscape */ $secureRenderer->renderStyleAsTag(
"position: absolute;",
diff --git a/app/code/Magento/Theme/view/frontend/templates/js/css_rel_preload.phtml b/app/code/Magento/Theme/view/frontend/templates/js/css_rel_preload.phtml
index 11a388a0fec20..2df684ccffee1 100644
--- a/app/code/Magento/Theme/view/frontend/templates/js/css_rel_preload.phtml
+++ b/app/code/Magento/Theme/view/frontend/templates/js/css_rel_preload.phtml
@@ -2,12 +2,12 @@
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
+ * @deprecated as polyfill isn't used anymore
*/
/** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
-?>
-
-= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js b/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js
index 83220028a72df..1f870e9e819a1 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js
@@ -63,9 +63,14 @@ define([
return _.chain(searchString.slice(1).split('&'))
.map(function (item) {
+
if (item && item.search(this.filterKey) !== -1) {
itemArray = item.split('=');
+ if (itemArray[1].search('\\[') === 0) {
+ itemArray[1] = itemArray[1].replace(/[\[\]]/g, '').split(',');
+ }
+
itemArray[0] = itemArray[0].replace(this.filterKey, '')
.replace(/[\[\]]/g, '');
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/cells/thumbnail.html b/app/code/Magento/Ui/view/base/web/templates/grid/cells/thumbnail.html
index 705becce75a0a..25f26813d6eaa 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/cells/thumbnail.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/cells/thumbnail.html
@@ -4,4 +4,8 @@
* See COPYING.txt for license details.
*/
-->
-
+
+
+
+
+
diff --git a/app/code/Magento/Vault/view/frontend/layout/customer_account.xml b/app/code/Magento/Vault/view/frontend/layout/customer_account.xml
index 4495d425ff595..05044da272e6d 100644
--- a/app/code/Magento/Vault/view/frontend/layout/customer_account.xml
+++ b/app/code/Magento/Vault/view/frontend/layout/customer_account.xml
@@ -15,7 +15,7 @@
vault/cards/listaction
Stored Payment Methods
- 70
+ 160
diff --git a/app/code/Magento/Wishlist/view/frontend/layout/customer_account.xml b/app/code/Magento/Wishlist/view/frontend/layout/customer_account.xml
index 63a296dd3e48e..4d0ffce0a2274 100644
--- a/app/code/Magento/Wishlist/view/frontend/layout/customer_account.xml
+++ b/app/code/Magento/Wishlist/view/frontend/layout/customer_account.xml
@@ -12,7 +12,7 @@
wishlist
My Wish List
- 30
+ 210
diff --git a/app/code/Magento/Wishlist/view/frontend/layout/default.xml b/app/code/Magento/Wishlist/view/frontend/layout/default.xml
index cd8e6349783b4..c4f0d01707b20 100644
--- a/app/code/Magento/Wishlist/view/frontend/layout/default.xml
+++ b/app/code/Magento/Wishlist/view/frontend/layout/default.xml
@@ -13,7 +13,7 @@
- 30
+ 60
diff --git a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentCreateTest.php b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentCreateTest.php
index 08b3c4548e08f..29a11f9d68e8f 100644
--- a/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentCreateTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/ShipmentCreateTest.php
@@ -6,10 +6,16 @@
namespace Magento\Sales\Service\V1;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Webapi\Rest\Request;
+use Magento\Sales\Model\Order;
+use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\WebapiAbstract;
/**
* Class ShipmentCreateTest
+ *
+ * Test shipment save API
*/
class ShipmentCreateTest extends WebapiAbstract
{
@@ -20,23 +26,78 @@ class ShipmentCreateTest extends WebapiAbstract
const SERVICE_VERSION = 'V1';
/**
- * @var \Magento\Framework\ObjectManagerInterface
+ * @var ObjectManagerInterface
*/
protected $objectManager;
protected function setUp(): void
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->objectManager = Bootstrap::getObjectManager();
}
/**
+ * Test save shipment return valid result with multiple tracks with multiple comments
+ *
* @magentoApiDataFixture Magento/Sales/_files/order.php
*/
- public function testInvoke()
+ public function testInvokeWithMultipleTrackAndComments()
{
- /** @var \Magento\Sales\Model\Order $order */
- $order = $this->objectManager->create(\Magento\Sales\Model\Order::class)->loadByIncrementId('100000001');
- $orderItem = current($order->getAllItems());
+ $data = $this->getEntityData();
+ $result = $this->_webApiCall(
+ $this->getServiceInfo(),
+ [
+ 'entity' => $data['shipment data with multiple tracking and multiple comments']]
+ );
+ $this->assertNotEmpty($result);
+ $this->assertEquals(3, count($result['tracks']));
+ $this->assertEquals(3, count($result['comments']));
+ }
+
+ /**
+ * Test save shipment return valid result with multiple tracks with no comments
+ *
+ * @magentoApiDataFixture Magento/Sales/_files/order.php
+ */
+ public function testInvokeWithMultipleTrackAndNoComments()
+ {
+ $data = $this->getEntityData();
+ $result = $this->_webApiCall(
+ $this->getServiceInfo(),
+ [
+ 'entity' => $data['shipment data with multiple tracking']]
+ );
+ $this->assertNotEmpty($result);
+ $this->assertEquals(3, count($result['tracks']));
+ $this->assertEquals(0, count($result['comments']));
+ }
+
+ /**
+ * Test save shipment return valid result with no tracks with multiple comments
+ *
+ * @magentoApiDataFixture Magento/Sales/_files/order.php
+ */
+ public function testInvokeWithNoTrackAndMultipleComments()
+ {
+ $data = $this->getEntityData();
+ $result = $this->_webApiCall(
+ $this->getServiceInfo(),
+ [
+ 'entity' => $data['shipment data with multiple comments']]
+ );
+ $this->assertNotEmpty($result);
+ $this->assertEquals(0, count($result['tracks']));
+ $this->assertEquals(3, count($result['comments']));
+ }
+
+ /**
+ * @return array
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function getEntityData()
+ {
+ $existingOrder = $this->getOrder('100000001');
+ $orderItem = current($existingOrder->getAllItems());
+
$items = [
[
'order_item_id' => $orderItem->getId(),
@@ -53,10 +114,201 @@ public function testInvoke()
'weight' => null,
],
];
- $serviceInfo = [
+ return [
+ 'shipment data with multiple tracking and multiple comments' => [
+ 'order_id' => $existingOrder->getId(),
+ 'entity_id' => null,
+ 'store_id' => null,
+ 'total_weight' => null,
+ 'total_qty' => null,
+ 'email_sent' => null,
+ 'customer_id' => null,
+ 'shipping_address_id' => null,
+ 'billing_address_id' => null,
+ 'shipment_status' => null,
+ 'increment_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'shipping_label' => null,
+ 'tracks' => [
+ [
+ 'carrier_code' => 'UPS',
+ 'order_id' => $existingOrder->getId(),
+ 'title' => 'ground',
+ 'description' => null,
+ 'track_number' => '12345678',
+ 'parent_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'qty' => null,
+ 'weight' => null
+ ],
+ [
+ 'carrier_code' => 'UPS',
+ 'order_id' => $existingOrder->getId(),
+ 'title' => 'ground',
+ 'description' => null,
+ 'track_number' => '654563221',
+ 'parent_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'qty' => null,
+ 'weight' => null
+ ],
+ [
+ 'carrier_code' => 'USPS',
+ 'order_id' => $existingOrder->getId(),
+ 'title' => 'ground',
+ 'description' => null,
+ 'track_number' => '789654565',
+ 'parent_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'qty' => null,
+ 'weight' => null
+ ]
+ ],
+ 'items' => $items,
+ 'comments' => [
+ [
+ 'comment' => 'Shipment-related comment-1.',
+ 'is_customer_notified' => null,
+ 'is_visible_on_front' => null,
+ 'parent_id' => null
+ ],
+ [
+ 'comment' => 'Shipment-related comment-2.',
+ 'is_customer_notified' => null,
+ 'is_visible_on_front' => null,
+ 'parent_id' => null
+ ],
+ [
+ 'comment' => 'Shipment-related comment-3.',
+ 'is_customer_notified' => null,
+ 'is_visible_on_front' => null,
+ 'parent_id' => null
+ ]
+
+ ]
+ ],
+ 'shipment data with multiple tracking' => [
+ 'order_id' => $existingOrder->getId(),
+ 'entity_id' => null,
+ 'store_id' => null,
+ 'total_weight' => null,
+ 'total_qty' => null,
+ 'email_sent' => null,
+ 'customer_id' => null,
+ 'shipping_address_id' => null,
+ 'billing_address_id' => null,
+ 'shipment_status' => null,
+ 'increment_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'shipping_label' => null,
+ 'tracks' => [
+ [
+ 'carrier_code' => 'UPS',
+ 'order_id' => $existingOrder->getId(),
+ 'title' => 'ground',
+ 'description' => null,
+ 'track_number' => '12345678',
+ 'parent_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'qty' => null,
+ 'weight' => null
+ ],
+ [
+ 'carrier_code' => 'UPS',
+ 'order_id' => $existingOrder->getId(),
+ 'title' => 'ground',
+ 'description' => null,
+ 'track_number' => '654563221',
+ 'parent_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'qty' => null,
+ 'weight' => null
+ ],
+ [
+ 'carrier_code' => 'USPS',
+ 'order_id' => $existingOrder->getId(),
+ 'title' => 'ground',
+ 'description' => null,
+ 'track_number' => '789654565',
+ 'parent_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'qty' => null,
+ 'weight' => null
+ ]
+ ],
+ 'items' => $items,
+ 'comments' => []
+ ],
+ 'shipment data with multiple comments' => [
+ 'order_id' => $existingOrder->getId(),
+ 'entity_id' => null,
+ 'store_id' => null,
+ 'total_weight' => null,
+ 'total_qty' => null,
+ 'email_sent' => null,
+ 'customer_id' => null,
+ 'shipping_address_id' => null,
+ 'billing_address_id' => null,
+ 'shipment_status' => null,
+ 'increment_id' => null,
+ 'created_at' => null,
+ 'updated_at' => null,
+ 'shipping_label' => null,
+ 'tracks' => [],
+ 'items' => $items,
+ 'comments' => [
+ [
+ 'comment' => 'Shipment-related comment-1.',
+ 'is_customer_notified' => null,
+ 'is_visible_on_front' => null,
+ 'parent_id' => null
+ ],
+ [
+ 'comment' => 'Shipment-related comment-2.',
+ 'is_customer_notified' => null,
+ 'is_visible_on_front' => null,
+ 'parent_id' => null
+ ],
+ [
+ 'comment' => 'Shipment-related comment-3.',
+ 'is_customer_notified' => null,
+ 'is_visible_on_front' => null,
+ 'parent_id' => null
+ ]
+
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Returns order by increment id.
+ *
+ * @param string $incrementId
+ * @return Order
+ */
+ private function getOrder(string $incrementId): Order
+ {
+ return $this->objectManager->create(Order::class)->loadByIncrementId($incrementId);
+ }
+
+ /**
+ * @return array
+ */
+ private function getServiceInfo(): array
+ {
+ return [
'rest' => [
'resourcePath' => self::RESOURCE_PATH,
- 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
+ 'httpMethod' => Request::HTTP_METHOD_POST,
],
'soap' => [
'service' => self::SERVICE_READ_NAME,
@@ -64,46 +316,5 @@ public function testInvoke()
'operation' => self::SERVICE_READ_NAME . 'save',
],
];
- $data = [
- 'order_id' => $order->getId(),
- 'entity_id' => null,
- 'store_id' => null,
- 'total_weight' => null,
- 'total_qty' => null,
- 'email_sent' => null,
- 'customer_id' => null,
- 'shipping_address_id' => null,
- 'billing_address_id' => null,
- 'shipment_status' => null,
- 'increment_id' => null,
- 'created_at' => null,
- 'updated_at' => null,
- 'shipping_label' => null,
- 'tracks' => [
- [
- 'carrier_code' => 'UPS',
- 'order_id' => $order->getId(),
- 'title' => 'ground',
- 'description' => null,
- 'track_number' => '12345678',
- 'parent_id' => null,
- 'created_at' => null,
- 'updated_at' => null,
- 'qty' => null,
- 'weight' => null
- ]
- ],
- 'items' => $items,
- 'comments' => [
- [
- 'comment' => 'Shipment-related comment.',
- 'is_customer_notified' => null,
- 'is_visible_on_front' => null,
- 'parent_id' => null
- ]
- ],
- ];
- $result = $this->_webApiCall($serviceInfo, ['entity' => $data]);
- $this->assertNotEmpty($result);
}
}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
index 4c1e31c85ec77..48b27c83ee6e3 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
@@ -69,9 +69,9 @@ protected function _getConfigValue($configPath, $scopeCode = null)
* @param string $configPath
* @param string $scopeType
* @param string|null $scopeCode
- * @return string|null
+ * @return mixed|null
*/
- protected function getScopeConfigValue(string $configPath, string $scopeType, string $scopeCode = null): ?string
+ protected function getScopeConfigValue(string $configPath, string $scopeType, string $scopeCode = null)
{
$result = null;
if ($scopeCode !== false) {
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/Type/File/ValidatorFileMock.php
similarity index 84%
rename from dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php
rename to dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/Type/File/ValidatorFileMock.php
index 9b5650b1826c3..b7df1205f2ba3 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/_files/ValidatorFileMock.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Catalog/Model/Product/Option/Type/File/ValidatorFileMock.php
@@ -5,19 +5,22 @@
*/
declare(strict_types=1);
-namespace Magento\Checkout\_files;
+namespace Magento\TestFramework\Catalog\Model\Product\Option\Type\File;
use Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
/**
* Creates mock for ValidatorFile to replace real instance in fixtures.
*/
-class ValidatorFileMock extends \PHPUnit\Framework\TestCase
+class ValidatorFileMock extends TestCase
{
/**
* Returns mock.
+ *
* @param array|null $fileData
- * @return ValidatorFile|\PHPUnit_Framework_MockObject_MockObject
+ * @return ValidatorFile|MockObject
*/
public function getInstance($fileData = null)
{
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Serialize/Serializer.php b/dev/tests/integration/framework/Magento/TestFramework/Serialize/Serializer.php
new file mode 100644
index 0000000000000..60f2a7ce6c4cc
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Serialize/Serializer.php
@@ -0,0 +1,52 @@
+getObjectManager();
$cache = $objectManager->get(CacheInterface::class);
- $serializer = $objectManager->get(SerializerInterface::class);
+ $serializer = $objectManager->get(\Magento\TestFramework\Serialize\Serializer::class);
$cachedProperties = $cache->load(self::CACHE_NAME);
if ($cachedProperties) {
@@ -187,9 +187,10 @@ public static function backupStaticVariables()
| Files::INCLUDE_TESTS
),
function ($classFile) {
- return StaticProperties::_isClassInCleanableFolders($classFile)
- // phpcs:ignore Magento2.Functions.DiscouragedFunction
- && strpos(file_get_contents($classFile), ' static ') > 0;
+ return strpos($classFile, 'TestFramework') === -1
+ && StaticProperties::_isClassInCleanableFolders($classFile)
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction
+ && strpos(file_get_contents($classFile), ' static ') > 0;
}
);
$namespacePattern = '/namespace [a-zA-Z0-9\\\\]+;/';
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/order_with_bundle_shipped_separately.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/order_with_bundle_shipped_separately.php
index b91d479cdf1ef..e5f089ae9637c 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/order_with_bundle_shipped_separately.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/order_with_bundle_shipped_separately.php
@@ -155,6 +155,8 @@
/** @var \Magento\Sales\Model\Order\Item $orderItem */
$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class);
$orderItem->setProductId($product->getId());
+$orderItem->setSku($product->getSku());
+$orderItem->setName($product->getName());
$orderItem->setQtyOrdered(1);
$orderItem->setBasePrice($product->getPrice());
$orderItem->setPrice($product->getPrice());
@@ -172,6 +174,8 @@
/** @var \Magento\Sales\Model\Order\Item $orderItem */
$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class);
$orderItem->setProductId($productId);
+ $orderItem->setSku($selectedProduct->getSku());
+ $orderItem->setName($selectedProduct->getName());
$orderItem->setQtyOrdered(1);
$orderItem->setBasePrice($selectedProduct->getPrice());
$orderItem->setPrice($selectedProduct->getPrice());
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_with_source_model.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_with_source_model.php
index 3056bf6cc5384..dd7081eaf508b 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_with_source_model.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_multiselect_attribute_with_source_model.php
@@ -8,7 +8,6 @@
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/multiselect_attribute_with_source_model.php');
-Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/ValidatorFileMock.php');
/** Create product with options and multiselect attribute */
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_min_max_sale_qty.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_min_max_sale_qty.php
new file mode 100644
index 0000000000000..e8666ad9a13cd
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_min_max_sale_qty.php
@@ -0,0 +1,44 @@
+get(ProductInterfaceFactory::class);
+/** @var ProductRepositoryInterface $productRepositoryFactory */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+
+$product = $productFactory->create();
+$product->setTypeId(Type::TYPE_SIMPLE)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([1])
+ ->setName('Simple Product min and max sale qty')
+ ->setSku('simple_product_min_max_sale_qty')
+ ->setPrice(10)
+ ->setWeight(1)
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStockData(
+ [
+ 'use_config_manage_stock' => 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
+ 'min_sale_qty' => 5,
+ 'max_sale_qty' => 20,
+ ]
+ )
+ ->setCanSaveCustomOptions(true)
+ ->setHasOptions(true);
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_min_max_sale_qty_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_min_max_sale_qty_rollback.php
new file mode 100644
index 0000000000000..bc06240d2e9a3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_min_max_sale_qty_rollback.php
@@ -0,0 +1,29 @@
+get(Registry::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $productRepository->deleteById('simple_product_min_max_sale_qty');
+} catch (NoSuchEntityException $e) {
+ //product already deleted
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_qty_increments.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_qty_increments.php
new file mode 100644
index 0000000000000..bf425e2e57874
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_qty_increments.php
@@ -0,0 +1,44 @@
+get(ProductInterfaceFactory::class);
+/** @var ProductRepositoryInterface $productRepositoryFactory */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+
+$product = $productFactory->create();
+$product->setTypeId(Type::TYPE_SIMPLE)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([1])
+ ->setName('Simple Product with qty increments')
+ ->setSku('simple_product_with_qty_increments')
+ ->setPrice(10)
+ ->setWeight(1)
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStockData(
+ [
+ 'use_config_manage_stock' => 1,
+ 'qty' => 100,
+ 'is_qty_decimal' => 0,
+ 'is_in_stock' => 1,
+ 'enable_qty_increments' => 1,
+ 'qty_increments' => 3,
+ ]
+ )
+ ->setCanSaveCustomOptions(true)
+ ->setHasOptions(true);
+$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_qty_increments_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_qty_increments_rollback.php
new file mode 100644
index 0000000000000..d6cd1212daeb8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/simple_product_with_qty_increments_rollback.php
@@ -0,0 +1,29 @@
+get(Registry::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+try {
+ $productRepository->deleteById('simple_product_with_qty_increments');
+} catch (NoSuchEntityException $e) {
+ //product already deleted
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/AddTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/AddTest.php
new file mode 100644
index 0000000000000..424fa13d74890
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/Cart/AddTest.php
@@ -0,0 +1,231 @@
+json = $this->_objectManager->get(SerializerInterface::class);
+ $this->checkoutSessionFactory = $this->_objectManager->get(CheckoutSessionFactory::class);
+ $this->productRepository = $this->_objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->executeInStoreContext = $this->_objectManager->get(ExecuteInStoreContext::class);
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ }
+
+ /**
+ * Test with simple product and activated redirect to cart
+ *
+ * @magentoDataFixture Magento/Catalog/_files/products.php
+ * @magentoConfigFixture current_store checkout/cart/redirect_to_cart 1
+ *
+ * @return void
+ */
+ public function testMessageAtAddToCartWithRedirect(): void
+ {
+ $this->prepareReferer();
+ $checkoutSession = $this->checkoutSessionFactory->create();
+ $postData = [
+ 'qty' => '1',
+ 'product' => '1',
+ 'custom_price' => 1,
+ 'isAjax' => 1,
+ ];
+ $this->dispatchAddToCartRequest($postData);
+ $this->assertEquals(
+ $this->json->serialize(['backUrl' => 'http://localhost/checkout/cart/']),
+ $this->getResponse()->getBody()
+ );
+ $this->assertSessionMessages(
+ $this->containsEqual((string)__('You added %1 to your shopping cart.', 'Simple Product')),
+ MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertCount(1, $checkoutSession->getQuote()->getItemsCollection());
+ }
+
+ /**
+ * Test with simple product and deactivated redirect to cart
+ *
+ * @magentoDataFixture Magento/Catalog/_files/products.php
+ * @magentoConfigFixture current_store checkout/cart/redirect_to_cart 0
+ *
+ * @return void
+ */
+ public function testMessageAtAddToCartWithoutRedirect(): void
+ {
+ $this->prepareReferer();
+ $checkoutSession = $this->checkoutSessionFactory->create();
+ $postData = [
+ 'qty' => '1',
+ 'product' => '1',
+ 'custom_price' => 1,
+ 'isAjax' => 1,
+ ];
+ $this->dispatchAddToCartRequest($postData);
+ $this->assertFalse($this->getResponse()->isRedirect());
+ $this->assertEquals('[]', $this->getResponse()->getBody());
+ $message = (string)__(
+ 'You added %1 to your
shopping cart.',
+ 'Simple Product',
+ 'http://localhost/checkout/cart/'
+ );
+ $this->assertSessionMessages(
+ $this->containsEqual("\n" . $message),
+ MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertCount(1, $checkoutSession->getQuote()->getItemsCollection());
+ }
+
+ /**
+ * @dataProvider wrongParamsDataProvider
+ *
+ * @param array $params
+ * @return void
+ */
+ public function testWithWrongParams(array $params): void
+ {
+ $this->prepareReferer();
+ $this->dispatchAddToCartRequest($params);
+ $this->assertRedirect($this->stringContains('http://localhost/test'));
+ }
+
+ /**
+ * @return array
+ */
+ public function wrongParamsDataProvider(): array
+ {
+ return [
+ 'empty_params' => ['params' => []],
+ 'with_not_existing_product_id' => ['params' => ['product' => 989]],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ *
+ * @return void
+ */
+ public function testAddProductFromUnavailableWebsite(): void
+ {
+ $this->prepareReferer();
+ $product = $this->productRepository->get('simple-1');
+ $postData = ['product' => $product->getId()];
+ $this->executeInStoreContext->execute('fixture_second_store', [$this, 'dispatchAddToCartRequest'], $postData);
+ $this->assertRedirect($this->stringContains('http://localhost/test'));
+ $message = $this->escaper->escapeHtml(
+ (string)__('The product wasn\'t found. Verify the product and try again.')
+ );
+ $this->assertSessionMessages($this->containsEqual($message), MessageInterface::TYPE_ERROR);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ *
+ * @return void
+ */
+ public function testAddProductWithUnavailableQty(): void
+ {
+ $product = $this->productRepository->get('simple-1');
+ $postData = ['product' => $product->getId(), 'qty' => '1000'];
+ $this->dispatchAddToCartRequest($postData);
+ $message = (string)__('The requested qty is not available');
+ $this->assertSessionMessages($this->containsEqual($message), MessageInterface::TYPE_ERROR);
+ $this->assertRedirect($this->stringContains($product->getProductUrl()));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/products_related_multiple.php
+ *
+ * @return void
+ */
+ public function testAddProductWithRelated(): void
+ {
+ $this->prepareReferer();
+ $checkoutSession = $this->checkoutSessionFactory->create();
+ $product = $this->productRepository->get('simple_with_cross');
+ $params = [
+ 'product' => $product->getId(),
+ 'related_product' => implode(',', $product->getRelatedProductIds()),
+ ];
+ $this->dispatchAddToCartRequest($params);
+ $this->assertCount(3, $checkoutSession->getQuote()->getItemsCollection());
+ $message = (string)__(
+ 'You added %1 to your
shopping cart.',
+ $product->getName(),
+ 'http://localhost/checkout/cart/'
+ );
+ $this->assertSessionMessages(
+ $this->containsEqual("\n" . $message),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * Dispatch add product to cart request.
+ *
+ * @param array $postData
+ * @return void
+ */
+ public function dispatchAddToCartRequest(array $postData = []): void
+ {
+ $this->getRequest()->setPostValue($postData);
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->dispatch('checkout/cart/add');
+ }
+
+ /**
+ * Prepare referer to test.
+ *
+ * @return void
+ */
+ private function prepareReferer(): void
+ {
+ $parameters = $this->_objectManager->create(Parameters::class);
+ $parameters->set('HTTP_REFERER', 'http://localhost/test');
+ $this->getRequest()->setServer($parameters);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php
index a9714a17ffe4f..fd89229bb73be 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Controller/CartTest.php
@@ -352,77 +352,6 @@ public function addAddProductDataProvider()
];
}
- /**
- * Test for \Magento\Checkout\Controller\Cart\Add::execute() with simple product and activated redirect to cart
- *
- * @magentoDataFixture Magento/Catalog/_files/products.php
- * @magentoConfigFixture current_store checkout/cart/redirect_to_cart 1
- * @magentoAppIsolation enabled
- */
- public function testMessageAtAddToCartWithRedirect()
- {
- $formKey = $this->_objectManager->get(FormKey::class);
- $postData = [
- 'qty' => '1',
- 'product' => '1',
- 'custom_price' => 1,
- 'form_key' => $formKey->getFormKey(),
- 'isAjax' => 1
- ];
- \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend');
- $this->getRequest()->setPostValue($postData);
- $this->getRequest()->setMethod('POST');
-
- $this->dispatch('checkout/cart/add');
-
- $this->assertEquals(
- '{"backUrl":"http:\/\/localhost\/index.php\/checkout\/cart\/"}',
- $this->getResponse()->getBody()
- );
-
- $this->assertSessionMessages(
- $this->containsEqual(
- 'You added Simple Product to your shopping cart.'
- ),
- \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
- );
- }
-
- /**
- * Test for \Magento\Checkout\Controller\Cart\Add::execute() with simple product and deactivated redirect to cart
- *
- * @magentoDataFixture Magento/Catalog/_files/products.php
- * @magentoConfigFixture current_store checkout/cart/redirect_to_cart 0
- * @magentoAppIsolation enabled
- */
- public function testMessageAtAddToCartWithoutRedirect()
- {
- $formKey = $this->_objectManager->get(FormKey::class);
- $postData = [
- 'qty' => '1',
- 'product' => '1',
- 'custom_price' => 1,
- 'form_key' => $formKey->getFormKey(),
- 'isAjax' => 1
- ];
- \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('frontend');
- $this->getRequest()->setPostValue($postData);
- $this->getRequest()->setMethod('POST');
-
- $this->dispatch('checkout/cart/add');
-
- $this->assertFalse($this->getResponse()->isRedirect());
- $this->assertEquals('[]', $this->getResponse()->getBody());
-
- $this->assertSessionMessages(
- $this->containsEqual(
- "\n" . 'You added Simple Product to your ' .
- '
shopping cart.'
- ),
- \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
- );
- }
-
/**
* @covers \Magento\Checkout\Controller\Cart\Addgroup::execute()
*
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/CartTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/CartTest.php
index f534904e9db6b..c8f3bc891a413 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Model/CartTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/CartTest.php
@@ -3,51 +3,163 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Checkout\Model;
+use Magento\Catalog\Api\Data\ProductInterfaceFactory;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Checkout\Model\Session as CheckoutSession;
+use Magento\Checkout\Model\SessionFactory as CheckoutSessionFactory;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Quote\Api\CartRepositoryInterface;
+use Magento\Quote\Api\Data\CartInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Store\ExecuteInStoreContext;
+use PHPUnit\Framework\TestCase;
-class CartTest extends \PHPUnit\Framework\TestCase
+/**
+ * Test for checkout cart model.
+ *
+ * @see \Magento\Checkout\Model\Cart
+ * @magentoDbIsolation enabled
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class CartTest extends TestCase
{
+ /** @var ObjectManagerInterface */
+ private $objectManager;
+
+ /** @var CartFactory */
+ private $cartFactory;
+
+ /** @var ProductInterfaceFactory */
+ private $productFactory;
+
+ /** @var ProductRepositoryInterface */
+ private $productRepository;
+
+ /** @var ExecuteInStoreContext */
+ private $executeInStoreContext;
+
+ /** @var CheckoutSession */
+ private $checkoutSession;
+
+ /** @var CartInterface */
+ private $quote;
+
+ /** @var CartRepositoryInterface */
+ private $quoteRepository;
+
/**
- * @var Cart
+ * @inheritdoc
*/
- private $cart;
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->cartFactory = $this->objectManager->get(CartFactory::class);
+ $this->productFactory = $this->objectManager->get(ProductInterfaceFactory::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->executeInStoreContext = $this->objectManager->get(ExecuteInStoreContext::class);
+ $this->checkoutSession = $this->objectManager->get(CheckoutSessionFactory::class)->create();
+ $this->quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
+ }
/**
- * @var ProductRepositoryInterface
+ * @inheritdoc
*/
- private $productRepository;
-
- protected function setUp(): void
+ protected function tearDown(): void
{
- $this->cart = Bootstrap::getObjectManager()->create(Cart::class);
- $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class);
+ if ($this->quote instanceof CartInterface) {
+ $this->quoteRepository->delete($this->quote);
+ }
+
+ parent::tearDown();
}
/**
- * @magentoDataFixture Magento/Checkout/_files/simple_product.php
* @magentoDataFixture Magento/Checkout/_files/set_product_min_in_cart.php
- * @magentoDbIsolation enabled
* @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testAddProductWithLowerQty()
+ public function testAddProductWithLowerQty(): void
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
- $this->expectExceptionMessage('The fewest you may purchase is 3');
+ $cart = $this->cartFactory->create();
+ $this->expectException(LocalizedException::class);
+ $this->expectExceptionMessage((string)__('The fewest you may purchase is %1', 3));
$product = $this->productRepository->get('simple');
- $this->cart->addProduct($product->getId(), ['qty' => 1]);
+ $cart->addProduct($product->getId(), ['qty' => 1]);
}
/**
- * @magentoDataFixture Magento/Checkout/_files/simple_product.php
* @magentoDataFixture Magento/Checkout/_files/set_product_min_in_cart.php
- * @magentoDbIsolation enabled
+ *
+ * @return void
+ */
+ public function testAddProductWithNoQty(): void
+ {
+ $cart = $this->cartFactory->create();
+ $product = $this->productRepository->get('simple');
+ $cart->addProduct($product->getId(), [])->save();
+ $this->quote = $cart->getQuote();
+ $this->assertCount(1, $cart->getItems());
+ $this->assertEquals($product->getId(), $this->checkoutSession->getLastAddedProductId());
+ }
+
+ /**
+ * @return void
+ */
+ public function testAddNotExistingProduct(): void
+ {
+ $product = $this->productFactory->create();
+ $this->expectExceptionObject(
+ new LocalizedException(__('The product wasn\'t found. Verify the product and try again.'))
+ );
+ $this->cartFactory->create()->addProduct($product);
+ }
+
+ /**
+ * @return void
+ */
+ public function testAddNotExistingProductId(): void
+ {
+ $this->expectExceptionObject(
+ new LocalizedException(__('The product wasn\'t found. Verify the product and try again.'))
+ );
+ $this->cartFactory->create()->addProduct(989);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ *
+ * @return void
+ */
+ public function testAddProductFromUnavailableWebsite(): void
+ {
+ $product = $this->productRepository->get('simple');
+ $this->expectExceptionObject(
+ new LocalizedException(__('The product wasn\'t found. Verify the product and try again.'))
+ );
+ $this->executeInStoreContext
+ ->execute('fixture_second_store', [$this->cartFactory->create(), 'addProduct'], $product->getId());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ *
+ * @return void
*/
- public function testAddProductWithNoQty()
+ public function testAddProductWithInvalidRequest(): void
{
$product = $this->productRepository->get('simple');
- $this->cart->addProduct($product->getId(), []);
+ $message = __('We found an invalid request for adding product to quote.');
+ $this->expectExceptionObject(new LocalizedException($message));
+ $this->cartFactory->create()->addProduct($product->getId(), '');
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
index 41bf18619332a..32968572b4ac8 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
@@ -3,6 +3,8 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Checkout\Model;
use Magento\Catalog\Api\Data\ProductTierPriceInterface;
@@ -10,22 +12,24 @@
use Magento\Catalog\Model\Product\Attribute\Source\Status;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\Session as CustomerSession;
-use Magento\Framework\Api\FilterBuilder;
-use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\ObjectManagerInterface;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartInterface;
-use Magento\Quote\Model\Quote;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
+use PHPUnit\Framework\TestCase;
/**
* Checkout Session model test.
*
+ * @see \Magento\Checkout\Model\Session
+ * @magentoDbIsolation enabled
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class SessionTest extends \PHPUnit\Framework\TestCase
+class SessionTest extends TestCase
{
/**
- * @var \Magento\Framework\ObjectManagerInterface
+ * @var ObjectManagerInterface
*/
private $objectManager;
@@ -45,40 +49,78 @@ class SessionTest extends \PHPUnit\Framework\TestCase
private $checkoutSession;
/**
- * @return void
+ * @var GetQuoteByReservedOrderId
+ */
+ private $getQuoteByReservedOrderId;
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
+ * @var CartRepositoryInterface
+ */
+ private $quoteRepository;
+
+ /**
+ * @var CartInterface
+ */
+ private $quote;
+
+ /**
+ * @inheritdoc
*/
protected function setUp(): void
{
+ parent::setUp();
+
$this->objectManager = Bootstrap::getObjectManager();
$this->customerRepository = $this->objectManager->create(CustomerRepositoryInterface::class);
$this->customerSession = $this->objectManager->get(CustomerSession::class);
- $this->checkoutSession = $this->objectManager->create(Session::class);
+ $this->checkoutSession = $this->objectManager->get(Session::class);
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ if ($this->quote instanceof CartInterface) {
+ $this->quoteRepository->delete($this->quote);
+ }
+ $this->customerSession->setCustomerId(null);
+ $this->checkoutSession->clearQuote();
+ $this->checkoutSession->setCustomerData(null);
+
+ parent::tearDown();
}
/**
* Tests that quote items and totals are correct when product becomes unavailable.
*
- * @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testGetQuoteWithUnavailableProduct()
+ public function testGetQuoteWithUnavailableProduct(): void
{
$reservedOrderId = 'test01';
$quoteGrandTotal = 10;
-
- $quote = $this->getQuote($reservedOrderId);
+ $quote = $this->getQuoteByReservedOrderId->execute($reservedOrderId);
$this->assertEquals(1, $quote->getItemsCount());
$this->assertCount(1, $quote->getItems());
$this->assertEquals($quoteGrandTotal, $quote->getShippingAddress()->getBaseGrandTotal());
-
- $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
- $product = $productRepository->get('simple');
+ $product = $this->productRepository->get('simple');
$product->setStatus(Status::STATUS_DISABLED);
- $productRepository->save($product);
+ $this->productRepository->save($product);
$this->checkoutSession->setQuoteId($quote->getId());
$quote = $this->checkoutSession->getQuote();
-
$this->assertEquals(0, $quote->getItemsCount());
$this->assertEmpty($quote->getItems());
$this->assertEquals(0, $quote->getShippingAddress()->getBaseGrandTotal());
@@ -90,15 +132,15 @@ public function testGetQuoteWithUnavailableProduct()
* Expected result - quote object should be loaded and customer data should be set to it.
*
* @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
+ *
+ * @return void
*/
- public function testGetQuoteNotInitializedCustomerSet()
+ public function testGetQuoteNotInitializedCustomerSet(): void
{
$customer = $this->customerRepository->getById(1);
$this->checkoutSession->setCustomerData($customer);
-
- /** Execute SUT */
$quote = $this->checkoutSession->getQuote();
- $this->_validateCustomerDataInQuote($quote);
+ $this->validateCustomerDataInQuote($quote);
}
/**
@@ -107,36 +149,29 @@ public function testGetQuoteNotInitializedCustomerSet()
* Expected result - quote object should be loaded and customer data should be set to it.
*
* @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
- * @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testGetQuoteNotInitializedCustomerLoggedIn()
+ public function testGetQuoteNotInitializedCustomerLoggedIn(): void
{
$customer = $this->customerRepository->getById(1);
$this->customerSession->setCustomerDataObject($customer);
-
- /** Execute SUT */
$quote = $this->checkoutSession->getQuote();
- $this->_validateCustomerDataInQuote($quote);
+ $this->validateCustomerDataInQuote($quote);
}
/**
* @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
- * @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testGetQuoteWithMismatchingSession()
+ public function testGetQuoteWithMismatchingSession(): void
{
- /** @var Quote $quote */
- $quote = Bootstrap::getObjectManager()->create(Quote::class);
- /** @var \Magento\Quote\Model\ResourceModel\Quote $quoteResource */
- $quoteResource = Bootstrap::getObjectManager()->create(\Magento\Quote\Model\ResourceModel\Quote::class);
- $quoteResource->load($quote, 'test01', 'reserved_order_id');
-
- // Customer on quote is not logged in
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
$this->checkoutSession->setQuoteId($quote->getId());
-
- $sessionQuote = $this->checkoutSession->getQuote();
- $this->assertEmpty($sessionQuote->getCustomerId());
- $this->assertNotEquals($quote->getId(), $sessionQuote->getId());
+ $this->quote = $this->checkoutSession->getQuote();
+ $this->assertEmpty($this->quote->getCustomerId());
+ $this->assertNotEquals($quote->getId(), $this->quote->getId());
}
/**
@@ -150,96 +185,90 @@ public function testGetQuoteWithMismatchingSession()
* Quote which is set to checkout session should contain customer data
*
* @magentoDataFixture Magento/Customer/_files/customer.php
- * @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testLoadCustomerQuoteCustomerWithoutQuote()
+ public function testLoadCustomerQuoteCustomerWithoutQuote(): void
{
- $quote = $this->checkoutSession->getQuote();
- $this->assertEmpty($quote->getCustomerId(), 'Precondition failed: Customer data must not be set to quote');
- $this->assertEmpty($quote->getCustomerEmail(), 'Precondition failed: Customer data must not be set to quote');
-
+ $this->quote = $this->checkoutSession->getQuote();
+ $this->assertEmpty(
+ $this->quote->getCustomerId(),
+ 'Precondition failed: Customer data must not be set to quote'
+ );
+ $this->assertEmpty(
+ $this->quote->getCustomerEmail(),
+ 'Precondition failed: Customer data must not be set to quote'
+ );
$customer = $this->customerRepository->getById(1);
$this->customerSession->setCustomerDataObject($customer);
-
- /** Ensure that customer data is still unavailable before SUT invocation */
- $quote = $this->checkoutSession->getQuote();
- $this->assertEmpty($quote->getCustomerEmail(), 'Precondition failed: Customer data must not be set to quote');
-
- /** Execute SUT */
+ $this->quote = $this->checkoutSession->getQuote();
+ $this->assertEmpty(
+ $this->quote->getCustomerEmail(),
+ 'Precondition failed: Customer data must not be set to quote'
+ );
$this->checkoutSession->loadCustomerQuote();
- $quote = $this->checkoutSession->getQuote();
- $this->_validateCustomerDataInQuote($quote);
+ $this->quote = $this->checkoutSession->getQuote();
+ $this->validateCustomerDataInQuote($this->quote);
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/Sales/_files/quote.php
+ *
+ * @return void
*/
- public function testGetQuoteWithProductWithTierPrice()
+ public function testGetQuoteWithProductWithTierPrice(): void
{
$reservedOrderId = 'test01';
$customerGroupId = 1;
$tierPriceQty = 1;
$tierPriceValue = 9;
-
- $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
- $product = $productRepository->get('simple');
- $tierPrice = $this->objectManager->create(ProductTierPriceInterface::class)
+ $product = $this->productRepository->get('simple');
+ $tierPrice = $this->objectManager->get(ProductTierPriceInterface::class)
->setCustomerGroupId($customerGroupId)
->setQty($tierPriceQty)
->setValue($tierPriceValue);
$product->setTierPrices([$tierPrice]);
- $productRepository->save($product);
-
- $quote = $this->getQuote($reservedOrderId);
+ $this->productRepository->save($product);
+ $quote = $this->getQuoteByReservedOrderId->execute($reservedOrderId);
$this->checkoutSession->setQuoteId($quote->getId());
-
$quote = $this->checkoutSession->getQuote();
$item = $quote->getItems()[0];
- /** @var \Magento\Catalog\Model\Product $quoteProduct */
$quoteProduct = $item->getProduct();
$this->assertEquals(10, $quoteProduct->getTierPrice($tierPriceQty));
-
$customer = $this->customerRepository->getById(1);
$this->customerSession->setCustomerDataAsLoggedIn($customer);
-
$quote = $this->checkoutSession->getQuote();
$item = $quote->getItems()[0];
- /** @var \Magento\Catalog\Model\Product $quoteProduct */
$quoteProduct = $item->getProduct();
$this->assertEquals($tierPriceValue, $quoteProduct->getTierPrice(1));
}
/**
- * Returns quote by reserved order id.
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
*
- * @param string $reservedOrderId
- * @return CartInterface
+ * @return void
*/
- private function getQuote(string $reservedOrderId): CartInterface
+ public function testMergeGuestQuoteWithCustomerQuote(): void
{
- $filterBuilder = $this->objectManager->create(FilterBuilder::class);
- $filter = $filterBuilder->setField('reserved_order_id')
- ->setConditionType('=')
- ->setValue($reservedOrderId)
- ->create();
- $searchCriteriaBuilder = $this->objectManager->create(SearchCriteriaBuilder::class);
- $searchCriteria = $searchCriteriaBuilder->addFilters([$filter])
- ->create();
- $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
- $searchResult = $quoteRepository->getList($searchCriteria);
- /** @var CartInterface[] $items */
- $items = $searchResult->getItems();
-
- return \array_values($items)[0];
+ $guestQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_simple_product_without_address');
+ $customerQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($guestQuote->getId());
+ $this->customerSession->setCustomerId(1);
+ $updatedQuote = $this->checkoutSession->loadCustomerQuote()->getQuote();
+ $this->assertNull($this->getQuoteByReservedOrderId->execute('test_order_with_simple_product_without_address'));
+ $this->assertEquals($customerQuote->getId(), $updatedQuote->getId());
+ $this->assertCount(2, $updatedQuote->getItems());
}
/**
* Ensure that quote has customer data specified in customer fixture.
*
- * @param \Magento\Quote\Model\Quote $quote
+ * @param CartInterface $quote
+ * @return void
*/
- protected function _validateCustomerDataInQuote($quote)
+ private function validateCustomerDataInQuote(CartInterface $quote): void
{
$customerIdFromFixture = 1;
$customerEmailFromFixture = 'customer@example.com';
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_with_items_simple_product_options.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_with_items_simple_product_options.php
index 66d984301d14f..f0ba56f7179aa 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_with_items_simple_product_options.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_with_items_simple_product_options.php
@@ -10,7 +10,7 @@
use Magento\Catalog\Model\Product\Option;
use Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile;
use Magento\Catalog\Model\Product\Option\Value;
-use Magento\Checkout\_files\ValidatorFileMock;
+use Magento\TestFramework\Catalog\Model\Product\Option\Type\File\ValidatorFileMock;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\DataObject;
use Magento\Quote\Api\CartRepositoryInterface;
@@ -83,7 +83,7 @@
$itemsOptions[$dropDownValue->getTitle()] = $options;
}
-$validatorFileMock = (new ValidatorFileMock())->getInstance();
+$validatorFileMock = $objectManager->get(ValidatorFileMock::class)->getInstance();
$objectManager->addSharedInstance($validatorFileMock, ValidatorFile::class);
$quote->setStoreId($storeManager->getStore()->getId())
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_items_and_custom_options_saved.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_items_and_custom_options_saved.php
index 3abe6b21f110e..2293f0662c699 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_items_and_custom_options_saved.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_items_and_custom_options_saved.php
@@ -4,7 +4,12 @@
* See COPYING.txt for license details.
*/
-use Magento\Checkout\_files\ValidatorFileMock;
+use Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile;
+use Magento\Framework\DataObject;
+use Magento\Quote\Model\QuoteIdMask;
+use Magento\Quote\Model\QuoteIdMaskFactory;
+use Magento\Quote\Model\QuoteRepository;
+use Magento\TestFramework\Catalog\Model\Product\Option\Type\File\ValidatorFileMock;
use Magento\Quote\Model\QuoteFactory;
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
use Magento\TestFramework\Helper\Bootstrap;
@@ -12,7 +17,6 @@
Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_address.php');
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_with_options.php');
-Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/ValidatorFileMock.php');
$objectManager = Bootstrap::getObjectManager();
/** @var QuoteFactory $quoteFactory */
@@ -45,18 +49,18 @@
$options[$option->getId()] = $value;
}
-$requestInfo = new \Magento\Framework\DataObject(['qty' => 1, 'options' => $options]);
-$validatorFile = (new ValidatorFileMock())->getInstance();
-$objectManager->addSharedInstance($validatorFile, \Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile::class);
+$requestInfo = new DataObject(['qty' => 1, 'options' => $options]);
+$validatorFile = $objectManager->get(ValidatorFileMock::class)->getInstance();
+$objectManager->addSharedInstance($validatorFile, ValidatorFile::class);
$quote->setReservedOrderId('test_order_item_with_items_and_custom_options');
$quote->addProduct($product, $requestInfo);
$quote->collectTotals();
-$objectManager->get(\Magento\Quote\Model\QuoteRepository::class)->save($quote);
+$objectManager->get(QuoteRepository::class)->save($quote);
-/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
+/** @var QuoteIdMask $quoteIdMask */
$quoteIdMask = Bootstrap::getObjectManager()
- ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ ->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->setQuoteId($quote->getId());
$quoteIdMask->setDataChanges(true);
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php
index d58aa4e049b78..4d9178f1a0659 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/PageTest.php
@@ -4,29 +4,36 @@
* See COPYING.txt for license details.
*/
-/**
- * Test class for \Magento\Cms\Controller\Page.
- */
namespace Magento\Cms\Controller;
use Magento\Cms\Api\GetPageByIdentifierInterface;
+use Magento\Cms\Model\Page\CustomLayoutManagerInterface;
use Magento\Framework\View\LayoutInterface;
-use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Cms\Model\CustomLayoutManager;
+use Magento\TestFramework\TestCase\AbstractController;
-class PageTest extends \Magento\TestFramework\TestCase\AbstractController
+/**
+ * Test for \Magento\Cms\Controller\Page\View class.
+ */
+class PageTest extends AbstractController
{
+ /**
+ * @var GetPageByIdentifierInterface
+ */
+ private $pageRetriever;
+
/**
* @inheritDoc
*/
protected function setUp(): void
{
- Bootstrap::getObjectManager()->configure([
+ parent::setUp();
+ $this->_objectManager->configure([
'preferences' => [
- \Magento\Cms\Model\Page\CustomLayoutManagerInterface::class =>
- \Magento\TestFramework\Cms\Model\CustomLayoutManager::class
+ CustomLayoutManagerInterface::class => CustomLayoutManager::class,
]
]);
- parent::setUp();
+ $this->pageRetriever = $this->_objectManager->get(GetPageByIdentifierInterface::class);
}
public function testViewAction()
@@ -51,9 +58,7 @@ public function testViewRedirectWithTrailingSlash()
public function testAddBreadcrumbs()
{
$this->dispatch('/enable-cookies');
- $layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\Framework\View\LayoutInterface::class
- );
+ $layout = $this->_objectManager->get(LayoutInterface::class);
$breadcrumbsBlock = $layout->getBlock('breadcrumbs');
$this->assertStringContainsString($breadcrumbsBlock->toHtml(), $this->getResponse()->getBody());
}
@@ -90,12 +95,10 @@ public static function cmsPageWithSystemRouteFixture()
*/
public function testCustomHandles(): void
{
- /** @var GetPageByIdentifierInterface $pageFinder */
- $pageFinder = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class);
- $page = $pageFinder->execute('test_custom_layout_page_3', 0);
- $this->dispatch('/cms/page/view/page_id/' .$page->getId());
+ $page = $this->pageRetriever->execute('test_custom_layout_page_3', 0);
+ $this->dispatch('/cms/page/view/page_id/' . $page->getId());
/** @var LayoutInterface $layout */
- $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class);
+ $layout = $this->_objectManager->get(LayoutInterface::class);
$handles = $layout->getUpdate()->getHandles();
$this->assertContains('cms_page_view_selectable_test_custom_layout_page_3_test_selected', $handles);
}
@@ -111,8 +114,37 @@ public function testHomePageCustomHandles(): void
{
$this->dispatch('/');
/** @var LayoutInterface $layout */
- $layout = Bootstrap::getObjectManager()->get(LayoutInterface::class);
+ $layout = $this->_objectManager->get(LayoutInterface::class);
$handles = $layout->getUpdate()->getHandles();
$this->assertContains('cms_page_view_selectable_home_page_custom_layout', $handles);
}
+
+ /**
+ * Tests page renders even with unavailable custom page layout.
+ *
+ * @magentoDataFixture Magento/Cms/Fixtures/page_list.php
+ * @dataProvider pageLayoutDataProvider
+ * @param string $pageIdentifier
+ * @return void
+ */
+ public function testPageWithCustomLayout(string $pageIdentifier): void
+ {
+ $page = $this->pageRetriever->execute($pageIdentifier, 0);
+ $this->dispatch('/cms/page/view/page_id/' . $page->getId());
+ $this->assertStringContainsString(
+ '
',
+ $this->getResponse()->getBody()
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function pageLayoutDataProvider(): array
+ {
+ return [
+ 'Page with 1column layout' => ['page-with-1column-layout'],
+ 'Page with unavailable layout' => ['page-with-unavailable-layout']
+ ];
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php
index ae431f5c4cf1a..2fa0bf3a5bc13 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list.php
@@ -14,15 +14,27 @@
$data = [
[
'title' => 'simplePage',
- 'is_active' => 1
+ 'is_active' => 1,
],
[
'title' => 'simplePage01',
- 'is_active' => 1
+ 'is_active' => 1,
],
[
'title' => '01simplePage',
- 'is_active' => 1
+ 'is_active' => 1,
+ ],
+ [
+ 'title' => 'Page with 1column layout',
+ 'is_active' => 1,
+ 'content' => 'Test Page Content
',
+ 'page_layout' => '1column',
+ ],
+ [
+ 'title' => 'Page with unavailable layout',
+ 'content' => 'Test Page Content
',
+ 'is_active' => 1,
+ 'page_layout' => 'unavailable-layout',
],
];
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php
index 261cdba589653..00bec67bcfefc 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Fixtures/page_list_rollback.php
@@ -16,7 +16,18 @@
/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class);
-$searchCriteria = $searchCriteriaBuilder->addFilter('title', ['simplePage', 'simplePage01', '01simplePage'], 'in')
+$searchCriteria = $searchCriteriaBuilder
+ ->addFilter(
+ 'title',
+ [
+ 'simplePage',
+ 'simplePage01',
+ '01simplePage',
+ 'Page with 1column layout',
+ 'Page with unavailable layout',
+ ],
+ 'in'
+ )
->create();
$result = $pageRepository->getList($searchCriteria);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php
index 8aab31b921007..325ab7db23aaf 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableProductPriceTest.php
@@ -14,7 +14,10 @@
use Magento\Framework\Registry;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\View\Result\Page;
+use Magento\Framework\View\Result\PageFactory;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Store\ExecuteInStoreContext;
use PHPUnit\Framework\TestCase;
/**
@@ -44,6 +47,12 @@ class ConfigurableProductPriceTest extends TestCase
/** @var SerializerInterface */
private $json;
+ /** @var StoreManagerInterface */
+ private $storeManager;
+
+ /** @var ExecuteInStoreContext */
+ private $executeInStoreContext;
+
/**
* @inheritdoc
*/
@@ -53,11 +62,13 @@ protected function setUp(): void
$this->objectManager = Bootstrap::getObjectManager();
$this->registry = $this->objectManager->get(Registry::class);
- $this->page = $this->objectManager->get(Page::class);
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
$this->productRepository->cleanCache();
$this->productCustomOption = $this->objectManager->get(ProductCustomOptionInterface::class);
$this->json = $this->objectManager->get(SerializerInterface::class);
+ $this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $this->executeInStoreContext = $this->objectManager->get(ExecuteInStoreContext::class);
}
/**
@@ -78,7 +89,20 @@ protected function tearDown(): void
*/
public function testConfigurablePrice(): void
{
- $this->assertPrice($this->processPriceView('configurable'), 10.00);
+ $this->assertPrice('configurable', 10.00);
+ }
+
+ /**
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website.php
+ * @magentoDbIsolation disabled
+ *
+ * @return void
+ */
+ public function testConfigurablePriceOnSecondWebsite(): void
+ {
+ $this->executeInStoreContext->execute('fixture_second_store', [$this, 'assertPrice'], 'configurable', 10.00);
+ $this->resetPageLayout();
+ $this->assertPrice('configurable', 150.00);
}
/**
@@ -88,7 +112,7 @@ public function testConfigurablePrice(): void
*/
public function testConfigurablePriceWithDisabledFirstChild(): void
{
- $this->assertPrice($this->processPriceView('configurable'), 20.00);
+ $this->assertPrice('configurable', 20.00);
}
/**
@@ -98,7 +122,7 @@ public function testConfigurablePriceWithDisabledFirstChild(): void
*/
public function testConfigurablePriceWithOutOfStockFirstChild(): void
{
- $this->assertPrice($this->processPriceView('configurable'), 20.00);
+ $this->assertPrice('configurable', 20.00);
}
/**
@@ -110,7 +134,7 @@ public function testConfigurablePriceWithOutOfStockFirstChild(): void
*/
public function testConfigurablePriceWithCatalogRule(): void
{
- $this->assertPrice($this->processPriceView('configurable'), 9.00);
+ $this->assertPrice('configurable', 9.00);
}
/**
@@ -120,7 +144,7 @@ public function testConfigurablePriceWithCatalogRule(): void
*/
public function testConfigurablePriceWithCustomOption(): void
{
- $product = $this->productRepository->get('configurable');
+ $product = $this->getProduct('configurable');
$this->registerProduct($product);
$this->preparePageLayout();
$customOptionsBlock = $this->page->getLayout()
@@ -162,6 +186,16 @@ private function preparePageLayout(): void
$this->page->getLayout()->generateXml();
}
+ /**
+ * Reset layout page to get new block html.
+ *
+ * @return void
+ */
+ private function resetPageLayout(): void
+ {
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
+ }
+
/**
* Process view product final price block html.
*
@@ -170,7 +204,7 @@ private function preparePageLayout(): void
*/
private function processPriceView(string $sku): string
{
- $product = $this->productRepository->get($sku);
+ $product = $this->getProduct($sku);
$this->registerProduct($product);
$this->preparePageLayout();
@@ -180,12 +214,13 @@ private function processPriceView(string $sku): string
/**
* Assert that html contain price label and expected final price amount.
*
- * @param string $priceBlockHtml
+ * @param string $sku
* @param float $expectedPrice
* @return void
*/
- private function assertPrice(string $priceBlockHtml, float $expectedPrice): void
+ public function assertPrice(string $sku, float $expectedPrice): void
{
+ $priceBlockHtml = $this->processPriceView($sku);
$regexp = '/As low as<\/span>.*';
$regexp .= '\$%.2f<\/span><\/span>/';
$this->assertMatchesRegularExpression(
@@ -208,4 +243,15 @@ private function assertJsonConfig(string $config, string $expectedPrice, int $op
$this->assertNotNull($price);
$this->assertEquals($expectedPrice, $price);
}
+
+ /**
+ * Loads product by sku.s
+ *
+ * @param string $sku
+ * @return ProductInterface
+ */
+ private function getProduct(string $sku): ProductInterface
+ {
+ return $this->productRepository->get($sku, false, $this->storeManager->getStore()->getId(), true);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php
index d577994cdc45b..bf910359b893b 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableViewOnCategoryPageTest.php
@@ -7,11 +7,18 @@
namespace Magento\ConfigurableProduct\Block\Product\View\Type;
+use Magento\Catalog\Api\CategoryRepositoryInterface;
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Block\Product\ListProduct;
use Magento\Eav\Model\Entity\Collection\AbstractCollection;
use Magento\Framework\ObjectManagerInterface;
-use Magento\Framework\View\LayoutInterface;
+use Magento\Framework\Registry;
+use Magento\Framework\View\Result\Page;
+use Magento\Framework\View\Result\PageFactory;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Store\ExecuteInStoreContext;
use PHPUnit\Framework\TestCase;
/**
@@ -21,17 +28,30 @@
* @magentoAppIsolation enabled
* @magentoAppArea frontend
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ConfigurableViewOnCategoryPageTest extends TestCase
{
/** @var ObjectManagerInterface */
private $objectManager;
- /** @var LayoutInterface */
- private $layout;
+ /** @var ProductRepositoryInterface */
+ private $productRepository;
- /** @var ListProduct $listingBlock */
- private $listingBlock;
+ /** @var CategoryRepositoryInterface */
+ private $categoryRepository;
+
+ /** @var Page */
+ private $page;
+
+ /** @var Registry */
+ private $registry;
+
+ /** @var StoreManagerInterface */
+ private $storeManager;
+
+ /** @var ExecuteInStoreContext */
+ private $executeInStoreContext;
/**
* @inheritdoc
@@ -41,9 +61,22 @@ protected function setUp(): void
parent::setUp();
$this->objectManager = Bootstrap::getObjectManager();
- $this->layout = $this->objectManager->get(LayoutInterface::class);
- $this->listingBlock = $this->layout->createBlock(ListProduct::class);
- $this->listingBlock->setCategoryId(333);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->categoryRepository = $this->objectManager->get(CategoryRepositoryInterface::class);
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $this->executeInStoreContext = $this->objectManager->get(ExecuteInStoreContext::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('current_category');
+ parent::tearDown();
}
/**
@@ -53,8 +86,8 @@ protected function setUp(): void
*/
public function testOutOfStockProductWithEnabledConfigView(): void
{
- $collection = $this->listingBlock->getLoadedProductCollection();
- $this->assertCollectionSize(1, $collection);
+ $this->preparePageLayout();
+ $this->assertCollectionSize(1, $this->getListingBlock()->getLoadedProductCollection());
}
/**
@@ -64,8 +97,50 @@ public function testOutOfStockProductWithEnabledConfigView(): void
*/
public function testOutOfStockProductWithDisabledConfigView(): void
{
- $collection = $this->listingBlock->getLoadedProductCollection();
- $this->assertCollectionSize(0, $collection);
+ $this->preparePageLayout();
+ $this->assertCollectionSize(0, $this->getListingBlock()->getLoadedProductCollection());
+ }
+
+ /**
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_category.php
+ *
+ * @return void
+ */
+ public function testCheckConfigurablePrice(): void
+ {
+ $this->assertProductPrice('configurable', 'As low as $10.00');
+ }
+
+ /**
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website.php
+ *
+ * @return void
+ */
+ public function testCheckConfigurablePriceOnSecondWebsite(): void
+ {
+ $this->executeInStoreContext->execute(
+ 'fixture_second_store',
+ [$this, 'assertProductPrice'],
+ 'configurable',
+ __('As low as') . ' $10.00'
+ );
+ $this->resetPageLayout();
+ $this->assertProductPrice('configurable', __('As low as') . ' $150.00');
+ }
+
+ /**
+ * Checks product price.
+ *
+ * @param string $sku
+ * @param string $priceString
+ * @return void
+ */
+ public function assertProductPrice(string $sku, string $priceString): void
+ {
+ $this->preparePageLayout();
+ $this->assertCollectionSize(1, $this->getListingBlock()->getLoadedProductCollection());
+ $priceHtml = $this->getListingBlock()->getProductPrice($this->getProduct($sku));
+ $this->assertEquals($priceString, $this->clearPriceHtml($priceHtml));
}
/**
@@ -80,4 +155,64 @@ private function assertCollectionSize(int $expectedSize, AbstractCollection $col
$this->assertEquals($expectedSize, $collection->getSize());
$this->assertCount($expectedSize, $collection->getItems());
}
+
+ /**
+ * Prepare category page.
+ *
+ * @return void
+ */
+ private function preparePageLayout(): void
+ {
+ $this->registry->unregister('current_category');
+ $this->registry->register(
+ 'current_category',
+ $this->categoryRepository->get(333, $this->storeManager->getStore()->getId())
+ );
+ $this->page->addHandle(['default', 'catalog_category_view']);
+ $this->page->getLayout()->generateXml();
+ }
+
+ /**
+ * Reset layout page to get new block html.
+ *
+ * @return void
+ */
+ private function resetPageLayout(): void
+ {
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
+ }
+
+ /**
+ * Removes html tags and spaces from price html string.
+ *
+ * @param string $priceHtml
+ * @return string
+ */
+ private function clearPriceHtml(string $priceHtml): string
+ {
+ return trim(preg_replace('/\s+/', ' ', strip_tags($priceHtml)));
+ }
+
+ /**
+ * Returns product list block.
+ *
+ * @return null|ListProduct
+ */
+ private function getListingBlock(): ?ListProduct
+ {
+ $block = $this->page->getLayout()->getBlock('category.products.list');
+
+ return $block ? $block : null;
+ }
+
+ /**
+ * Loads product by sku.
+ *
+ * @param string $sku
+ * @return ProductInterface
+ */
+ private function getProduct(string $sku): ProductInterface
+ {
+ return $this->productRepository->get($sku, false, $this->storeManager->getStore()->getId(), true);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php
index 52bb7a2f8ab6d..2ffba24e465e0 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/Configurable/PriceTest.php
@@ -13,14 +13,17 @@
use Magento\Customer\Model\Group;
use Magento\Framework\ObjectManagerInterface;
use Magento\Store\Api\WebsiteRepositoryInterface;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Catalog\Model\Product\Price\GetPriceIndexDataByProductId;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Store\ExecuteInStoreContext;
use PHPUnit\Framework\TestCase;
/**
* Provides tests for configurable product pricing.
*
* @magentoDbIsolation disabled
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class PriceTest extends TestCase
{
@@ -49,6 +52,16 @@ class PriceTest extends TestCase
*/
private $websiteRepository;
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
+ /**
+ * @var ExecuteInStoreContext
+ */
+ private $executeInStoreContext;
+
/**
* @inheritdoc
*/
@@ -60,6 +73,8 @@ protected function setUp(): void
$this->productRepository->cleanCache();
$this->getPriceIndexDataByProductId = $this->objectManager->get(GetPriceIndexDataByProductId::class);
$this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class);
+ $this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $this->executeInStoreContext = $this->objectManager->get(ExecuteInStoreContext::class);
}
/**
@@ -84,6 +99,46 @@ public function testGetFinalPrice(): void
);
}
+ /**
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website.php
+ * @return void
+ */
+ public function testGetFinalPriceOnSecondWebsite(): void
+ {
+ $this->executeInStoreContext->execute('fixture_second_store', [$this, 'assertPrice'], 10);
+ $this->executeInStoreContext->execute(
+ 'fixture_second_store',
+ [$this, 'assertIndexTableData'],
+ 'configurable',
+ ['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 30, 'tier_price' => null]
+ );
+ $this->executeInStoreContext->execute(
+ 'fixture_second_store',
+ [$this, 'assertIndexTableData'],
+ 'simple_option_1',
+ ['price' => 20, 'final_price' => 10, 'min_price' => 10, 'max_price' => 10, 'tier_price' => null]
+ );
+ $this->executeInStoreContext->execute(
+ 'fixture_second_store',
+ [$this, 'assertIndexTableData'],
+ 'simple_option_2',
+ ['price' => 40, 'final_price' => 30, 'min_price' => 30, 'max_price' => 30, 'tier_price' => null]
+ );
+ $this->assertPrice(150);
+ $this->assertIndexTableData(
+ 'configurable',
+ ['price' => 0, 'final_price' => 0, 'min_price' => 150, 'max_price' => 150, 'tier_price' => null]
+ );
+ $this->assertIndexTableData(
+ 'simple_option_1',
+ ['price' => 150, 'final_price' => 150, 'min_price' => 150, 'max_price' => 150, 'tier_price' => null]
+ );
+ $this->assertIndexTableData(
+ 'simple_option_2',
+ ['price' => 150, 'final_price' => 150, 'min_price' => 150, 'max_price' => 150, 'tier_price' => null]
+ );
+ }
+
/**
* @magentoConfigFixture current_store tax/display/type 1
* @magentoDataFixture Magento/ConfigurableProduct/_files/tax_rule.php
@@ -127,7 +182,7 @@ public function testGetFinalPriceIncludingExcludingTax(): void
public function testGetFinalPriceWithSelectedSimpleProduct(): void
{
$product = $this->productRepository->get('configurable');
- $product->addCustomOption('simple_product', 20, $this->productRepository->get('simple_20'));
+ $product->addCustomOption('simple_product', 20, $this->getProduct('simple_20'));
$this->assertPrice(20, $product);
}
@@ -137,7 +192,7 @@ public function testGetFinalPriceWithSelectedSimpleProduct(): void
*/
public function testGetFinalPriceWithCustomOptionAndSimpleTierPrice(): void
{
- $configurable = $this->productRepository->get('configurable');
+ $configurable = $this->getProduct('configurable');
$this->assertIndexTableData(
'configurable',
['price' => 0, 'final_price' => 0, 'min_price' => 9, 'max_price' => 30, 'tier_price' => 15]
@@ -167,12 +222,12 @@ public function testGetFinalPriceWithCustomOptionAndSimpleTierPrice(): void
* @param array $expectedPrices
* @return void
*/
- private function assertIndexTableData(string $sku, array $expectedPrices): void
+ public function assertIndexTableData(string $sku, array $expectedPrices): void
{
$data = $this->getPriceIndexDataByProductId->execute(
- (int)$this->productRepository->get($sku)->getId(),
+ (int)$this->getProduct($sku)->getId(),
Group::NOT_LOGGED_IN_ID,
- (int)$this->websiteRepository->get('base')->getId()
+ (int)$this->storeManager->getStore()->getWebsiteId()
);
$data = reset($data);
foreach ($expectedPrices as $column => $price) {
@@ -187,13 +242,24 @@ private function assertIndexTableData(string $sku, array $expectedPrices): void
* @param ProductInterface|null $product
* @return void
*/
- private function assertPrice(float $expectedPrice, ?ProductInterface $product = null): void
+ public function assertPrice(float $expectedPrice, ?ProductInterface $product = null): void
{
- $product = $product ?: $this->productRepository->get('configurable');
+ $product = $product ?: $this->getProduct('configurable');
// final price is the lowest price of configurable variations
$this->assertEquals(
round($expectedPrice, 2),
round($this->priceModel->getFinalPrice(1, $product), 2)
);
}
+
+ /**
+ * Loads product by sku.
+ *
+ * @param string $sku
+ * @return ProductInterface
+ */
+ private function getProduct(string $sku): ProductInterface
+ {
+ return $this->productRepository->get($sku, false, $this->storeManager->getStore()->getId(), true);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website.php
new file mode 100644
index 0000000000000..a0edde0becd9e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website.php
@@ -0,0 +1,130 @@
+requireDataFixture('Magento/Store/_files/second_website_with_store_group_and_store.php');
+Resolver::getInstance()->requireDataFixture('Magento/ConfigurableProduct/_files/configurable_attribute.php');
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/category.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var ProductAttributeRepositoryInterface $productAttributeRepository */
+$productAttributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class);
+/** @var WebsiteRepositoryInterface $websiteRepository */
+$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
+/** @var ProductFactory $productFactory */
+$productFactory = $objectManager->get(ProductFactory::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var Factory $optionsFactory */
+$optionsFactory = $objectManager->get(Factory::class);
+/** @var ProductExtensionFactory $extensionAttributesFactory */
+$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class);
+/** @var Config $configResource */
+$configResource = $objectManager->get(Config::class);
+/** @var SwitchPriceAttributeScopeOnConfigChange $observer */
+$observer = $objectManager->get(Observer::class);
+/** @var DefaultCategory $categoryHelper */
+$categoryHelper = $objectManager->get(DefaultCategory::class);
+
+$attribute = $productAttributeRepository->get('test_configurable');
+$options = $attribute->getOptions();
+$baseWebsite = $websiteRepository->get('base');
+$secondWebsite = $websiteRepository->get('test');
+$attributeValues = [];
+$associatedProductIds = [];
+array_shift($options);
+
+foreach ($options as $option) {
+ $product = $productFactory->create();
+ $product->setTypeId(ProductType::TYPE_SIMPLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsite->getId(), $secondWebsite->getId()])
+ ->setName('Configurable Option ' . $option->getLabel())
+ ->setSku(strtolower(str_replace(' ', '_', 'simple ' . $option->getLabel())))
+ ->setTestConfigurable($option->getValue())
+ ->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setPrice(150)
+ ->setCategoryIds([$categoryHelper->getId(), 333])
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
+ $product = $productRepository->save($product);
+ $associatedProductIds[] = $product->getId();
+ $attributeValues[] = [
+ 'label' => 'test',
+ 'attribute_id' => $attribute->getId(),
+ 'value_index' => $option->getValue(),
+ ];
+}
+$configurableAttributesData = [
+ [
+ 'values' => $attributeValues,
+ 'attribute_id' => $attribute->getId(),
+ 'code' => $attribute->getAttributeCode(),
+ 'label' => $attribute->getStoreLabel(),
+ 'position' => '0',
+ ],
+];
+$configurableOptions = $optionsFactory->create($configurableAttributesData);
+
+$product = $productFactory->create();
+$extensionConfigurableAttributes = $product->getExtensionAttributes() ?: $extensionAttributesFactory->create();
+$extensionConfigurableAttributes->setConfigurableProductOptions($configurableOptions);
+$extensionConfigurableAttributes->setConfigurableProductLinks($associatedProductIds);
+$product->setExtensionAttributes($extensionConfigurableAttributes);
+$product->setTypeId(Configurable::TYPE_CODE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setWebsiteIds([$baseWebsite->getId(), $secondWebsite->getId()])
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setCategoryIds([$categoryHelper->getId(), 333])
+ ->setSku('configurable')
+ ->setName('Configurable Product')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStockData(['use_config_manage_stock' => 1, 'is_in_stock' => 1]);
+$productRepository->save($product);
+
+$configResource->saveConfig(Data::XML_PATH_PRICE_SCOPE, Store::PRICE_SCOPE_WEBSITE, 'default', 0);
+$objectManager->get(ReinitableConfigInterface::class)->reinit();
+$objectManager->get(SwitchPriceAttributeScopeOnConfigChange::class)->execute($observer);
+/** @var StoreManagerInterface $storeManager */
+$storeManager = $objectManager->get(StoreManagerInterface::class);
+$secondStoreId = $storeManager->getStore('fixture_second_store')->getId();
+
+try {
+ $currentStoreCode = $storeManager->getStore()->getCode();
+ $storeManager->setCurrentStore('fixture_second_store');
+ $firstChild = $productRepository->get('simple_option_1', false, $secondStoreId, true);
+ $firstChild->setPrice(20)
+ ->setSpecialPrice(10);
+ $productRepository->save($firstChild);
+ $secondChild = $productRepository->get('simple_option_2', false, $secondStoreId, true);
+ $secondChild->setPrice(40)
+ ->setSpecialPrice(30);
+ $productRepository->save($secondChild);
+} finally {
+ $storeManager->setCurrentStore($currentStoreCode);
+}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website_rollback.php
new file mode 100644
index 0000000000000..ceeeb13fb5a36
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_product_with_price_on_second_website_rollback.php
@@ -0,0 +1,34 @@
+get(DeleteConfigurableProduct::class);
+$deleteConfigurableProduct->execute('configurable');
+/** @var Config $configResource */
+$configResource = $objectManager->get(Config::class);
+$configResource->deleteConfig(Data::XML_PATH_PRICE_SCOPE, 'default', 0);
+$objectManager->get(ReinitableConfigInterface::class)->reinit();
+$observer = $objectManager->get(Observer::class);
+$objectManager->get(SwitchPriceAttributeScopeOnConfigChange::class)->execute($observer);
+
+Resolver::getInstance()->requireDataFixture(
+ 'Magento/Store/_files/second_website_with_store_group_and_store_rollback.php'
+);
+Resolver::getInstance()->requireDataFixture(
+ 'Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php'
+);
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/category_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/OptionsTest.php
new file mode 100644
index 0000000000000..879707edd9224
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/OptionsTest.php
@@ -0,0 +1,164 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->create(Options::class);
+ }
+
+ /**
+ * Test suffix and prefix options
+ *
+ * @dataProvider optionsDataProvider
+ *
+ * @param string $optionType
+ * @param array $showOptionConfig
+ * @param array $optionValuesConfig
+ * @param array $expectedOptions
+ * @return void
+ */
+ public function testOptions(
+ string $optionType,
+ array $showOptionConfig,
+ array $optionValuesConfig,
+ array $expectedOptions
+ ): void {
+ $this->setConfig($showOptionConfig);
+ $this->setConfig($optionValuesConfig);
+
+ /** @var array $options */
+ $options = $optionType === 'prefix'
+ ? $this->model->getNamePrefixOptions()
+ : $this->model->getNameSuffixOptions();
+
+ $this->assertEquals($expectedOptions, $options);
+ }
+
+ /**
+ * Set config param
+ *
+ * @param array $data
+ * @param string|null $scopeType
+ * @param string|null $scopeCode
+ * @return void
+ */
+ private function setConfig(
+ array $data,
+ ?string $scopeType = ScopeInterface::SCOPE_STORE,
+ ?string $scopeCode = 'default'
+ ): void {
+ $path = array_key_first($data);
+ $this->objectManager->get(MutableScopeConfigInterface::class)
+ ->setValue($path, $data[$path], $scopeType, $scopeCode);
+ }
+
+ /**
+ * DataProvider for testOptions()
+ *
+ * @return array
+ */
+ public function optionsDataProvider(): array
+ {
+ $optionPrefixName = 'prefix';
+ $optionSuffixName = 'suffix';
+ $optionValues = 'v1;v2';
+ $expectedValues = ['v1', 'v2'];
+ $optionValuesWithBlank = ';v1;v2';
+ $expectedValuesWithBlank = [' ', 'v1', 'v2'];
+ $optionValuesWithTwoBlank = ';v1;v2;';
+ $expectedValuesTwoBlank = [' ', 'v1', 'v2', ' '];
+
+ return [
+ 'prefix_required_with_blank_option' => [
+ $optionPrefixName,
+ [self::XML_PATH_PREFIX_SHOW => Nooptreq::VALUE_REQUIRED],
+ [self::XML_PATH_PREFIX_OPTIONS => $optionValuesWithBlank],
+ $expectedValuesWithBlank,
+ ],
+ 'prefix_required' => [
+ $optionPrefixName,
+ [self::XML_PATH_PREFIX_SHOW => Nooptreq::VALUE_REQUIRED],
+ [self::XML_PATH_PREFIX_OPTIONS => $optionValues],
+ $expectedValues,
+ ],
+ 'prefix_required_with_two_blank_option' => [
+ $optionPrefixName,
+ [self::XML_PATH_PREFIX_SHOW => Nooptreq::VALUE_REQUIRED],
+ [self::XML_PATH_PREFIX_OPTIONS => $optionValuesWithTwoBlank],
+ $expectedValuesTwoBlank,
+ ],
+ 'prefix_optional' => [
+ $optionPrefixName,
+ [self::XML_PATH_PREFIX_SHOW => Nooptreq::VALUE_OPTIONAL],
+ [self::XML_PATH_PREFIX_OPTIONS => $optionValues],
+ $expectedValuesWithBlank,
+ ],
+ 'suffix_optional' => [
+ $optionSuffixName,
+ [self::XML_PATH_SUFFIX_SHOW => Nooptreq::VALUE_OPTIONAL],
+ [self::XML_PATH_SUFFIX_OPTIONS => $optionValues],
+ $expectedValuesWithBlank,
+ ],
+ 'suffix_optional_with_blank_option' => [
+ $optionSuffixName,
+ [self::XML_PATH_SUFFIX_SHOW => Nooptreq::VALUE_OPTIONAL],
+ [self::XML_PATH_SUFFIX_OPTIONS => $optionValuesWithBlank],
+ $expectedValuesWithBlank,
+ ],
+ 'suffix_required_with_blank_option' => [
+ $optionSuffixName,
+ [self::XML_PATH_SUFFIX_SHOW => Nooptreq::VALUE_OPTIONAL],
+ [self::XML_PATH_SUFFIX_OPTIONS => $optionValuesWithBlank],
+ $expectedValuesWithBlank,
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->objectManager->removeSharedInstance(Address::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php b/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php
index 8f1771759cee0..1046c678e253a 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Interception/PluginListGeneratorTest.php
@@ -11,8 +11,14 @@
use Magento\Framework\Filesystem\DriverInterface;
use Magento\TestFramework\Application;
use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
-class PluginListGeneratorTest extends \PHPUnit\Framework\TestCase
+/**
+ * Provide tests for PluginListGeneratorTest
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class PluginListGeneratorTest extends TestCase
{
/**
* Generated plugin list config for frontend scope
@@ -93,8 +99,7 @@ public function testPluginListConfigGeneration()
$expected = [
1 => [
0 => 'genericHeaderPlugin',
- 1 => 'asyncCssLoad',
- 2 => 'response-http-page-cache'
+ 1 => 'response-http-page-cache'
]
];
// Here in test is assumed that this class below has 3 plugins. But the amount of plugins and class itself
@@ -104,6 +109,7 @@ public function testPluginListConfigGeneration()
$configData[2],
'Processed plugin does not exist in the processed plugins array.'
);
+
$this->assertSame(
$expected,
$configData[2]['Magento\\Framework\\App\\Response\\Http_sendResponse___self'],
@@ -116,7 +122,7 @@ public function testPluginListConfigGeneration()
*
* @return array
*/
- private function getCustomDirs()
+ private function getCustomDirs(): array
{
$path = DirectoryList::PATH;
$generated = "{$this->application->getTempDir()}/generated";
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/Item/CartItemPersisterTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/Item/CartItemPersisterTest.php
new file mode 100644
index 0000000000000..647b8a188a55c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/Item/CartItemPersisterTest.php
@@ -0,0 +1,133 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->get(CartItemPersister::class);
+ $this->quoteFactory = $this->objectManager->get(CartInterfaceFactory::class);
+ $this->itemFactory = $this->objectManager->get(CartItemInterfaceFactory::class);
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/simple_product_disabled.php
+ *
+ * @return void
+ */
+ public function testSaveDisabledItem(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $item = $this->itemFactory->create();
+ $item->setSku('product_disabled')->setQty(1);
+ $this->expectExceptionObject(
+ new LocalizedException(__('Product that you are trying to add is not available.'))
+ );
+ $this->model->save($quote, $item);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ *
+ * @return void
+ */
+ public function testSaveQuoteItemWithoutQty(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $item = $this->itemFactory->create();
+ $item->setSku('simple-1');
+ $this->expectExceptionObject(InputException::invalidFieldValue('qty', null));
+ $this->model->save($quote, $item);
+ }
+
+ /**
+ * @return void
+ */
+ public function testSaveQuoteItemWithNotExistingProduct(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $item = $this->itemFactory->create();
+ $item->setSku('not_existing_product_sku')->setQty(1);
+ $this->expectExceptionObject(
+ new NoSuchEntityException(
+ __('The product that was requested doesn\'t exist. Verify the product and try again.')
+ )
+ );
+ $this->model->save($quote, $item);
+ }
+
+ /**
+ * @return void
+ */
+ public function testUpdateNotExistingQuoteItem(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $item = $this->itemFactory->create();
+ $item->setItemId(989)->setQty(1);
+ $this->expectExceptionObject(
+ new NoSuchEntityException(
+ __('The %1 Cart doesn\'t contain the %2 item.', null, 989)
+ )
+ );
+ $this->model->save($quote, $item);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_taxable_product_and_customer.php
+ *
+ * @return void
+ */
+ public function testUpdateQuoteItemMoreQty(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_taxable_product');
+ $quoteItem = current($quote->getItems());
+ $item = $this->itemFactory->create();
+ $item->setQty(9999)->setSku($quoteItem->getSku())->setItemId($quoteItem->getItemId());
+ $this->expectExceptionObject(new LocalizedException(__('The requested qty is not available')));
+ $this->model->save($quote, $item);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php
index dac05f17089a1..facb4879650b1 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php
@@ -9,22 +9,30 @@
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product\Type;
-use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\StateException;
+use Magento\Framework\ObjectManagerInterface;
use Magento\Quote\Api\CartManagementInterface;
-use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Sales\Api\OrderManagementInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
-use Magento\TestFramework\ObjectManager;
+use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
use PHPUnit\Framework\ExpectationFailedException;
+use PHPUnit\Framework\TestCase;
/**
* Class for testing QuoteManagement model
+ *
+ * @see \Magento\Quote\Model\QuoteManagement
+ * @magentoDbIsolation enabled
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class QuoteManagementTest extends \PHPUnit\Framework\TestCase
+class QuoteManagementTest extends TestCase
{
/**
- * @var ObjectManager
+ * @var ObjectManagerInterface
*/
private $objectManager;
@@ -33,14 +41,46 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase
*/
private $cartManagement;
+ /**
+ * @var OrderRepositoryInterface
+ */
+ private $orderRepository;
+
+ /**
+ * @var GetQuoteByReservedOrderId
+ */
+ private $getQuoteByReservedOrderId;
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
+ /**
+ * @var CustomerRepositoryInterface
+ */
+ private $customerRepository;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
/**
* @inheritdoc
*/
protected function setUp(): void
{
- $this->objectManager = Bootstrap::getObjectManager();
+ parent::setUp();
- $this->cartManagement = $this->objectManager->create(CartManagementInterface::class);
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->cartManagement = $this->objectManager->get(CartManagementInterface::class);
+ $this->orderRepository = $this->objectManager->get(OrderRepositoryInterface::class);
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
+ $this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
}
/**
@@ -48,22 +88,20 @@ protected function setUp(): void
*
* @magentoAppIsolation enabled
* @magentoDataFixture Magento/Sales/_files/quote_with_bundle.php
+ *
+ * @return void
*/
- public function testSubmit()
+ public function testSubmit(): void
{
- $quote = $this->getQuote('test01');
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
$orderId = $this->cartManagement->placeOrder($quote->getId());
-
- /** @var OrderRepositoryInterface $orderRepository */
- $orderRepository = $this->objectManager->create(OrderRepositoryInterface::class);
- $order = $orderRepository->get($orderId);
-
+ $order = $this->orderRepository->get($orderId);
$orderItems = $order->getItems();
- self::assertCount(3, $orderItems);
+ $this->assertCount(3, $orderItems);
foreach ($orderItems as $orderItem) {
if ($orderItem->getProductType() == Type::TYPE_SIMPLE) {
- self::assertNotEmpty($orderItem->getParentItem(), 'Parent is not set for child product');
- self::assertNotEmpty($orderItem->getParentItemId(), 'Parent is not set for child product');
+ $this->assertNotEmpty($orderItem->getParentItem(), 'Parent is not set for child product');
+ $this->assertNotEmpty($orderItem->getParentItemId(), 'Parent is not set for child product');
}
}
}
@@ -75,17 +113,13 @@ public function testSubmit()
* @magentoAppIsolation enabled
* @magentoDataFixture Magento/Sales/_files/quote_with_bundle.php
*/
- public function testSubmitWithDeletedItem()
+ public function testSubmitWithDeletedItem(): void
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
- $this->expectExceptionMessage('Some of the products below do not have all the required options.');
-
- /** @var ProductRepositoryInterface $productRepository */
- $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
- $product = $productRepository->get('simple-2');
- $productRepository->delete($product);
- $quote = $this->getQuote('test01');
-
+ $this->productRepository->deleteById('simple-2');
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
+ $this->expectExceptionObject(
+ new LocalizedException(__('Some of the products below do not have all the required options.'))
+ );
$this->cartManagement->placeOrder($quote->getId());
}
@@ -95,13 +129,11 @@ public function testSubmitWithDeletedItem()
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoDbIsolation enabled
*/
- public function testSubmitWithItemOutOfStock()
+ public function testSubmitWithItemOutOfStock(): void
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
- $this->expectExceptionMessage('Some of the products are out of stock.');
-
$this->makeProductOutOfStock('simple');
- $quote = $this->getQuote('test01');
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
+ $this->expectExceptionObject(new LocalizedException(__('Some of the products are out of stock.')));
$this->cartManagement->placeOrder($quote->getId());
}
@@ -111,21 +143,20 @@ public function testSubmitWithItemOutOfStock()
* Order should not start placing if order validation is failed.
*
* @magentoDataFixture Magento/Quote/Fixtures/quote_without_customer_email.php
+ *
+ * @return void
*/
- public function testSubmitWithEmptyCustomerEmail()
+ public function testSubmitWithEmptyCustomerEmail(): void
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
- $this->expectExceptionMessage('Email has a wrong format');
-
- $quote = $this->getQuote('test01');
- $orderManagement = $this->getMockForAbstractClass(OrderManagementInterface::class);
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
+ $orderManagement = $this->createMock(OrderManagementInterface::class);
$orderManagement->expects($this->never())
->method('place');
$cartManagement = $this->objectManager->create(
CartManagementInterface::class,
['orderManagement' => $orderManagement]
);
-
+ $this->expectExceptionObject(new LocalizedException(__('Email has a wrong format')));
try {
$cartManagement->placeOrder($quote->getId());
} catch (ExpectationFailedException $e) {
@@ -134,24 +165,56 @@ public function testSubmitWithEmptyCustomerEmail()
}
/**
- * Gets quote by reserved order ID.
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
+ * @magentoDataFixture Magento/Customer/_files/customer.php
*
- * @param string $reservedOrderId
- * @return Quote
+ * @return void
*/
- private function getQuote(string $reservedOrderId): Quote
+ public function testAssignCustomerToQuote(): void
{
- /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
- $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
- $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
- ->create();
+ $customer = $this->customerRepository->get('customer@example.com');
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_simple_product_without_address');
+ $result = $this->cartManagement->assignCustomer($quote->getId(), $customer->getId(), $customer->getStoreId());
+ $this->assertTrue($result);
+ $customerQuote = $this->cartManagement->getCartForCustomer($customer->getId());
+ $this->assertEquals($quote->getId(), $customerQuote->getId());
+ $this->assertEquals($customer->getId(), $customerQuote->getCustomerId());
+ $this->assertEquals($customer->getEmail(), $customerQuote->getCustomerEmail());
+ }
- /** @var CartRepositoryInterface $quoteRepository */
- $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
- $items = $quoteRepository->getList($searchCriteria)
- ->getItems();
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
+ * @magentoDataFixture Magento/Customer/_files/customer_for_second_website.php
+ *
+ * @return void
+ */
+ public function testAssignCustomerFromAnotherWebsiteToQuote(): void
+ {
+ $websiteId = $this->storeManager->getWebsite('test')->getId();
+ $customer = $this->customerRepository->get('customer@example.com', $websiteId);
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_simple_product_without_address');
+ $this->expectExceptionObject(
+ new StateException(
+ __('The customer can\'t be assigned to the cart. The cart belongs to a different store.')
+ )
+ );
+ $this->cartManagement->assignCustomer($quote->getId(), $customer->getId(), $quote->getStoreId());
+ }
- return array_pop($items);
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ * @magentoDataFixture Magento/Customer/_files/customer_with_uk_address.php
+ *
+ * @return void
+ */
+ public function testAssignCustomerToQuoteAlreadyHaveCustomer(): void
+ {
+ $customer = $this->customerRepository->get('customer_uk_address@test.com');
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->expectExceptionObject(
+ new StateException(__('The customer can\'t be assigned to the cart because the cart isn\'t anonymous.'))
+ );
+ $this->cartManagement->assignCustomer($quote->getId(), $customer->getId(), $quote->getStoreId());
}
/**
@@ -160,14 +223,12 @@ private function getQuote(string $reservedOrderId): Quote
* @param string $sku
* @return void
*/
- private function makeProductOutOfStock(string $sku)
+ private function makeProductOutOfStock(string $sku): void
{
- /** @var ProductRepositoryInterface $productRepository */
- $productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
- $product = $productRepository->get($sku);
+ $product = $this->productRepository->get($sku);
$extensionAttributes = $product->getExtensionAttributes();
$stockItem = $extensionAttributes->getStockItem();
$stockItem->setIsInStock(false);
- $productRepository->save($product);
+ $this->productRepository->save($product);
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
index 3b18ee0ceaa5e..f3684e5167b58 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteRepositoryTest.php
@@ -3,26 +3,37 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Quote\Model;
-use Magento\Store\Model\StoreRepository;
-use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper;
-use Magento\Framework\ObjectManagerInterface;
-use Magento\Framework\Api\SearchCriteriaBuilder;
-use Magento\Framework\Api\SearchCriteria;
-use Magento\Framework\Api\SearchResults;
use Magento\Framework\Api\FilterBuilder;
+use Magento\Framework\Api\SearchCriteria;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Quote\Api\CartRepositoryInterface;
+use Magento\Quote\Api\Data\AddressInterfaceFactory;
+use Magento\Quote\Api\Data\CartExtension;
use Magento\Quote\Api\Data\CartInterface;
+use Magento\Quote\Api\Data\CartInterfaceFactory;
+use Magento\Quote\Api\Data\CartItemInterfaceFactory;
use Magento\Quote\Api\Data\CartSearchResultsInterface;
-use Magento\Quote\Api\Data\CartExtension;
-use Magento\User\Api\Data\UserInterface;
+use Magento\Quote\Api\Data\ShippingAssignmentInterface;
+use Magento\Quote\Api\Data\ShippingInterface;
use Magento\Quote\Model\Quote\Address as QuoteAddress;
+use Magento\Store\Api\StoreRepositoryInterface;
+use Magento\TestFramework\Helper\Bootstrap as BootstrapHelper;
+use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
+use PHPUnit\Framework\TestCase;
/**
+ * Test for quote repository
+ *
+ * @see \Magento\Quote\Model\QuoteRepository
+ * @magentoDbIsolation enabled
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- * @magentoDbIsolation disabled
*/
-class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase
+class QuoteRepositoryTest extends TestCase
{
/**
* @var ObjectManagerInterface
@@ -30,7 +41,7 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase
private $objectManager;
/**
- * @var QuoteRepository
+ * @var CartRepositoryInterface
*/
private $quoteRepository;
@@ -45,14 +56,63 @@ class QuoteRepositoryTest extends \PHPUnit\Framework\TestCase
private $filterBuilder;
/**
- * Set up
+ * @var GetQuoteByReservedOrderId
+ */
+ private $getQuoteByReservedOrderId;
+
+ /**
+ * @var StoreRepositoryInterface
+ */
+ private $storeRepository;
+
+ /**
+ * @var AddressInterfaceFactory
+ */
+ private $addressFactory;
+
+ /**
+ * @var CartInterfaceFactory
+ */
+ private $quoteFactory;
+
+ /**
+ * @var CartItemInterfaceFactory
+ */
+ private $itemFactory;
+
+ /**
+ * @var CartInterface|null
+ */
+ private $quote;
+
+ /**
+ * @inheritdoc
*/
protected function setUp(): void
{
+ parent::setUp();
+
$this->objectManager = BootstrapHelper::getObjectManager();
- $this->quoteRepository = $this->objectManager->create(QuoteRepository::class);
- $this->searchCriteriaBuilder = $this->objectManager->create(SearchCriteriaBuilder::class);
- $this->filterBuilder = $this->objectManager->create(FilterBuilder::class);
+ $this->quoteRepository = $this->objectManager->create(CartRepositoryInterface::class);
+ $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
+ $this->filterBuilder = $this->objectManager->get(FilterBuilder::class);
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->storeRepository = $this->objectManager->get(StoreRepositoryInterface::class);
+ $this->addressFactory = $this->objectManager->get(AddressInterfaceFactory::class);
+ $this->quoteFactory = $this->objectManager->get(CartInterfaceFactory::class);
+ $this->itemFactory = $this->objectManager->get(CartItemInterfaceFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ if ($this->quote instanceof CartInterface) {
+ $this->quoteRepository->delete($this->quote);
+ }
+
+ parent::tearDown();
}
/**
@@ -60,22 +120,18 @@ protected function setUp(): void
*
* @magentoDataFixture Magento/Sales/_files/quote.php
* @magentoDataFixture Magento/Store/_files/second_store.php
+ *
+ * @return void
*/
- public function testGetQuoteWithCustomStoreId()
+ public function testGetQuoteWithCustomStoreId(): void
{
$secondStoreCode = 'fixture_second_store';
$reservedOrderId = 'test01';
-
- $storeRepository = $this->objectManager->create(StoreRepository::class);
- $secondStore = $storeRepository->get($secondStoreCode);
-
- // Set store_id in quote to second store_id
- $quote = $this->getQuote($reservedOrderId);
+ $secondStore = $this->storeRepository->get($secondStoreCode);
+ $quote = $this->getQuoteByReservedOrderId->execute($reservedOrderId);
$quote->setStoreId($secondStore->getId());
$this->quoteRepository->save($quote);
-
$savedQuote = $this->quoteRepository->get($quote->getId());
-
$this->assertEquals(
$secondStore->getId(),
$savedQuote->getStoreId(),
@@ -85,8 +141,10 @@ public function testGetQuoteWithCustomStoreId()
/**
* @magentoDataFixture Magento/Sales/_files/quote.php
+ *
+ * @return void
*/
- public function testGetList()
+ public function testGetList(): void
{
$searchCriteria = $this->getSearchCriteria('test01');
$searchResult = $this->quoteRepository->getList($searchCriteria);
@@ -95,62 +153,50 @@ public function testGetList()
/**
* @magentoDataFixture Magento/Sales/_files/quote.php
+ *
+ * @return void
*/
- public function testGetListDoubleCall()
+ public function testGetListDoubleCall(): void
{
$searchCriteria1 = $this->getSearchCriteria('test01');
$searchCriteria2 = $this->getSearchCriteria('test02');
$searchResult = $this->quoteRepository->getList($searchCriteria1);
$this->performAssertions($searchResult);
$searchResult = $this->quoteRepository->getList($searchCriteria2);
-
$this->assertEmpty($searchResult->getItems());
}
/**
* @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testSaveWithNotExistingCustomerAddress()
+ public function testSaveWithNotExistingCustomerAddress(): void
{
$addressData = include __DIR__ . '/../../Sales/_files/address_data.php';
-
- /** @var QuoteAddress $billingAddress */
- $billingAddress = $this->objectManager->create(QuoteAddress::class, ['data' => $addressData]);
- $billingAddress->setAddressType(QuoteAddress::ADDRESS_TYPE_BILLING)
- ->setCustomerAddressId('not_existing');
-
- /** @var QuoteAddress $shippingAddress */
- $shippingAddress = $this->objectManager->create(QuoteAddress::class, ['data' => $addressData]);
- $shippingAddress->setAddressType(QuoteAddress::ADDRESS_TYPE_SHIPPING)
- ->setCustomerAddressId('not_existing');
-
- /** @var Shipping $shipping */
- $shipping = $this->objectManager->create(Shipping::class);
+ $billingAddress = $this->addressFactory->create(['data' => $addressData]);
+ $billingAddress->setAddressType(QuoteAddress::ADDRESS_TYPE_BILLING)->setCustomerAddressId('not_existing');
+ $shippingAddress = $this->addressFactory->create(['data' => $addressData]);
+ $shippingAddress->setAddressType(QuoteAddress::ADDRESS_TYPE_SHIPPING)->setCustomerAddressId('not_existing');
+ $shipping = $this->objectManager->get(ShippingInterface::class);
$shipping->setAddress($shippingAddress);
-
- /** @var ShippingAssignment $shippingAssignment */
- $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shippingAssignment = $this->objectManager->get(ShippingAssignmentInterface::class);
$shippingAssignment->setItems([]);
$shippingAssignment->setShipping($shipping);
-
- /** @var CartExtension $extensionAttributes */
- $extensionAttributes = $this->objectManager->create(CartExtension::class);
+ $extensionAttributes = $this->objectManager->get(CartExtension::class);
$extensionAttributes->setShippingAssignments([$shippingAssignment]);
-
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $quote->setStoreId(1)
+ $this->quote = $this->quoteFactory->create();
+ $this->quote->setStoreId(1)
->setIsActive(true)
- ->setIsMultiShipping(false)
+ ->setIsMultiShipping(0)
->setBillingAddress($billingAddress)
->setShippingAddress($shippingAddress)
->setExtensionAttributes($extensionAttributes)
->save();
- $this->quoteRepository->save($quote);
-
- $this->assertNull($quote->getBillingAddress()->getCustomerAddressId());
+ $this->quoteRepository->save($this->quote);
+ $this->assertNull($this->quote->getBillingAddress()->getCustomerAddressId());
$this->assertNull(
- $quote->getExtensionAttributes()
+ $this->quote->getExtensionAttributes()
->getShippingAssignments()[0]
->getShipping()
->getAddress()
@@ -159,21 +205,37 @@ public function testSaveWithNotExistingCustomerAddress()
}
/**
- * Returns quote by reserved order id.
+ * @magentoDataFixture Magento/Catalog/_files/multiple_products.php
*
- * @param string $reservedOrderId
- * @return CartInterface
+ * @return void
*/
- private function getQuote(string $reservedOrderId)
+ public function testSaveQuoteWithItems(): void
{
- $searchCriteria = $this->getSearchCriteria($reservedOrderId);
- $searchResult = $this->quoteRepository->getList($searchCriteria);
- $items = $searchResult->getItems();
-
- /** @var CartInterface $quote */
- $quote = array_pop($items);
+ $items = $this->prepareQuoteItems(['simple1', 'simple2']);
+ $this->quote = $this->quoteFactory->create();
+ $this->quote->setItems($items);
+ $this->quoteRepository->save($this->quote);
+ $this->assertCount(2, $this->quote->getItemsCollection());
+ $this->assertEquals(2, $this->quote->getItemsCount());
+ $this->assertEquals(2, $this->quote->getItemsQty());
+ }
- return $quote;
+ /**
+ * Prepare quote items by products sku.
+ *
+ * @param array $productsSku
+ * @return array
+ */
+ private function prepareQuoteItems(array $productsSku): array
+ {
+ $items = [];
+ foreach ($productsSku as $sku) {
+ $item = $this->itemFactory->create();
+ $item->setSku($sku)->setQty(1);
+ $items[] = $item;
+ }
+
+ return $items;
}
/**
@@ -182,7 +244,7 @@ private function getQuote(string $reservedOrderId)
* @param string $filterValue
* @return SearchCriteria
*/
- private function getSearchCriteria($filterValue)
+ private function getSearchCriteria(string $filterValue): SearchCriteria
{
$filters = [];
$filters[] = $this->filterBuilder->setField('reserved_order_id')
@@ -197,24 +259,19 @@ private function getSearchCriteria($filterValue)
/**
* Perform assertions
*
- * @param SearchResults|CartSearchResultsInterface $searchResult
+ * @param CartSearchResultsInterface $searchResult
+ * @return void
*/
- private function performAssertions($searchResult)
+ private function performAssertions(CartSearchResultsInterface $searchResult): void
{
$expectedExtensionAttributes = [
'firstname' => 'firstname',
'lastname' => 'lastname',
- 'email' => 'admin@example.com'
+ 'email' => 'admin@example.com',
];
-
$items = $searchResult->getItems();
-
- /** @var CartInterface $actualQuote */
$actualQuote = array_pop($items);
-
- /** @var UserInterface $testAttribute */
$testAttribute = $actualQuote->getExtensionAttributes()->getQuoteTestAttribute();
-
$this->assertInstanceOf(CartInterface::class, $actualQuote);
$this->assertEquals('test01', $actualQuote->getReservedOrderId());
$this->assertEquals($expectedExtensionAttributes['firstname'], $testAttribute->getFirstName());
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
index a9fdbf59a371b..081cae5f98ee5 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
@@ -8,46 +8,111 @@
namespace Magento\Quote\Model;
use Magento\Catalog\Api\ProductRepositoryInterface;
-use Magento\Catalog\Model\ProductRepository;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Customer\Api\Data\CustomerInterfaceFactory;
-use Magento\Customer\Model\Data\Customer;
+use Magento\Customer\Model\CustomerFactory;
+use Magento\Customer\Model\GroupFactory;
+use Magento\Customer\Model\GroupManagement;
+use Magento\Customer\Model\ResourceModel\Customer as CustomerResourceModel;
+use Magento\Framework\Api\DataObjectHelper;
+use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\ObjectManagerInterface;
use Magento\Quote\Api\Data\AddressInterface;
+use Magento\Quote\Api\Data\AddressInterfaceFactory;
+use Magento\Quote\Api\Data\CartInterface;
+use Magento\Quote\Api\Data\CartItemInterface;
+use Magento\Quote\Api\Data\CartItemInterfaceFactory;
use Magento\TestFramework\Helper\Bootstrap;
-use Magento\TestFramework\ObjectManager;
-use Magento\Framework\Api\SearchCriteriaBuilder;
-use Magento\Quote\Api\CartRepositoryInterface;
-use Magento\Framework\Api\ExtensibleDataInterface;
+use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
+use PHPUnit\Framework\TestCase;
/**
+ * Tests for quote model.
+ *
+ * @see \Magento\Quote\Model\Quote
+ *
+ * @magentoDbIsolation enabled
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class QuoteTest extends \PHPUnit\Framework\TestCase
+class QuoteTest extends TestCase
{
- /**
- * @var ObjectManager
- */
+ /** @var ObjectManagerInterface */
private $objectManager;
+ /** @var ProductRepositoryInterface */
+ private $productRepository;
+
+ /** @var GetQuoteByReservedOrderId */
+ private $getQuoteByReservedOrderId;
+
+ /** @var QuoteFactory */
+ private $quoteFactory;
+
+ /** @var DataObjectHelper */
+ private $dataObjectHelper;
+
+ /** @var CustomerRepositoryInterface */
+ private $customerRepository;
+
+ /** @var CustomerInterfaceFactory */
+ private $customerDataFactory;
+
+ /** @var CustomerFactory */
+ private $customerFactory;
+
+ /** @var AddressInterfaceFactory */
+ private $addressFactory;
+
+ /** @var CartItemInterfaceFactory */
+ private $itemFactory;
+
+ /** @var CustomerResourceModel */
+ private $customerResourceModel;
+
+ /** @var int */
+ private $customerIdToDelete;
+
+ /** @var GroupFactory */
+ private $groupFactory;
+
+ /** @var ExtensibleDataObjectConverter */
+ private $extensibleDataObjectConverter;
+
/**
* @inheritdoc
*/
protected function setUp(): void
{
+ parent::setUp();
+
$this->objectManager = Bootstrap::getObjectManager();
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->quoteFactory = $this->objectManager->get(QuoteFactory::class);
+ $this->dataObjectHelper = $this->objectManager->get(DataObjectHelper::class);
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
+ $this->customerDataFactory = $this->objectManager->get(CustomerInterfaceFactory::class);
+ $this->customerFactory = $this->objectManager->get(CustomerFactory::class);
+ $this->addressFactory = $this->objectManager->get(AddressInterfaceFactory::class);
+ $this->itemFactory = $this->objectManager->get(CartItemInterfaceFactory::class);
+ $this->customerResourceModel = $this->objectManager->get(CustomerResourceModel::class);
+ $this->groupFactory = $this->objectManager->get(GroupFactory::class);
+ $this->extensibleDataObjectConverter = $this->objectManager->get(ExtensibleDataObjectConverter::class);
}
/**
- * @param ExtensibleDataInterface $entity
- * @return array
+ * @inheritdoc
*/
- private function convertToArray(ExtensibleDataInterface $entity): array
+ protected function tearDown(): void
{
- return $this->objectManager
- ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class)
- ->toFlatArray($entity);
+ if ($this->customerIdToDelete) {
+ $this->customerRepository->deleteById($this->customerIdToDelete);
+ }
+
+ parent::tearDown();
}
/**
@@ -57,16 +122,10 @@ private function convertToArray(ExtensibleDataInterface $entity): array
*/
public function testCollectTotalsWithVirtual(): void
{
- $quote = $this->objectManager->create(Quote::class);
- $quote->load('test01', 'reserved_order_id');
-
- $productRepository = $this->objectManager->create(
- ProductRepositoryInterface::class
- );
- $product = $productRepository->get('virtual-product', false, null, true);
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
+ $product = $this->productRepository->get('virtual-product', false, null, true);
$quote->addProduct($product);
$quote->collectTotals();
-
$this->assertEquals(2, $quote->getItemsQty());
$this->assertEquals(1, $quote->getVirtualItemsQty());
$this->assertEquals(20, $quote->getGrandTotal());
@@ -75,15 +134,13 @@ public function testCollectTotalsWithVirtual(): void
/**
* @magentoDataFixture Magento/Catalog/_files/product_virtual.php
- * @magentoDataFixture Magento/Quote/_files/empty_quote.php
+ *
* @return void
*/
- public function testGetAddressWithVirtualProduct()
+ public function testGetAddressWithVirtualProduct(): void
{
- /** @var Quote $quote */
$quote = $this->objectManager->create(Quote::class);
- $quote->load('reserved_order_id_1', 'reserved_order_id');
- $billingAddress = $this->objectManager->create(AddressInterface::class);
+ $billingAddress = $this->addressFactory->create();
$billingAddress->setFirstname('Joe')
->setLastname('Doe')
->setCountryId('US')
@@ -93,7 +150,7 @@ public function testGetAddressWithVirtualProduct()
->setPostcode('11501')
->setTelephone('123456789');
$quote->setBillingAddress($billingAddress);
- $shippingAddress = $this->objectManager->create(AddressInterface::class);
+ $shippingAddress = $this->addressFactory->create();
$shippingAddress->setFirstname('Joe')
->setLastname('Doe')
->setCountryId('US')
@@ -103,10 +160,7 @@ public function testGetAddressWithVirtualProduct()
->setPostcode('07102')
->setTelephone('9734685221');
$quote->setShippingAddress($shippingAddress);
- $productRepository = $this->objectManager->create(
- ProductRepositoryInterface::class
- );
- $product = $productRepository->get('virtual-product', false, null, true);
+ $product = $this->productRepository->get('virtual-product', false, null, true);
$quote->addProduct($product);
$quote->save();
$expectedAddress = $quote->getBillingAddress();
@@ -118,73 +172,43 @@ public function testGetAddressWithVirtualProduct()
*/
public function testSetCustomerData(): void
{
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- /** @var CustomerInterfaceFactory $customerFactory */
- $customerFactory = $this->objectManager->create(
- CustomerInterfaceFactory::class
- );
- /** @var \Magento\Framework\Api\DataObjectHelper $dataObjectHelper */
- $dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class);
- $expected = $this->_getCustomerDataArray();
- $customer = $customerFactory->create();
- $dataObjectHelper->populateWithArray(
- $customer,
- $expected,
- \Magento\Customer\Api\Data\CustomerInterface::class
- );
-
- $this->assertEquals($expected, $this->convertToArray($customer));
+ $quote = $this->quoteFactory->create();
+ $expected = $this->getCustomerDataArray();
+ $customer = $this->customerDataFactory->create();
+ $this->dataObjectHelper->populateWithArray($customer, $expected, CustomerInterfaceFactory::class);
+ $this->assertEquals($expected, $this->extensibleDataObjectConverter->toFlatArray($customer));
$quote->setCustomer($customer);
$customer = $quote->getCustomer();
- $this->assertEquals($expected, $this->convertToArray($customer));
- $this->assertEquals('qa@example.com', $quote->getCustomerEmail());
- $this->assertEquals('Joe', $quote->getCustomerFirstname());
- $this->assertEquals('Dou', $quote->getCustomerLastname());
- $this->assertEquals('Ivan', $quote->getCustomerMiddlename());
+ $this->assertEquals($expected, $this->extensibleDataObjectConverter->toFlatArray($customer));
+ $this->assertEquals($expected[CustomerInterface::EMAIL], $quote->getCustomerEmail());
+ $this->assertEquals($expected[CustomerInterface::FIRSTNAME], $quote->getCustomerFirstname());
+ $this->assertEquals($expected[CustomerInterface::LASTNAME], $quote->getCustomerLastname());
+ $this->assertEquals($expected[CustomerInterface::MIDDLENAME], $quote->getCustomerMiddlename());
}
/**
+ * @magentoAppArea adminhtml
+ *
* @return void
*/
public function testUpdateCustomerData(): void
{
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $customerFactory = $this->objectManager->create(
- CustomerInterfaceFactory::class
- );
- /** @var \Magento\Framework\Api\DataObjectHelper $dataObjectHelper */
- $dataObjectHelper = $this->objectManager->create(\Magento\Framework\Api\DataObjectHelper::class);
- $expected = $this->_getCustomerDataArray();
- //For save in repository
- $expected = $this->removeIdFromCustomerData($expected);
- $customerDataSet = $customerFactory->create();
- $dataObjectHelper->populateWithArray(
- $customerDataSet,
- $expected,
- \Magento\Customer\Api\Data\CustomerInterface::class
- );
- $this->assertEquals($expected, $this->convertToArray($customerDataSet));
- /**
- * @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
- */
- $customerRepository = $this->objectManager
- ->create(\Magento\Customer\Api\CustomerRepositoryInterface::class);
- $customerRepository->save($customerDataSet);
+ $quote = $this->quoteFactory->create();
+ $expected = $this->getCustomerDataArray();
+ unset($expected[CustomerInterface::ID]);
+ $customerDataSet = $this->customerDataFactory->create();
+ $this->dataObjectHelper->populateWithArray($customerDataSet, $expected, CustomerInterface::class);
+ $this->assertEquals($expected, $this->extensibleDataObjectConverter->toFlatArray($customerDataSet));
+ $customer = $this->customerRepository->save($customerDataSet);
+ $this->customerIdToDelete = $customer->getId();
$quote->setCustomer($customerDataSet);
- $expected = $this->_getCustomerDataArray();
- $expected = $this->changeEmailInCustomerData('test@example.com', $expected);
- $customerDataUpdated = $customerFactory->create();
- $dataObjectHelper->populateWithArray(
- $customerDataUpdated,
- $expected,
- \Magento\Customer\Api\Data\CustomerInterface::class
- );
+ $expected = $this->getCustomerDataArray();
+ $expected[CustomerInterface::EMAIL] = 'test@example.com';
+ $customerDataUpdated = $this->customerDataFactory->create();
+ $this->dataObjectHelper->populateWithArray($customerDataUpdated, $expected, CustomerInterface::class);
$quote->updateCustomerData($customerDataUpdated);
$customer = $quote->getCustomer();
- $expected = $this->changeEmailInCustomerData('test@example.com', $expected);
- $actual = $this->convertToArray($customer);
+ $actual = $this->extensibleDataObjectConverter->toFlatArray($customer);
foreach ($expected as $item) {
$this->assertContains($item, $actual);
}
@@ -198,19 +222,11 @@ public function testUpdateCustomerData(): void
*/
public function testGetCustomerGroupFromCustomer(): void
{
- /** Preconditions */
- /** @var CustomerInterfaceFactory $customerFactory */
- $customerFactory = $this->objectManager->create(
- CustomerInterfaceFactory::class
- );
$customerGroupId = 3;
- $customerData = $customerFactory->create()->setId(1)->setGroupId($customerGroupId);
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
+ $customerData = $this->customerDataFactory->create()->setId(1)->setGroupId($customerGroupId);
+ $quote = $this->quoteFactory->create();
$quote->setCustomer($customerData);
$quote->unsetData('customer_group_id');
-
- /** Execute SUT */
$this->assertEquals($customerGroupId, $quote->getCustomerGroupId(), "Customer group ID is invalid");
}
@@ -220,19 +236,11 @@ public function testGetCustomerGroupFromCustomer(): void
*/
public function testGetCustomerTaxClassId(): void
{
- /**
- * Preconditions: create quote and assign ID of customer group created in fixture to it.
- */
$fixtureGroupCode = 'custom_group';
$fixtureTaxClassId = 3;
- /** @var \Magento\Customer\Model\Group $group */
- $group = $this->objectManager->create(\Magento\Customer\Model\Group::class);
- $fixtureGroupId = $group->load($fixtureGroupCode, 'customer_group_code')->getId();
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
+ $fixtureGroupId = $this->groupFactory->create()->load($fixtureGroupCode, 'customer_group_code')->getId();
+ $quote = $this->quoteFactory->create();
$quote->setCustomerGroupId($fixtureGroupId);
-
- /** Execute SUT */
$this->assertEquals($fixtureTaxClassId, $quote->getCustomerTaxClassId(), 'Customer tax class ID is invalid.');
}
@@ -246,60 +254,37 @@ public function testGetCustomerTaxClassId(): void
*/
public function testAssignCustomerWithAddressChangeAddressesNotSpecified(): void
{
- /** Preconditions:
- * Customer with two addresses created
- * First address is default billing, second is default shipping.
- */
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote);
-
- /** Execute SUT */
+ $quote = $this->quoteFactory->create();
+ $customerData = $this->prepareQuoteForTestAssignCustomerWithAddressChange($quote);
$quote->assignCustomerWithAddressChange($customerData);
-
- /** Check if SUT caused expected effects */
$fixtureCustomerId = 1;
$this->assertEquals($fixtureCustomerId, $quote->getCustomerId(), 'Customer ID in quote is invalid.');
$expectedBillingAddressData = [
- 'street' => 'Green str, 67',
- 'telephone' => 3468676,
- 'postcode' => 75477,
- 'country_id' => 'US',
- 'city' => 'CityM',
- 'lastname' => 'Smith',
- 'firstname' => 'John',
- 'customer_id' => 1,
- 'customer_address_id' => 1,
- 'region_id' => 1
+ AddressInterface::KEY_STREET => 'Green str, 67',
+ AddressInterface::KEY_TELEPHONE => 3468676,
+ AddressInterface::KEY_POSTCODE => 75477,
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_CITY => 'CityM',
+ AddressInterface::KEY_LASTNAME => 'Smith',
+ AddressInterface::KEY_FIRSTNAME => 'John',
+ AddressInterface::KEY_CUSTOMER_ID => 1,
+ AddressInterface::CUSTOMER_ADDRESS_ID => 1,
+ AddressInterface::KEY_REGION_ID => 1,
];
- $billingAddress = $quote->getBillingAddress();
- foreach ($expectedBillingAddressData as $field => $value) {
- $this->assertEquals(
- $value,
- $billingAddress->getData($field),
- "'{$field}' value in quote billing address is invalid."
- );
- }
+ $this->assertQuoteAddress($expectedBillingAddressData, $quote->getBillingAddress());
$expectedShippingAddressData = [
- 'customer_address_id' => 2,
- 'telephone' => 3234676,
- 'postcode' => 47676,
- 'country_id' => 'US',
- 'city' => 'CityX',
- 'street' => 'Black str, 48',
- 'lastname' => 'Smith',
- 'firstname' => 'John',
- 'customer_id' => 1,
- 'region_id' => 1
+ AddressInterface::CUSTOMER_ADDRESS_ID => 2,
+ AddressInterface::KEY_TELEPHONE => 3234676,
+ AddressInterface::KEY_POSTCODE => 47676,
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_CITY => 'CityX',
+ AddressInterface::KEY_STREET => 'Black str, 48',
+ AddressInterface::KEY_LASTNAME => 'Smith',
+ AddressInterface::KEY_FIRSTNAME => 'John',
+ AddressInterface::KEY_CUSTOMER_ID => 1,
+ AddressInterface::KEY_REGION_ID => 1,
];
- $shippingAddress = $quote->getShippingAddress();
- foreach ($expectedShippingAddressData as $field => $value) {
- $this->assertEquals(
- $value,
- $shippingAddress->getData($field),
- "'{$field}' value in quote shipping address is invalid."
- );
- }
+ $this->assertQuoteAddress($expectedShippingAddressData, $quote->getShippingAddress());
}
/**
@@ -312,63 +297,37 @@ public function testAssignCustomerWithAddressChangeAddressesNotSpecified(): void
*/
public function testAssignCustomerWithAddressChange(): void
{
- /** Preconditions:
- * Customer with two addresses created
- * First address is default billing, second is default shipping.
- */
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote);
- /** @var \Magento\Quote\Model\Quote\Address $quoteBillingAddress */
+ $quote = $this->quoteFactory->create();
+ $customerData = $this->prepareQuoteForTestAssignCustomerWithAddressChange($quote);
$expectedBillingAddressData = [
- 'street' => 'Billing str, 67',
- 'telephone' => 16546757,
- 'postcode' => 2425457,
- 'country_id' => 'US',
- 'city' => 'CityBilling',
- 'lastname' => 'LastBilling',
- 'firstname' => 'FirstBilling',
- 'region_id' => 1
+ AddressInterface::KEY_STREET => 'Billing str, 67',
+ AddressInterface::KEY_TELEPHONE => 16546757,
+ AddressInterface::KEY_POSTCODE => 2425457,
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_CITY => 'CityBilling',
+ AddressInterface::KEY_LASTNAME => 'LastBilling',
+ AddressInterface::KEY_FIRSTNAME => 'FirstBilling',
+ AddressInterface::KEY_REGION_ID => 1,
];
- $quoteBillingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class);
+ $quoteBillingAddress = $this->addressFactory->create();
$quoteBillingAddress->setData($expectedBillingAddressData);
-
$expectedShippingAddressData = [
- 'telephone' => 787878787,
- 'postcode' => 117785,
- 'country_id' => 'US',
- 'city' => 'CityShipping',
- 'street' => 'Shipping str, 48',
- 'lastname' => 'LastShipping',
- 'firstname' => 'FirstShipping',
- 'region_id' => 1
+ AddressInterface::KEY_TELEPHONE => 787878787,
+ AddressInterface::KEY_POSTCODE => 117785,
+ AddressInterface::KEY_COUNTRY_ID => 'US',
+ AddressInterface::KEY_CITY => 'CityShipping',
+ AddressInterface::KEY_STREET => 'Shipping str, 48',
+ AddressInterface::KEY_LASTNAME => 'LastShipping',
+ AddressInterface::KEY_FIRSTNAME => 'FirstShipping',
+ AddressInterface::KEY_REGION_ID => 1,
];
- $quoteShippingAddress = $this->objectManager->create(\Magento\Quote\Model\Quote\Address::class);
+ $quoteShippingAddress = $this->addressFactory->create();
$quoteShippingAddress->setData($expectedShippingAddressData);
-
- /** Execute SUT */
$quote->assignCustomerWithAddressChange($customerData, $quoteBillingAddress, $quoteShippingAddress);
-
- /** Check if SUT caused expected effects */
$fixtureCustomerId = 1;
$this->assertEquals($fixtureCustomerId, $quote->getCustomerId(), 'Customer ID in quote is invalid.');
-
- $billingAddress = $quote->getBillingAddress();
- foreach ($expectedBillingAddressData as $field => $value) {
- $this->assertEquals(
- $value,
- $billingAddress->getData($field),
- "'{$field}' value in quote billing address is invalid."
- );
- }
- $shippingAddress = $quote->getShippingAddress();
- foreach ($expectedShippingAddressData as $field => $value) {
- $this->assertEquals(
- $value,
- $shippingAddress->getData($field),
- "'{$field}' value in quote shipping address is invalid."
- );
- }
+ $this->assertQuoteAddress($expectedBillingAddressData, $quote->getBillingAddress());
+ $this->assertQuoteAddress($expectedShippingAddressData, $quote->getShippingAddress());
}
/**
@@ -379,14 +338,11 @@ public function testAssignCustomerWithAddressChange(): void
* @magentoDataFixture Magento/Backend/_files/allowed_countries_fr.php
* @return void
*/
- public function testAssignCustomerWithAddressChangeWithNotAllowedCountry()
+ public function testAssignCustomerWithAddressChangeWithNotAllowedCountry(): void
{
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote);
+ $quote = $this->quoteFactory->create();
+ $customerData = $this->prepareQuoteForTestAssignCustomerWithAddressChange($quote);
$quote->assignCustomerWithAddressChange($customerData);
-
- /** Check that addresses are empty */
$this->assertNull($quote->getBillingAddress()->getCountryId());
$this->assertNull($quote->getShippingAddress()->getCountryId());
}
@@ -397,17 +353,9 @@ public function testAssignCustomerWithAddressChangeWithNotAllowedCountry()
*/
public function testAddProductUpdateItem(): void
{
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $quote->load('test01', 'reserved_order_id');
-
+ $quote = $this->quoteFactory->create();
$productStockQty = 100;
-
- $productRepository = $this->objectManager->create(
- ProductRepositoryInterface::class
- );
- $product = $productRepository->get('simple-1', false, null, true);
-
+ $product = $this->productRepository->get('simple-1', false, null, true);
$quote->addProduct($product, 50);
$quote->setTotalsCollectedFlag(false)->collectTotals();
$this->assertEquals(50, $quote->getItemsQty());
@@ -418,98 +366,19 @@ public function testAddProductUpdateItem(): void
'related_product' => '',
'product' => $product->getId(),
'qty' => 1,
- 'id' => 0
+ 'id' => 0,
];
$updateParams = new \Magento\Framework\DataObject($params);
$quote->updateItem($updateParams['id'], $updateParams);
$quote->setTotalsCollectedFlag(false)->collectTotals();
$this->assertEquals(1, $quote->getItemsQty());
-
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+ $this->expectException(LocalizedException::class);
// TODO: fix test or implementation as described in https://github.com/magento-engcom/msi/issues/1037
// $this->expectExceptionMessage('The requested qty is not available');
$updateParams['qty'] = $productStockQty + 1;
$quote->updateItem($updateParams['id'], $updateParams);
}
- /**
- * Prepare quote for testing assignCustomerWithAddressChange method.
- *
- * Customer with two addresses created. First address is default billing, second is default shipping.
- *
- * @param Quote $quote
- * @return CustomerInterface
- */
- protected function _prepareQuoteForTestAssignCustomerWithAddressChange(Quote $quote): CustomerInterface
- {
- $customerRepository = $this->objectManager->create(
- CustomerRepositoryInterface::class
- );
- $fixtureCustomerId = 1;
- /** @var \Magento\Customer\Model\Customer $customer */
- $customer = $this->objectManager->create(\Magento\Customer\Model\Customer::class);
- $fixtureSecondAddressId = 2;
- $customer->load($fixtureCustomerId)->setDefaultShipping($fixtureSecondAddressId)->save();
- $customerData = $customerRepository->getById($fixtureCustomerId);
- $this->assertEmpty(
- $quote->getBillingAddress()->getId(),
- "Precondition failed: billing address should be empty."
- );
- $this->assertEmpty(
- $quote->getShippingAddress()->getId(),
- "Precondition failed: shipping address should be empty."
- );
- return $customerData;
- }
-
- /**
- * @param string $email
- * @param array $customerData
- * @return array
- */
- protected function changeEmailInCustomerData(string $email, array $customerData): array
- {
- $customerData[\Magento\Customer\Model\Data\Customer::EMAIL] = $email;
- return $customerData;
- }
-
- /**
- * @param array $customerData
- * @return array
- */
- protected function removeIdFromCustomerData(array $customerData): array
- {
- unset($customerData[\Magento\Customer\Model\Data\Customer::ID]);
- return $customerData;
- }
-
- /**
- * @return array
- */
- protected function _getCustomerDataArray(): array
- {
- return [
- Customer::CONFIRMATION => 'test',
- Customer::CREATED_AT => '2/3/2014',
- Customer::CREATED_IN => 'Default',
- Customer::DEFAULT_BILLING => 'test',
- Customer::DEFAULT_SHIPPING => 'test',
- Customer::DOB => '2014-02-03 00:00:00',
- Customer::EMAIL => 'qa@example.com',
- Customer::FIRSTNAME => 'Joe',
- Customer::GENDER => 0,
- Customer::GROUP_ID => \Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID,
- Customer::ID => 1,
- Customer::LASTNAME => 'Dou',
- Customer::MIDDLENAME => 'Ivan',
- Customer::PREFIX => 'Dr.',
- Customer::STORE_ID => 1,
- Customer::SUFFIX => 'Jr.',
- Customer::TAXVAT => 1,
- Customer::WEBSITE_ID => 1
- ];
- }
-
/**
* Test to verify that reserved_order_id will be changed if it already in used
*
@@ -519,9 +388,7 @@ protected function _getCustomerDataArray(): array
*/
public function testReserveOrderId(): void
{
- /** @var Quote $quote */
- $quote = $this->objectManager->create(Quote::class);
- $quote->load('reserved_order_id', 'reserved_order_id');
+ $quote = $this->getQuoteByReservedOrderId->execute('reserved_order_id');
$quote->reserveOrderId();
$this->assertEquals('reserved_order_id', $quote->getReservedOrderId());
$quote->setReservedOrderId('100000001');
@@ -536,19 +403,10 @@ public function testReserveOrderId(): void
*/
public function testAddedProductToQuoteIsSalable(): void
{
- $productId = 99;
-
- /** @var ProductRepository $productRepository */
- $productRepository = $this->objectManager->get(ProductRepository::class);
-
- /** @var Quote $quote */
- $product = $productRepository->getById($productId, false, null, true);
-
+ $product = $this->productRepository->getById(99, false, null, true);
$this->expectException(LocalizedException::class);
- $this->expectExceptionMessage('Product that you are trying to add is not available.');
-
- $quote = $this->objectManager->create(Quote::class);
- $quote->addProduct($product);
+ $this->expectExceptionMessage((string)__('Product that you are trying to add is not available.'));
+ $this->quoteFactory->create()->addProduct($product);
}
/**
@@ -558,20 +416,15 @@ public function testAddedProductToQuoteIsSalable(): void
*/
public function testGetItemById(): void
{
- $quote = $this->objectManager->create(Quote::class);
- $quote->load('test01', 'reserved_order_id');
-
- $quoteItem = $this->objectManager->create(\Magento\Quote\Model\Quote\Item::class);
-
- $productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
- $product = $productRepository->get('simple');
-
+ $quote = $this->getQuoteByReservedOrderId->execute('test01');
+ $quoteItem = $this->itemFactory->create();
+ $product = $this->productRepository->get('simple');
$quoteItem->setProduct($product);
$quote->addItem($quoteItem);
$quote->save();
-
- $this->assertInstanceOf(\Magento\Quote\Model\Quote\Item::class, $quote->getItemById($quoteItem->getId()));
- $this->assertEquals($quoteItem->getId(), $quote->getItemById($quoteItem->getId())->getId());
+ $item = $quote->getItemById($quoteItem->getId());
+ $this->assertInstanceOf(CartItemInterface::class, $item);
+ $this->assertEquals($quoteItem->getId(), $item->getId());
}
/**
@@ -586,42 +439,32 @@ public function testGetItemById(): void
*
* @magentoDataFixture Magento/Sales/_files/quote.php
* @dataProvider giftMessageDataProvider
- * @throws LocalizedException
* @return void
*/
public function testMerge(
- $guestItemGiftMessageId,
- $customerItemGiftMessageId,
- $guestOrderGiftMessageId,
- $customerOrderGiftMessageId,
- $expectedItemGiftMessageId,
- $expectedOrderGiftMessageId
+ ?int $guestItemGiftMessageId,
+ ?int $customerItemGiftMessageId,
+ ?int $guestOrderGiftMessageId,
+ ?int $customerOrderGiftMessageId,
+ ?int $expectedItemGiftMessageId,
+ ?int $expectedOrderGiftMessageId
): void {
- $productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
- $product = $productRepository->get('simple', false, null, true);
-
- /** @var Quote $quote */
- $guestQuote = $this->getQuote('test01');
+ $product = $this->productRepository->get('simple', false, null, true);
+ $guestQuote = $this->getQuoteByReservedOrderId->execute('test01');
$guestQuote->setGiftMessageId($guestOrderGiftMessageId);
-
- /** @var Quote $customerQuote */
- $customerQuote = $this->objectManager->create(Quote::class);
+ $customerQuote = $this->quoteFactory->create();
$customerQuote->setReservedOrderId('test02')
->setStoreId($guestQuote->getStoreId())
->addProduct($product);
$customerQuote->setGiftMessageId($customerOrderGiftMessageId);
-
$guestItem = $guestQuote->getItemByProduct($product);
$guestItem->setGiftMessageId($guestItemGiftMessageId);
-
$customerItem = $customerQuote->getItemByProduct($product);
$customerItem->setGiftMessageId($customerItemGiftMessageId);
-
$customerQuote->merge($guestQuote);
$mergedItemItem = $customerQuote->getItemByProduct($product);
-
- self::assertEquals($expectedOrderGiftMessageId, $customerQuote->getGiftMessageId());
- self::assertEquals($expectedItemGiftMessageId, $mergedItemItem->getGiftMessageId());
+ $this->assertEquals($expectedOrderGiftMessageId, $customerQuote->getGiftMessageId());
+ $this->assertEquals($expectedItemGiftMessageId, $mergedItemItem->getGiftMessageId());
}
/**
@@ -638,7 +481,7 @@ public function giftMessageDataProvider(): array
'guestOrderId' => null,
'customerOrderId' => 11,
'expectedItemId' => 1,
- 'expectedOrderId' => 11
+ 'expectedOrderId' => 11,
],
[
'guestItemId' => 1,
@@ -646,28 +489,252 @@ public function giftMessageDataProvider(): array
'guestOrderId' => 11,
'customerOrderId' => 22,
'expectedItemId' => 1,
- 'expectedOrderId' => 11
- ]
+ 'expectedOrderId' => 11,
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_file_option.php
+ *
+ * @return void
+ */
+ public function testAddProductWithoutChosenOptions(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple_with_custom_file_option');
+ $result = $quote->addProduct($product);
+ $this->assertEquals(
+ (string)__(
+ 'The product\'s required option(s) weren\'t entered. Make sure the options are entered and try again.'
+ ),
+ $result
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ *
+ * @return void
+ */
+ public function testAddProductWithInvalidRequestParams(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple-1');
+ $this->expectExceptionObject(
+ new LocalizedException(__('We found an invalid request for adding product to quote.'))
+ );
+ $quote->addProduct($product, '');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_out_of_stock.php
+ *
+ * @return void
+ */
+ public function testAddProductOutOfStock(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple-out-of-stock');
+ $this->expectExceptionObject(
+ new LocalizedException(__('Product that you are trying to add is not available.'))
+ );
+ $quote->addProduct($product, 1);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ *
+ * @return void
+ */
+ public function testAddProductWithMoreQty(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple-1');
+ $this->expectExceptionObject(new LocalizedException(__('The requested qty is not available')));
+ $quote->addProduct($product, 1500);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/simple_product_with_qty_increments.php
+ *
+ * @return void
+ */
+ public function testAddProductWithQtyIncrements(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple_product_with_qty_increments');
+ $this->expectExceptionObject(
+ new LocalizedException(__('You can buy this product only in quantities of %1 at a time.', 3))
+ );
+ $quote->addProduct($product, 1);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/simple_product_min_max_sale_qty.php
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testAddProductWithMinSaleQty(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple_product_min_max_sale_qty');
+ $messages = [
+ (string)__('The fewest you may purchase is %1.', 5),
+ (string)__('The fewest you may purchase is %1', 5),
+ ];
+ $this->expectException(LocalizedException::class);
+ $this->expectExceptionMessageMatches('/' . implode('|', $messages) . '/');
+ $quote->addProduct($product, 1);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/simple_product_min_max_sale_qty.php
+ *
+ * @return void
+ */
+ public function testAddProductWithMaxSaleQty(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple_product_min_max_sale_qty');
+ $messages = [
+ (string)__('The most you may purchase is %1.', 20),
+ (string)__('The requested qty exceeds the maximum qty allowed in shopping cart'),
+ ];
+ $this->expectException(LocalizedException::class);
+ $this->expectExceptionMessageMatches('/' . implode('|', $messages) . '/');
+ $quote->addProduct($product, 25);
+ }
+
+ /**
+ * @magentoConfigFixture current_store cataloginventory/item_options/enable_qty_increments 1
+ * @magentoConfigFixture current_store cataloginventory/item_options/qty_increments 3
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testAddProductWithConfigQtyIncrements(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple-1');
+ $this->expectExceptionObject(
+ new LocalizedException(__('You can buy this product only in quantities of %1 at a time.', 3))
+ );
+ $quote->addProduct($product, 1);
+ }
+
+ /**
+ * @magentoConfigFixture current_store cataloginventory/item_options/min_sale_qty 5
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testAddProductWithConfigMinSaleQty(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple-1');
+ $messages = [
+ (string)__('The fewest you may purchase is %1.', 5),
+ (string)__('The fewest you may purchase is %1', 5),
+ ];
+ $this->expectException(LocalizedException::class);
+ $this->expectExceptionMessageMatches('/' . implode('|', $messages) . '/');
+ $quote->addProduct($product, 1);
+ }
+
+ /**
+ * @magentoConfigFixture current_store cataloginventory/item_options/max_sale_qty 20
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testAddProductWithConfigMaxSaleQty(): void
+ {
+ $quote = $this->quoteFactory->create();
+ $product = $this->productRepository->get('simple-1');
+ $messages = [
+ (string)__('The most you may purchase is %1.', 20),
+ (string)__('The requested qty exceeds the maximum qty allowed in shopping cart'),
];
+ $this->expectException(LocalizedException::class);
+ $this->expectExceptionMessageMatches('/' . implode('|', $messages) . '/');
+ $quote->addProduct($product, 25);
}
/**
- * Gets quote by reserved order id.
+ * Assert address in quote.
*
- * @param string $reservedOrderId
- * @return Quote
+ * @param array $expectedAddress
+ * @param AddressInterface $quoteAddress
+ * @return void
+ */
+ private function assertQuoteAddress(array $expectedAddress, AddressInterface $quoteAddress): void
+ {
+ foreach ($expectedAddress as $field => $value) {
+ $this->assertEquals(
+ $value,
+ $quoteAddress->getData($field),
+ sprintf('"%s" value in quote %s address is invalid.', $field, $quoteAddress->getAddressType())
+ );
+ }
+ }
+
+ /**
+ * Prepare quote for testing assignCustomerWithAddressChange method.
+ * Customer with two addresses created. First address is default billing, second is default shipping.
+ *
+ * @param CartInterface $quote
+ * @return CustomerInterface
*/
- private function getQuote(string $reservedOrderId): Quote
+ private function prepareQuoteForTestAssignCustomerWithAddressChange(CartInterface $quote): CustomerInterface
{
- /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
- $searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class);
- $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
- ->create();
+ $fixtureCustomerId = 1;
+ $fixtureSecondAddressId = 2;
+ $customer = $this->customerFactory->create();
+ $this->customerResourceModel->load($customer, $fixtureCustomerId);
+ $customer->setDefaultShipping($fixtureSecondAddressId);
+ $this->customerResourceModel->save($customer);
+ $customerData = $customer->getDataModel();
+ $this->assertEmpty(
+ $quote->getBillingAddress()->getId(),
+ "Precondition failed: billing address should be empty."
+ );
+ $this->assertEmpty(
+ $quote->getShippingAddress()->getId(),
+ "Precondition failed: shipping address should be empty."
+ );
- /** @var CartRepositoryInterface $quoteRepository */
- $quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
- $items = $quoteRepository->getList($searchCriteria)->getItems();
+ return $customerData;
+ }
- return array_pop($items);
+ /**
+ * @return array
+ */
+ private function getCustomerDataArray(): array
+ {
+ return [
+ CustomerInterface::CONFIRMATION => 'test',
+ CustomerInterface::CREATED_AT => '2/3/2014',
+ CustomerInterface::CREATED_IN => 'Default',
+ CustomerInterface::DEFAULT_BILLING => 'test',
+ CustomerInterface::DEFAULT_SHIPPING => 'test',
+ CustomerInterface::DOB => '2014-02-03 00:00:00',
+ CustomerInterface::EMAIL => 'qa@example.com',
+ CustomerInterface::FIRSTNAME => 'Joe',
+ CustomerInterface::GENDER => 0,
+ CustomerInterface::GROUP_ID => GroupManagement::NOT_LOGGED_IN_ID,
+ CustomerInterface::ID => 1,
+ CustomerInterface::LASTNAME => 'Dou',
+ CustomerInterface::MIDDLENAME => 'Ivan',
+ CustomerInterface::PREFIX => 'Dr.',
+ CustomerInterface::STORE_ID => 1,
+ CustomerInterface::SUFFIX => 'Jr.',
+ CustomerInterface::TAXVAT => 1,
+ CustomerInterface::WEBSITE_ID => 1,
+ ];
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Address/FormTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Address/FormTest.php
new file mode 100644
index 0000000000000..0a8db20d86966
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Address/FormTest.php
@@ -0,0 +1,75 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Form::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ }
+
+ /**
+ * @return void
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('order_address');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testGetFormValues(): void
+ {
+ $this->registry->unregister('order_address');
+ $order = $this->orderFactory->create()->loadByIncrementId(100000001);
+ $address = $order->getShippingAddress();
+ $this->registry->register('order_address', $address);
+ $formValues = $this->block->getFormValues();
+ $this->assertEquals($address->getData(), $formValues);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/AddressTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/AddressTest.php
new file mode 100644
index 0000000000000..8542d9bc48dcd
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/AddressTest.php
@@ -0,0 +1,111 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Address::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->orderFactory = $this->objectManager->get(OrderFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('order_address');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider addressTypeProvider
+ *
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @param string $type
+ * @return void
+ */
+ public function testGetHeaderText(string $type): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId(100000001);
+ $address = $this->getAddressByType($order, $type);
+ $this->registry->unregister('order_address');
+ $this->registry->register('order_address', $address);
+ $text = $this->block->getHeaderText();
+ $this->assertEquals(
+ (string)__('Edit Order %1 %2 Address', $order->getIncrementId(), ucfirst($type)),
+ (string)$text
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function addressTypeProvider(): array
+ {
+ return [
+ 'billing_address' => [
+ AddressType::TYPE_BILLING,
+ ],
+ 'shipping_address' => [
+ AddressType::TYPE_SHIPPING,
+ ]
+ ];
+ }
+
+ /**
+ * Get address by address type
+ *
+ * @param OrderInterface $order
+ * @param string $type
+ * @return OrderAddressInterface|null
+ */
+ private function getAddressByType(OrderInterface $order, string $type): ?OrderAddressInterface
+ {
+ return $type === AddressType::TYPE_BILLING ? $order->getBillingAddress() : $order->getShippingAddress();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php
new file mode 100644
index 0000000000000..31b5fdd81f592
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressSaveTest.php
@@ -0,0 +1,168 @@
+orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class);
+ $this->orderAddressRepository = $this->_objectManager->get(OrderAddressRepositoryInterface::class);
+ }
+
+ /**
+ * @dataProvider addressTypeProvider
+ *
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @param string $type
+ * @return void
+ */
+ public function testSave(string $type): void
+ {
+ $data = [
+ OrderAddressInterface::FIRSTNAME => 'New test name',
+ OrderAddressInterface::LASTNAME => 'New test lastname',
+ OrderAddressInterface::STREET => ['new test street'],
+ OrderAddressInterface::CITY => 'New Test City',
+ OrderAddressInterface::COUNTRY_ID => 'UA',
+ OrderAddressInterface::REGION => '1111',
+ OrderAddressInterface::POSTCODE => '97203',
+ OrderAddressInterface::TELEPHONE => '5555555555',
+ ];
+ $order = $this->orderFactory->create()->loadByIncrementId(100000001);
+ $addressId = $this->getAddressIdByType($order, $type);
+ $this->dispatchWithParams(
+ ['address_id' => $addressId],
+ $data
+ );
+ $this->assertSessionMessages(
+ $this->containsEqual((string)__('You updated the order address.'))
+ );
+ $this->assertRedirect(
+ $this->stringContains(sprintf('sales/order/view/order_id/%s/', $order->getId()))
+ );
+ $this->assertAddressData($addressId, $data);
+ }
+
+ /**
+ * @return array
+ */
+ public function addressTypeProvider(): array
+ {
+ return [
+ 'billing_address' => [
+ AddressType::TYPE_BILLING,
+ ],
+ 'shipping_address' => [
+ AddressType::TYPE_SHIPPING,
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider wrongRequestDataProvider
+ *
+ * @param array $params
+ * @param array $post
+ * @return void
+ */
+ public function testInvalidRequest(array $params, array $post = []): void
+ {
+ $this->dispatchWithParams($params, $post);
+ $this->assertRedirect($this->stringContains('backend/sales/order/index/'));
+ }
+
+ /**
+ * @return array
+ */
+ public function wrongRequestDataProvider(): array
+ {
+ return [
+ 'empty_post' => [
+ ['address_id' => 1],
+ ],
+ 'wrong_address_id' => [
+ ['address_id' => 7852147],
+ ],
+ ];
+ }
+
+ /**
+ * Check updated address data
+ *
+ * @param int $addressId
+ * @param array $expectedData
+ * @return void
+ */
+ private function assertAddressData(int $addressId, array $expectedData): void
+ {
+ $address = $this->orderAddressRepository->get($addressId);
+ foreach ($expectedData as $key => $value) {
+ $key === OrderAddressInterface::STREET
+ ? $this->assertEquals(reset($value), $address->getData($key))
+ : $this->assertEquals($value, $address->getData($key));
+ }
+ }
+
+ /**
+ * Get address id by address type
+ *
+ * @param OrderInterface $order
+ * @param string $type
+ * @return int
+ */
+ private function getAddressIdByType(OrderInterface $order, string $type): int
+ {
+ return $type === AddressType::TYPE_BILLING
+ ? (int)$order->getBillingAddressId()
+ : (int)$order->getShippingAddressId();
+ }
+
+ /**
+ * Dispatch with params
+ *
+ * @param array $params
+ * @param array $post
+ * @return void
+ */
+ private function dispatchWithParams(array $params, array $post): void
+ {
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setParams($params);
+ $this->getRequest()->setPostValue($post);
+ $this->dispatch('backend/sales/order/addressSave');
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php
new file mode 100644
index 0000000000000..6b508b662e7a4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/AddressTest.php
@@ -0,0 +1,76 @@
+orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class);
+ $this->registry = $this->_objectManager->get(Registry::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testSuccessfulEdit(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId(100000001);
+ $this->dispatchWithAddressId((int)$order->getBillingAddressId());
+ $this->assertInstanceOf(OrderAddressInterface::class, $this->registry->registry('order_address'));
+ }
+
+ /**
+ * @return void
+ */
+ public function testWithNotExistingAddressId(): void
+ {
+ $this->dispatchWithAddressId(51728);
+ $this->assertRedirect($this->stringContains('backend/sales/order/index/'));
+ }
+
+ /**
+ * Dispatch request with address_id param
+ *
+ * @param int $addressId
+ * @return void
+ */
+ private function dispatchWithAddressId(int $addressId): void
+ {
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setParam('address_id', $addressId);
+ $this->dispatch('backend/sales/order/address');
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_store_group_and_store.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_store_group_and_store.php
index ca469840daa31..a4e2b05b1a28c 100644
--- a/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_store_group_and_store.php
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_website_with_store_group_and_store.php
@@ -5,6 +5,7 @@
*/
declare(strict_types=1);
+use Magento\Catalog\Helper\DefaultCategory;
use Magento\CatalogSearch\Model\Indexer\Fulltext;
use Magento\Framework\Indexer\IndexerRegistry;
use Magento\Store\Api\Data\GroupInterface;
@@ -28,6 +29,8 @@
$storeResource = $objectManager->get(StoreResource::class);
/** @var GroupResource $groupResource */
$groupResource = $objectManager->get(GroupResource::class);
+/** @var DefaultCategory $defaultCategory */
+$defaultCategory = $objectManager->get(DefaultCategory::class);
/** @var WebsiteInterface $website */
$website = $objectManager->get(WebsiteInterfaceFactory::class)->create();
$website->setCode('test')->setName('Test Website');
@@ -35,6 +38,7 @@
/** @var GroupInterface $storeGroup */
$storeGroup = $objectManager->get(GroupInterfaceFactory::class)->create();
$storeGroup->setCode('second_group')
+ ->setRootCategoryId($defaultCategory->getId())
->setName('second store group')
->setWebsite($website);
$groupResource->save($storeGroup);
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js
index fb37c73abdd41..a3d49e382de51 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js
@@ -45,6 +45,14 @@ define([
'qty': '1'
});
});
+ it('return object from url with multiple filters parameter and filter value as array', function () {
+ var urlSearch = '?filters[name]=[27,23]&filters[qty]=1&anotherparam=1';
+
+ expect(urlFilterApplierObj.getFilterParam(urlSearch)).toEqual({
+ 'name': ['27', '23'],
+ 'qty': '1'
+ });
+ });
it('return object from url with another parameter', function () {
var urlSearch = '?anotherparam=1';
diff --git a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php
index 26457f23c6c78..69156225e9c39 100644
--- a/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php
+++ b/lib/internal/Magento/Framework/TestFramework/Unit/Helper/ObjectManager.php
@@ -5,9 +5,12 @@
*/
namespace Magento\Framework\TestFramework\Unit\Helper;
+use PHPUnit\Framework\MockObject\MockObject;
+
/**
* Helper class for basic object retrieving, such as blocks, models etc...
*
+ * @deprecated Class under test should be instantiated with `new` keyword with explicit dependencies declaration
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ObjectManager
@@ -44,7 +47,7 @@ public function __construct(\PHPUnit\Framework\TestCase $testObject)
*
* @param string $argClassName
* @param array $originalArguments
- * @return null|object|\PHPUnit_Framework_MockObject_MockObject
+ * @return null|object|MockObject
*/
protected function _createArgumentMock($argClassName, array $originalArguments)
{
@@ -82,7 +85,7 @@ protected function _processSpecialCases($className, $arguments)
/**
* Retrieve specific mock of core resource model
*
- * @return \Magento\Framework\Module\ResourceInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @return \Magento\Framework\Module\ResourceInterface|MockObject
*/
protected function _getResourceModelMock()
{
@@ -108,7 +111,7 @@ protected function _getResourceModelMock()
* Retrieve mock of core translator model
*
* @param string $className
- * @return \Magento\Framework\TranslateInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @return \Magento\Framework\TranslateInterface|MockObject
*/
protected function _getTranslatorMock($className)
{
@@ -130,7 +133,7 @@ protected function _getTranslatorMock($className)
* Get mock without call of original constructor
*
* @param string $className
- * @return \PHPUnit_Framework_MockObject_MockObject
+ * @return MockObject
*/
protected function _getMockWithoutConstructorCall($className)
{
@@ -152,6 +155,7 @@ protected function _getMockWithoutConstructorCall($className)
*/
public function getObject($className, array $arguments = [])
{
+ // phpstan:ignore
if (is_subclass_of($className, \Magento\Framework\Api\AbstractSimpleObjectBuilder::class)
|| is_subclass_of($className, \Magento\Framework\Api\Builder::class)
) {
@@ -262,6 +266,7 @@ public function getConstructArguments($className, array $arguments = [])
$defaultValue = $parameter->getDefaultValue();
}
+ $object = null;
try {
if ($parameter->getClass()) {
$argClassName = $parameter->getClass()->getName();
@@ -292,7 +297,7 @@ public function getConstructArguments($className, array $arguments = [])
*
* @param string $className
* @param array $data
- * @return \PHPUnit_Framework_MockObject_MockObject
+ * @return MockObject
* @throws \InvalidArgumentException
*/
public function getCollectionMock($className, array $data)
@@ -326,17 +331,16 @@ public function getCollectionMock($className, array $data)
*
* @param string $argClassName
* @param array $arguments
- * @return null|object|\PHPUnit_Framework_MockObject_MockObject
+ * @return null|object|MockObject
*/
private function _getMockObject($argClassName, array $arguments)
{
+ // phpstan:ignore
if (is_subclass_of($argClassName, \Magento\Framework\Api\ExtensibleObjectBuilder::class)) {
- $object = $this->getBuilder($argClassName, $arguments);
- return $object;
- } else {
- $object = $this->_createArgumentMock($argClassName, $arguments);
- return $object;
+ return $this->getBuilder($argClassName, $arguments);
}
+
+ return $this->_createArgumentMock($argClassName, $arguments);
}
/**
diff --git a/lib/internal/Magento/Framework/View/Page/Builder.php b/lib/internal/Magento/Framework/View/Page/Builder.php
index 66cc3f588a9a0..846ffd119dabd 100644
--- a/lib/internal/Magento/Framework/View/Page/Builder.php
+++ b/lib/internal/Magento/Framework/View/Page/Builder.php
@@ -6,11 +6,13 @@
namespace Magento\Framework\View\Page;
use Magento\Framework\App;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\Event;
use Magento\Framework\View;
+use Magento\Framework\View\Model\PageLayout\Config\BuilderInterface;
/**
- * Class Builder
+ * Page Layout Builder
*/
class Builder extends View\Layout\Builder
{
@@ -24,23 +26,31 @@ class Builder extends View\Layout\Builder
*/
protected $pageLayoutReader;
+ /**
+ * @var BuilderInterface|mixed
+ */
+ private $pageLayoutBuilder;
+
/**
* @param View\LayoutInterface $layout
* @param App\Request\Http $request
* @param Event\ManagerInterface $eventManager
* @param Config $pageConfig
* @param Layout\Reader $pageLayoutReader
+ * @param BuilderInterface|null $pageLayoutBuilder
*/
public function __construct(
View\LayoutInterface $layout,
App\Request\Http $request,
Event\ManagerInterface $eventManager,
Config $pageConfig,
- Layout\Reader $pageLayoutReader
+ Layout\Reader $pageLayoutReader,
+ ?BuilderInterface $pageLayoutBuilder = null
) {
parent::__construct($layout, $request, $eventManager);
$this->pageConfig = $pageConfig;
$this->pageLayoutReader = $pageLayoutReader;
+ $this->pageLayoutBuilder = $pageLayoutBuilder ?? ObjectManager::getInstance()->get(BuilderInterface::class);
$this->pageConfig->setBuilder($this);
}
@@ -57,6 +67,7 @@ protected function generateLayoutBlocks()
/**
* Read page layout and write structure to ReadContext
+ *
* @return void
*/
protected function readPageLayout()
@@ -69,10 +80,16 @@ protected function readPageLayout()
}
/**
+ * Get current page layout or fallback to default
+ *
* @return string
*/
protected function getPageLayout()
{
- return $this->pageConfig->getPageLayout() ?: $this->layout->getUpdate()->getPageLayout();
+ $pageLayout = $this->pageConfig->getPageLayout();
+
+ return ($pageLayout && $this->pageLayoutBuilder->getPageLayoutsConfig()->hasPageLayout($pageLayout))
+ ? $pageLayout
+ : $this->layout->getUpdate()->getPageLayout();
}
}
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Page/BuilderTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Page/BuilderTest.php
index 77bdd91041ad2..0f94e32b0a707 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Page/BuilderTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Page/BuilderTest.php
@@ -8,9 +8,11 @@
namespace Magento\Framework\View\Test\Unit\Page;
use Magento\Framework\View\Layout\Reader\Context;
+use Magento\Framework\View\Model\PageLayout\Config\BuilderInterface;
use Magento\Framework\View\Page\Builder;
use Magento\Framework\View\Page\Config;
use Magento\Framework\View\Page\Layout\Reader;
+use Magento\Framework\View\PageLayout\Config as PageLayoutConfig;
use PHPUnit\Framework\MockObject\MockObject;
/**
@@ -22,7 +24,7 @@ class BuilderTest extends \Magento\Framework\View\Test\Unit\Layout\BuilderTest
/**
* @param array $arguments
- * @return \Magento\Framework\View\Page\Builder
+ * @return \Magento\Framework\View\Layout\Builder
*/
protected function getBuilder($arguments)
{
@@ -39,6 +41,15 @@ protected function getBuilder($arguments)
$arguments['pageLayoutReader'] = $this->createMock(Reader::class);
$arguments['pageLayoutReader']->expects($this->once())->method('read')->with($readerContext, 'test_layout');
+ $pageLayoutConfig = $this->createMock(PageLayoutConfig::class);
+ $arguments['pageLayoutBuilder'] = $this->getMockForAbstractClass(BuilderInterface::class);
+ $arguments['pageLayoutBuilder']->expects($this->once())
+ ->method('getPageLayoutsConfig')
+ ->willReturn($pageLayoutConfig);
+ $pageLayoutConfig->expects($this->once())
+ ->method('hasPageLayout')
+ ->with('test_layout')
+ ->willReturn(true);
return parent::getBuilder($arguments);
}