isSuggestionsAllowed()):?>
- data-mage-init='{"quickSearch":{
- "formSelector":"#search_mini_form",
- "url":"= $block->escapeUrl($helper->getSuggestUrl())?>",
- "destinationSelector":"#search_autocomplete",
- "minSearchLength":"= $block->escapeHtml($helper->getMinQueryLength()) ?>"}
- }'
-
+ data-mage-init='{
+ "quickSearch": {
+ "formSelector": "#search_mini_form",
+ "url": "= /* @noEscape */ $quickSearchUrl ?>",
+ "destinationSelector": "#search_autocomplete",
+ "minSearchLength": "= $escaper->escapeHtml($helper->getMinQueryLength()) ?>"
+ }
+ }'
type="text"
- name="= $block->escapeHtmlAttr($helper->getQueryParamName()) ?>"
+ name="= $escaper->escapeHtmlAttr($helper->getQueryParamName()) ?>"
value="= /* @noEscape */ $helper->getEscapedQueryText() ?>"
- placeholder="= $block->escapeHtmlAttr(__('Search entire store here...')) ?>"
+ placeholder="= $escaper->escapeHtmlAttr(__('Search entire store here...')) ?>"
class="input-text"
- maxlength="= $block->escapeHtmlAttr($helper->getMaxQueryLength()) ?>"
+ maxlength="= $escaper->escapeHtmlAttr($helper->getMaxQueryLength()) ?>"
role="combobox"
aria-haspopup="false"
aria-autocomplete="both"
@@ -49,11 +51,11 @@ $configProvider = $block->getData('configProvider');
diff --git a/app/code/Magento/Search/view/frontend/web/js/form-mini.js b/app/code/Magento/Search/view/frontend/web/js/form-mini.js
index 9b4c814f73d73..b8034fead76d0 100644
--- a/app/code/Magento/Search/view/frontend/web/js/form-mini.js
+++ b/app/code/Magento/Search/view/frontend/web/js/form-mini.js
@@ -35,12 +35,12 @@ define([
selectClass: 'selected',
template:
'
' +
- '' +
- ' <%- data.title %>' +
- '' +
- '' +
- '<%- data.num_results %>' +
- '' +
+ '' +
+ ' <%- data.title %>' +
+ '' +
+ '' +
+ '<%- data.num_results %>' +
+ '' +
'',
submitBtn: 'button[type="submit"]',
searchLabel: '[data-role=minisearch-label]',
@@ -300,60 +300,63 @@ define([
if (value.length >= parseInt(this.options.minSearchLength, 10)) {
this.submitBtn.disabled = false;
- $.getJSON(this.options.url, {
- q: value
- }, $.proxy(function (data) {
- if (data.length) {
- $.each(data, function (index, element) {
- var html;
-
- element.index = index;
- html = template({
- data: element
- });
- dropdown.append(html);
- });
-
- this._resetResponseList(true);
- this.responseList.indexList = this.autoComplete.html(dropdown)
- .css(clonePosition)
- .show()
- .find(this.options.responseFieldElements + ':visible');
-
- this.element.removeAttr('aria-activedescendant');
+ if (this.options.url !== '') { //eslint-disable-line eqeqeq
+ $.getJSON(this.options.url, {
+ q: value
+ }, $.proxy(function (data) {
+ if (data.length) {
+ $.each(data, function (index, element) {
+ var html;
+
+ element.index = index;
+ html = template({
+ data: element
+ });
+ dropdown.append(html);
+ });
- if (this.responseList.indexList.length) {
- this._updateAriaHasPopup(true);
+ this._resetResponseList(true);
+
+ this.responseList.indexList = this.autoComplete.html(dropdown)
+ .css(clonePosition)
+ .show()
+ .find(this.options.responseFieldElements + ':visible');
+
+ this.element.removeAttr('aria-activedescendant');
+
+ if (this.responseList.indexList.length) {
+ this._updateAriaHasPopup(true);
+ } else {
+ this._updateAriaHasPopup(false);
+ }
+
+ this.responseList.indexList
+ .on('click', function (e) {
+ this.responseList.selected = $(e.currentTarget);
+ this.searchForm.trigger('submit');
+ }.bind(this))
+ .on('mouseenter mouseleave', function (e) {
+ this.responseList.indexList.removeClass(this.options.selectClass);
+ $(e.target).addClass(this.options.selectClass);
+ this.responseList.selected = $(e.target);
+ this.element.attr('aria-activedescendant', $(e.target).attr('id'));
+ }.bind(this))
+ .on('mouseout', function (e) {
+ if (!this._getLastElement() &&
+ this._getLastElement().hasClass(this.options.selectClass)) {
+ $(e.target).removeClass(this.options.selectClass);
+ this._resetResponseList(false);
+ }
+ }.bind(this));
} else {
+ this._resetResponseList(true);
+ this.autoComplete.hide();
this._updateAriaHasPopup(false);
+ this.element.removeAttr('aria-activedescendant');
}
-
- this.responseList.indexList
- .on('click', function (e) {
- this.responseList.selected = $(e.currentTarget);
- this.searchForm.trigger('submit');
- }.bind(this))
- .on('mouseenter mouseleave', function (e) {
- this.responseList.indexList.removeClass(this.options.selectClass);
- $(e.target).addClass(this.options.selectClass);
- this.responseList.selected = $(e.target);
- this.element.attr('aria-activedescendant', $(e.target).attr('id'));
- }.bind(this))
- .on('mouseout', function (e) {
- if (!this._getLastElement() &&
- this._getLastElement().hasClass(this.options.selectClass)) {
- $(e.target).removeClass(this.options.selectClass);
- this._resetResponseList(false);
- }
- }.bind(this));
- } else {
- this._resetResponseList(true);
- this.autoComplete.hide();
- this._updateAriaHasPopup(false);
- this.element.removeAttr('aria-activedescendant');
- }
- }, this));
+ }, this));
+ }
} else {
this._resetResponseList(true);
this.autoComplete.hide();
diff --git a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
index 100ba029beabd..0c9738540322c 100644
--- a/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
+++ b/app/code/Magento/Shipping/Controller/Adminhtml/Order/Shipment/Save.php
@@ -8,10 +8,11 @@
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\Controller\ResultFactory;
+use Magento\Sales\Helper\Data as SalesData;
use Magento\Sales\Model\Order\Shipment\Validation\QuantityValidator;
/**
- * Class Save
+ * Controller for generation of new Shipments from Backend
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Save extends \Magento\Backend\App\Action implements HttpPostActionInterface
@@ -43,19 +44,26 @@ class Save extends \Magento\Backend\App\Action implements HttpPostActionInterfac
*/
private $shipmentValidator;
+ /**
+ * @var SalesData
+ */
+ private $salesData;
+
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader
* @param \Magento\Shipping\Model\Shipping\LabelGenerator $labelGenerator
* @param \Magento\Sales\Model\Order\Email\Sender\ShipmentSender $shipmentSender
* @param \Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface|null $shipmentValidator
+ * @param SalesData $salesData
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Shipping\Controller\Adminhtml\Order\ShipmentLoader $shipmentLoader,
\Magento\Shipping\Model\Shipping\LabelGenerator $labelGenerator,
\Magento\Sales\Model\Order\Email\Sender\ShipmentSender $shipmentSender,
- \Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface $shipmentValidator = null
+ \Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface $shipmentValidator = null,
+ SalesData $salesData = null
) {
parent::__construct($context);
@@ -64,6 +72,8 @@ public function __construct(
$this->shipmentSender = $shipmentSender;
$this->shipmentValidator = $shipmentValidator ?: \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface::class);
+ $this->salesData = $salesData ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(SalesData::class);
}
/**
@@ -109,6 +119,7 @@ public function execute()
}
$data = $this->getRequest()->getParam('shipment');
+ $orderId = $this->getRequest()->getParam('order_id');
if (!empty($data['comment_text'])) {
$this->_objectManager->get(\Magento\Backend\Model\Session::class)->setCommentText($data['comment_text']);
@@ -118,7 +129,7 @@ public function execute()
$responseAjax = new \Magento\Framework\DataObject();
try {
- $this->shipmentLoader->setOrderId($this->getRequest()->getParam('order_id'));
+ $this->shipmentLoader->setOrderId($orderId);
$this->shipmentLoader->setShipmentId($this->getRequest()->getParam('shipment_id'));
$this->shipmentLoader->setShipment($data);
$this->shipmentLoader->setTracking($this->getRequest()->getParam('tracking'));
@@ -143,7 +154,7 @@ public function execute()
$this->messageManager->addErrorMessage(
__("Shipment Document Validation Error(s):\n" . implode("\n", $validationResult->getMessages()))
);
- return $resultRedirect->setPath('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]);
+ return $resultRedirect->setPath('*/*/new', ['order_id' => $orderId]);
}
$shipment->register();
@@ -156,7 +167,7 @@ public function execute()
$this->_saveShipment($shipment);
- if (!empty($data['send_email'])) {
+ if (!empty($data['send_email']) && $this->salesData->canSendNewShipmentEmail()) {
$this->shipmentSender->send($shipment);
}
@@ -173,7 +184,7 @@ public function execute()
$responseAjax->setMessage($e->getMessage());
} else {
$this->messageManager->addErrorMessage($e->getMessage());
- return $resultRedirect->setPath('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]);
+ return $resultRedirect->setPath('*/*/new', ['order_id' => $orderId]);
}
} catch (\Exception $e) {
$this->_objectManager->get(\Psr\Log\LoggerInterface::class)->critical($e);
@@ -182,13 +193,13 @@ public function execute()
$responseAjax->setMessage(__('An error occurred while creating shipping label.'));
} else {
$this->messageManager->addErrorMessage(__('Cannot save shipment.'));
- return $resultRedirect->setPath('*/*/new', ['order_id' => $this->getRequest()->getParam('order_id')]);
+ return $resultRedirect->setPath('*/*/new', ['order_id' => $orderId]);
}
}
if ($isNeedCreateLabel) {
return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setJsonData($responseAjax->toJson());
}
- return $resultRedirect->setPath('sales/order/view', ['order_id' => $shipment->getOrderId()]);
+ return $resultRedirect->setPath('sales/order/view', ['order_id' => $orderId]);
}
}
diff --git a/app/code/Magento/Shipping/Model/Config.php b/app/code/Magento/Shipping/Model/Config.php
index 26a76c90d3c85..565901ebe8ef0 100644
--- a/app/code/Magento/Shipping/Model/Config.php
+++ b/app/code/Magento/Shipping/Model/Config.php
@@ -4,16 +4,21 @@
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Shipping\Model;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\DataObject;
use Magento\Shipping\Model\Carrier\AbstractCarrierInterface;
+use Magento\Store\Model\ScopeInterface;
/**
- * Class Config
+ * Config model for shipping
* @api
* @since 100.0.2
*/
-class Config extends \Magento\Framework\DataObject
+class Config extends DataObject
{
/**
* Shipping origin settings
@@ -29,25 +34,25 @@ class Config extends \Magento\Framework\DataObject
/**
* Core store config
*
- * @var \Magento\Framework\App\Config\ScopeConfigInterface
+ * @var ScopeConfigInterface
*/
protected $_scopeConfig;
/**
- * @var \Magento\Shipping\Model\CarrierFactory
+ * @var CarrierFactory
*/
protected $_carrierFactory;
/**
* Constructor
*
- * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
- * @param \Magento\Shipping\Model\CarrierFactory $carrierFactory
+ * @param ScopeConfigInterface $scopeConfig
+ * @param CarrierFactory $carrierFactory
* @param array $data
*/
public function __construct(
- \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
- \Magento\Shipping\Model\CarrierFactory $carrierFactory,
+ ScopeConfigInterface $scopeConfig,
+ CarrierFactory $carrierFactory,
array $data = []
) {
$this->_scopeConfig = $scopeConfig;
@@ -58,17 +63,17 @@ public function __construct(
/**
* Retrieve active system carriers
*
- * @param mixed $store
- * @return AbstractCarrierInterface[]
+ * @param mixed $store
+ * @return AbstractCarrierInterface[]
*/
public function getActiveCarriers($store = null)
{
$carriers = [];
- $config = $this->_scopeConfig->getValue('carriers', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $store);
+ $config = $this->getCarriersConfig($store);
foreach (array_keys($config) as $carrierCode) {
if ($this->_scopeConfig->isSetFlag(
'carriers/' . $carrierCode . '/active',
- \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
+ ScopeInterface::SCOPE_STORE,
$store
)) {
$carrierModel = $this->_carrierFactory->create($carrierCode, $store);
@@ -77,25 +82,38 @@ public function getActiveCarriers($store = null)
}
}
}
+
return $carriers;
}
/**
* Retrieve all system carriers
*
- * @param mixed $store
- * @return AbstractCarrierInterface[]
+ * @param mixed $store
+ * @return AbstractCarrierInterface[]
*/
public function getAllCarriers($store = null)
{
$carriers = [];
- $config = $this->_scopeConfig->getValue('carriers', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $store);
+ $config = $this->getCarriersConfig($store);
foreach (array_keys($config) as $carrierCode) {
$model = $this->_carrierFactory->create($carrierCode, $store);
if ($model) {
$carriers[$carrierCode] = $model;
}
}
+
return $carriers;
}
+
+ /**
+ * Returns carriers config by store
+ *
+ * @param mixed $store
+ * @return array
+ */
+ private function getCarriersConfig($store = null): array
+ {
+ return $this->_scopeConfig->getValue('carriers', ScopeInterface::SCOPE_STORE, $store) ?: [];
+ }
}
diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml
index d448f51a00406..c174517375779 100644
--- a/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml
+++ b/app/code/Magento/Shipping/Test/Mftf/Test/StorefrontDisplayTableRatesShippingMethodForAETest.xml
@@ -27,6 +27,8 @@
+
+
diff --git a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php
index 1d71f8a0d8e0c..afd5f8819a8e0 100644
--- a/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php
+++ b/app/code/Magento/Shipping/Test/Unit/Controller/Adminhtml/Order/Shipment/SaveTest.php
@@ -21,6 +21,7 @@
use Magento\Framework\Message\Manager;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+use Magento\Sales\Helper\Data as SalesData;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Email\Sender\ShipmentSender;
use Magento\Sales\Model\Order\Shipment;
@@ -119,6 +120,11 @@ class SaveTest extends TestCase
*/
private $validationResult;
+ /**
+ * @var SalesData|MockObject
+ */
+ private $salesData;
+
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
@@ -140,7 +146,14 @@ protected function setUp(): void
->getMock();
$this->shipmentSender = $this->getMockBuilder(ShipmentSender::class)
->disableOriginalConstructor()
- ->setMethods([])
+ ->setMethods(['send'])
+ ->getMock();
+ $this->shipmentSender->expects($this->any())
+ ->method('send')
+ ->willReturn(true);
+ $this->salesData = $this->getMockBuilder(SalesData::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['canSendNewShipmentEmail'])
->getMock();
$this->objectManager = $this->getMockForAbstractClass(ObjectManagerInterface::class);
$this->context = $this->createPartialMock(Context::class, [
@@ -232,7 +245,8 @@ protected function setUp(): void
'shipmentLoader' => $this->shipmentLoader,
'request' => $this->request,
'response' => $this->response,
- 'shipmentValidator' => $this->shipmentValidatorMock
+ 'shipmentValidator' => $this->shipmentValidatorMock,
+ 'salesData' => $this->salesData
]
);
}
@@ -240,11 +254,19 @@ protected function setUp(): void
/**
* @param bool $formKeyIsValid
* @param bool $isPost
+ * @param string $sendEmail
+ * @param bool $emailEnabled
+ * @param bool $shouldEmailBeSent
* @dataProvider executeDataProvider
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
- public function testExecute($formKeyIsValid, $isPost)
- {
+ public function testExecute(
+ $formKeyIsValid,
+ $isPost,
+ $sendEmail,
+ $emailEnabled,
+ $shouldEmailBeSent
+ ) {
$this->formKeyValidator->expects($this->any())
->method('validate')
->willReturn($formKeyIsValid);
@@ -269,7 +291,7 @@ public function testExecute($formKeyIsValid, $isPost)
$shipmentId = 1000012;
$orderId = 10003;
$tracking = [];
- $shipmentData = ['items' => [], 'send_email' => ''];
+ $shipmentData = ['items' => [], 'send_email' => $sendEmail];
$shipment = $this->createPartialMock(
Shipment::class,
['load', 'save', 'register', 'getOrder', 'getOrderId', '__wakeup']
@@ -287,6 +309,13 @@ public function testExecute($formKeyIsValid, $isPost)
]
);
+ $this->salesData->expects($this->any())
+ ->method('canSendNewShipmentEmail')
+ ->willReturn($emailEnabled);
+ if ($shouldEmailBeSent) {
+ $this->shipmentSender->expects($this->once())
+ ->method('send');
+ }
$this->shipmentLoader->expects($this->any())
->method('setShipmentId')
->with($shipmentId);
@@ -309,7 +338,7 @@ public function testExecute($formKeyIsValid, $isPost)
->willReturn($order);
$order->expects($this->once())
->method('setCustomerNoteNotify')
- ->with(false);
+ ->with(!empty($sendEmail));
$this->labelGenerator->expects($this->any())
->method('create')
->with($shipment, $this->request)
@@ -340,7 +369,7 @@ public function testExecute($formKeyIsValid, $isPost)
->with(Session::class)
->willReturn($this->session);
$arguments = ['order_id' => $orderId];
- $shipment->expects($this->once())
+ $shipment->expects($this->any())
->method('getOrderId')
->willReturn($orderId);
$this->prepareRedirect($arguments);
@@ -364,11 +393,22 @@ public function testExecute($formKeyIsValid, $isPost)
*/
public function executeDataProvider()
{
+ /**
+ * bool $formKeyIsValid
+ * bool $isPost
+ * string $sendEmail
+ * bool $emailEnabled
+ * bool $shouldEmailBeSent
+ */
return [
- [false, false],
- [true, false],
- [false, true],
- [true, true]
+ [false, false, '', false, false],
+ [true, false, '', false, false],
+ [false, true, '', false, false],
+ [true, true, '', false, false],
+ [true, true, '', true, false],
+ [true, true, 'on', false, false],
+ [true, true, 'on', true, true],
+
];
}
diff --git a/app/code/Magento/Shipping/Test/Unit/Model/ConfigTest.php b/app/code/Magento/Shipping/Test/Unit/Model/ConfigTest.php
new file mode 100644
index 0000000000000..e676b698c7f0e
--- /dev/null
+++ b/app/code/Magento/Shipping/Test/Unit/Model/ConfigTest.php
@@ -0,0 +1,152 @@
+ [
+ 'active' => '1',
+ 'name' => 'Fixed',
+ 'title' => 'Flat Rate',
+ ],
+ 'tablerate' => [
+ 'active' => '0',
+ 'name' => 'Table Rate',
+ 'title' => 'Best Way',
+ ]
+ ];
+
+ /**
+ * @var Config
+ */
+ private $model;
+
+ /**
+ * @var ScopeConfigInterface|MockObject
+ */
+ private $scopeConfigMock;
+
+ /**
+ * @var CarrierFactory|MockObject
+ */
+ private $carrierFactoryMock;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp(): void
+ {
+ $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
+ $this->carrierFactoryMock = $this->createMock(CarrierFactory::class);
+
+ $this->model = new Config($this->scopeConfigMock, $this->carrierFactoryMock, []);
+ }
+
+ /**
+ * Get active carriers when there is no active on the store
+ *
+ * @return void
+ */
+ public function testGetActiveCarriersWhenThereIsNoAvailable(): void
+ {
+ $this->scopeConfigMock->expects($this->once())
+ ->method('getValue')
+ ->with('carriers', ScopeInterface::SCOPE_STORE, null)
+ ->willReturn(null);
+
+ $this->assertEquals([], $this->model->getActiveCarriers());
+ }
+
+ /**
+ * Test for getActiveCarriers
+ *
+ * @return void
+ */
+ public function testGetActiveCarriers(): void
+ {
+ $this->scopeConfigMock->expects($this->once())
+ ->method('getValue')
+ ->with('carriers', ScopeInterface::SCOPE_STORE, self::STUB_STORE_CODE)
+ ->willReturn($this->shippingCarriersData);
+
+ $this->scopeConfigMock->expects($this->exactly(2))
+ ->method('isSetFlag')
+ ->withConsecutive(
+ ['carriers/flatrate/active', ScopeInterface::SCOPE_STORE, self::STUB_STORE_CODE],
+ ['carriers/tablerate/active', ScopeInterface::SCOPE_STORE, self::STUB_STORE_CODE],
+ )
+ ->willReturnOnConsecutiveCalls(
+ true,
+ false,
+ );
+
+ $this->carrierFactoryMock->expects($this->once())
+ ->method('create')
+ ->with('flatrate', self::STUB_STORE_CODE)
+ ->willReturn(true);
+
+ $this->assertEquals(['flatrate' => true], $this->model->getActiveCarriers(self::STUB_STORE_CODE));
+ }
+
+ /**
+ * Get all carriers when there is no carriers available on the store
+ *
+ * @return void
+ */
+ public function testGetAllCarriersWhenThereIsNoAvailable(): void
+ {
+ $this->scopeConfigMock->expects($this->once())
+ ->method('getValue')
+ ->with('carriers', ScopeInterface::SCOPE_STORE, null)
+ ->willReturn(null);
+
+ $this->assertEquals([], $this->model->getAllCarriers());
+ }
+
+ /**
+ * Test for getAllCarriers
+ *
+ * @return void
+ */
+ public function testGetAllCarriers(): void
+ {
+ $this->scopeConfigMock->expects($this->once())
+ ->method('getValue')
+ ->with('carriers', ScopeInterface::SCOPE_STORE, self::STUB_STORE_CODE)
+ ->willReturn($this->shippingCarriersData);
+
+ $this->carrierFactoryMock->expects($this->exactly(2))
+ ->method('create')
+ ->withConsecutive(
+ ['flatrate', self::STUB_STORE_CODE],
+ ['tablerate', self::STUB_STORE_CODE],
+ )
+ ->willReturnOnConsecutiveCalls(
+ true,
+ false,
+ );
+
+ $this->assertEquals(['flatrate' => true], $this->model->getAllCarriers(self::STUB_STORE_CODE));
+ }
+}
diff --git a/app/code/Magento/Store/Model/StoreResolver/GetStoresListByWebsiteIds.php b/app/code/Magento/Store/Model/StoreResolver/GetStoresListByWebsiteIds.php
new file mode 100644
index 0000000000000..416537caaf0e0
--- /dev/null
+++ b/app/code/Magento/Store/Model/StoreResolver/GetStoresListByWebsiteIds.php
@@ -0,0 +1,45 @@
+storeWebsiteRelation = $storeWebsiteRelation;
+ }
+
+ /**
+ * Retrieve list of stores by website ids
+ *
+ * @param array $websiteIds
+ * @return array
+ */
+ public function execute(array $websiteIds): array
+ {
+ $storeIdsArray = [];
+ foreach ($websiteIds as $websiteId) {
+ $storeIdsArray[] = $this->storeWebsiteRelation->getStoreByWebsiteId($websiteId);
+ }
+
+ return array_merge([], ...$storeIdsArray);
+ }
+}
diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml
index ab532538cc3f3..e05496efaa152 100644
--- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml
+++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontCustomerCanChangeProductOptionsUsingSwatchesTest.xml
@@ -22,8 +22,10 @@
+
+
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminGoToNewTaxRulePageActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminGoToNewTaxRulePageActionGroup.xml
new file mode 100644
index 0000000000000..ce16cfb73df67
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminGoToNewTaxRulePageActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Go to the create New Tax Rule page.
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSaveTaxRuleActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSaveTaxRuleActionGroup.xml
new file mode 100644
index 0000000000000..cf9f734b06f2d
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminSaveTaxRuleActionGroup.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ Clicks the Save Rule button on the Tax Rule page.
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml
index 84278468a0590..8bacc134c7ab8 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCheckingTaxReportGridTest.xml
@@ -154,8 +154,10 @@
-
-
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml
new file mode 100644
index 0000000000000..1765dbe1e7fcd
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Assert current page number on admin grid
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml
new file mode 100644
index 0000000000000..402bd077f9fbb
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ Assert total page count on admin grid
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml
new file mode 100644
index 0000000000000..7da082a279688
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Go to next page of the admin grid.
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml
index 51cebdb01a74d..eaea88fdddb03 100644
--- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml
+++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml
@@ -20,6 +20,7 @@
+
diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
index c5b000259e265..d2d39076bcfbb 100644
--- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
+++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
@@ -22,5 +22,6 @@
+
diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/website.js b/app/code/Magento/Ui/view/base/web/js/form/element/website.js
index 3c99cc0874cf9..0d1ed2d9961a1 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/element/website.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/element/website.js
@@ -26,10 +26,6 @@ define([
initialize: function () {
this._super();
- if (this.customerId || this.isGlobalScope) {
- this.disable(true);
- }
-
return this;
}
});
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
index 534d292809ed1..5f6c21cf6167f 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js
@@ -36,7 +36,8 @@ define([
imports: {
totalSelected: '${ $.selectProvider }:totalSelected',
totalRecords: '${ $.provider }:data.totalRecords',
- filters: '${ $.provider }:params.filters'
+ filters: '${ $.provider }:params.filters',
+ keywordUpdated: '${ $.provider }:params.keywordUpdated'
},
exports: {
@@ -58,7 +59,8 @@ define([
'pages': 'onPagesChange',
'pageSize': 'onPageSizeChange',
'totalRecords': 'updateCounter',
- '${ $.provider }:params.filters': 'goFirst'
+ '${ $.provider }:params.filters': 'goFirst',
+ '${ $.provider }:params.search': 'onSearchUpdate'
},
modules: {
@@ -282,6 +284,17 @@ define([
*/
onPagesChange: function () {
this.updateCursor();
+ },
+
+ /**
+ * Resent the pagination to Page 1 on search keyword update
+ */
+ onSearchUpdate: function () {
+ if (!_.isUndefined(this.keywordUpdated) && this.keywordUpdated) {
+ this.goFirst();
+ }
+
+ return this;
}
});
});
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js
index 3f5434761ba18..d4db0100db7c6 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js
@@ -22,6 +22,7 @@ define([
placeholder: $t('Search by keyword'),
label: $t('Keyword'),
value: '',
+ keywordUpdated: false,
previews: [],
chipsProvider: 'componentType = filtersChips, ns = ${ $.ns }',
statefull: {
@@ -31,7 +32,8 @@ define([
value: true,
previews: true,
inputValue: true,
- focused: true
+ focused: true,
+ keywordUpdated: true
},
imports: {
inputValue: 'value',
@@ -39,7 +41,8 @@ define([
focused: false
},
exports: {
- value: '${ $.provider }:params.search'
+ value: '${ $.provider }:params.search',
+ keywordUpdated: '${ $.provider }:params.keywordUpdated'
},
modules: {
chips: '${ $.chipsProvider }'
@@ -124,6 +127,7 @@ define([
apply: function (value) {
value = value || this.inputValue;
+ this.keywordUpdated = this.value !== value;
this.value = this.inputValue = value.trim();
return this;
diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFilterUrlRewriteGridByRequestPathAndStoreViewActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFilterUrlRewriteGridByRequestPathAndStoreViewActionGroup.xml
new file mode 100644
index 0000000000000..2e80fa0f798a6
--- /dev/null
+++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminFilterUrlRewriteGridByRequestPathAndStoreViewActionGroup.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ Goes to the Admin URL Rewrite grid page. Searches the grid based on the provided Redirect Path and StoreView name. Validates that the provided Redirect Path, Type and Target Path are present and correct in the grid.
+
+
+
+
+
+
+
diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml
index c14d0b175d2c0..ccd4297312f44 100644
--- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml
+++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml
@@ -109,10 +109,11 @@
-
+
+
diff --git a/app/code/Magento/User/etc/adminhtml/system.xml b/app/code/Magento/User/etc/adminhtml/system.xml
index 584b40a023c93..8b619c4f9cf48 100644
--- a/app/code/Magento/User/etc/adminhtml/system.xml
+++ b/app/code/Magento/User/etc/adminhtml/system.xml
@@ -14,6 +14,11 @@
Email template chosen based on theme fallback when "Default" option is selected.
Magento\Config\Model\Config\Source\Email\Template
+
+
+ Email template chosen based on theme fallback when "Default" option is selected.
+ Magento\Config\Model\Config\Source\Email\Template
+
diff --git a/app/code/Magento/User/view/adminhtml/email/new_user_notification.html b/app/code/Magento/User/view/adminhtml/email/new_user_notification.html
index 87f4e4669c4b6..0b6ceeb61cb71 100644
--- a/app/code/Magento/User/view/adminhtml/email/new_user_notification.html
+++ b/app/code/Magento/User/view/adminhtml/email/new_user_notification.html
@@ -4,12 +4,12 @@
* See COPYING.txt for license details.
*/
-->
-
+
+
+
+
+ 0
+ weee-attribute
+
+
diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml
index 0f4a7f9a55d26..ccbd431848dbc 100644
--- a/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml
+++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminFixedTaxValSavedForSpecificWebsiteTest.xml
@@ -22,7 +22,7 @@
-
+
diff --git a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml
index 0d7c21b6efffc..4e70c9ba87d64 100644
--- a/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml
+++ b/app/code/Magento/Weee/Test/Mftf/Test/AdminRemoveProductWeeeAttributeOptionTest.xml
@@ -18,7 +18,7 @@
-
+
diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml
index e78036458301b..833f619888bfb 100644
--- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml
+++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerPhysicalQuoteTest.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml
index dda125835110a..8e8667cb7e13d 100644
--- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml
+++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForCustomerVirtualQuoteTest.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml
index 74ba7c1f2bff3..3a3f9c7e8931a 100644
--- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml
+++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestPhysicalQuoteTest.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml
index 495b9a990a465..0d54991f84395 100644
--- a/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml
+++ b/app/code/Magento/Weee/Test/Mftf/Test/StorefrontFPTTaxInformationInShoppingCartForGuestVirtualQuoteTest.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php
index c48bf9e7e4c7a..a704a5676f632 100644
--- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php
+++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Instance/Edit/Tab/Main/Layout.php
@@ -315,25 +315,6 @@ public function getAddLayoutButtonHtml()
return $button->toHtml();
}
- /**
- * Retrieve remove layout button html
- *
- * @return string
- */
- public function getRemoveLayoutButtonHtml()
- {
- $button = $this->getLayout()->createBlock(
- \Magento\Backend\Block\Widget\Button::class
- )->setData(
- [
- 'label' => $this->escapeHtmlAttr(__('Remove Layout Update')),
- 'onclick' => 'WidgetInstance.removePageGroup(this)',
- 'class' => 'action-delete',
- ]
- );
- return $button->toHtml();
- }
-
/**
* Prepare and retrieve page groups data of widget instance
*
diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml
index e657b3eb73b53..5cf135f158130 100644
--- a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml
+++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetActionGroup.xml
@@ -23,6 +23,10 @@
+
+
+
+
diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetWthoutLayoutActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetWthoutLayoutActionGroup.xml
new file mode 100644
index 0000000000000..e9ee80c1a5f2a
--- /dev/null
+++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminCreateWidgetWthoutLayoutActionGroup.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Goes to the Admin Widget creation page without saving it
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminWidgetAddLayoutUpdateActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminWidgetAddLayoutUpdateActionGroup.xml
new file mode 100644
index 0000000000000..fa73fa4926e10
--- /dev/null
+++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminWidgetAddLayoutUpdateActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Add layouts during widgets creation
+
+
+
+
+
diff --git a/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminWidgetDeleteLayoutUpdateActionGroup.xml b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminWidgetDeleteLayoutUpdateActionGroup.xml
new file mode 100644
index 0000000000000..e52fb1a7f6514
--- /dev/null
+++ b/app/code/Magento/Widget/Test/Mftf/ActionGroup/AdminWidgetDeleteLayoutUpdateActionGroup.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ Delete layouts during widgets creation
+
+
+
+
diff --git a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml
index 8a17b589d7ab2..1ed77904d3a68 100644
--- a/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml
+++ b/app/code/Magento/Widget/Test/Mftf/Section/AdminNewWidgetSection.xml
@@ -17,6 +17,8 @@
+
+
@@ -52,6 +54,8 @@
+
+
diff --git a/app/code/Magento/Widget/Test/Mftf/Test/AdminWidgetAddAndDeleteMultipleLayoutSectionsTest.xml b/app/code/Magento/Widget/Test/Mftf/Test/AdminWidgetAddAndDeleteMultipleLayoutSectionsTest.xml
new file mode 100644
index 0000000000000..04c1552d53522
--- /dev/null
+++ b/app/code/Magento/Widget/Test/Mftf/Test/AdminWidgetAddAndDeleteMultipleLayoutSectionsTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml
index 93c5cac33f947..2d4f88709fd91 100644
--- a/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml
+++ b/app/code/Magento/Widget/view/adminhtml/templates/instance/edit/layout.phtml
@@ -38,7 +38,8 @@ var pageGroupTemplate = '][page_group]">Display on
*'+
'{$block->getDisplayOnSelectHtml()}'+
'
'+
- {$jsonHelper->jsonEncode($block->getRemoveLayoutButtonHtml())} +
+ ''+
'
'+
'
'+
''+
diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml
index aafd8b0b0d4d3..8fe3d3c707900 100644
--- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml
+++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontAddMultipleStoreProductsToWishlistTest.xml
@@ -36,6 +36,8 @@
+
+
@@ -49,7 +51,7 @@
-
+
diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml
index 689b76e42e6f1..96e5417f1cffd 100644
--- a/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml
+++ b/app/code/Magento/Wishlist/Test/Mftf/Test/WishListWithDisabledProductTest.xml
@@ -23,9 +23,11 @@
+
+
-
+
diff --git a/app/etc/di.xml b/app/etc/di.xml
index d4651499e6fce..b1d81ed70f6b4 100644
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -229,6 +229,17 @@
+
+
+ Magento\Framework\Communication\Config\Reader\XmlReader\Converter
+ Magento\Framework\Communication\Config\Reader\XmlReader\SchemaLocator
+ communication.xml
+
+ - name
+ - name
+
+
+
diff --git a/composer.lock b/composer.lock
index fa90484975728..b0b15f4e13f6c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -8439,21 +8439,21 @@
},
{
"name": "magento/magento-coding-standard",
- "version": "5",
+ "version": "6",
"source": {
"type": "git",
"url": "https://github.com/magento/magento-coding-standard.git",
- "reference": "da46c5d57a43c950dfa364edc7f1f0436d5353a5"
+ "reference": "efc9084db3d1bd145b92d6b8a2e9cb0faec54fa7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/da46c5d57a43c950dfa364edc7f1f0436d5353a5",
- "reference": "da46c5d57a43c950dfa364edc7f1f0436d5353a5",
+ "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/efc9084db3d1bd145b92d6b8a2e9cb0faec54fa7",
+ "reference": "efc9084db3d1bd145b92d6b8a2e9cb0faec54fa7",
"shasum": ""
},
"require": {
"php": ">=5.6.0",
- "squizlabs/php_codesniffer": "^3.4",
+ "squizlabs/php_codesniffer": "^3.5",
"webonyx/graphql-php": ">=0.12.6 <1.0"
},
"require-dev": {
@@ -8474,7 +8474,7 @@
"AFL-3.0"
],
"description": "A set of Magento specific PHP CodeSniffer rules.",
- "time": "2019-11-04T22:08:27+00:00"
+ "time": "2020-12-03T14:41:54+00:00"
},
{
"name": "magento/magento2-functional-testing-framework",
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php
index ba12a02cb5b1f..09987457e3c56 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php
@@ -235,8 +235,8 @@ public function testCreateWithNotDefaultStoreId()
$this->assertEquals($updatedImage['file'], $targetProduct->getData('image'));
// No values for default store view were provided
$this->assertNull($updatedImage['label_default']);
- $this->assertNull($updatedImage['position_default']);
- $this->assertNull($updatedImage['disabled_default']);
+ $this->assertEquals(1, $updatedImage['position_default']);
+ $this->assertEquals(0, $updatedImage['disabled_default']);
}
/**
@@ -483,7 +483,9 @@ public function testCreateThrowsExceptionIfProvidedImageHasWrongMimeType()
public function testCreateThrowsExceptionIfTargetProductDoesNotExist()
{
$this->expectException(\Exception::class);
- $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.');
+ $this->expectExceptionMessage(
+ 'The product that was requested doesn\'t exist. Verify the product and try again.'
+ );
$this->createServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media';
@@ -538,7 +540,9 @@ public function testCreateThrowsExceptionIfProvidedImageNameContainsForbiddenCha
public function testUpdateThrowsExceptionIfTargetProductDoesNotExist()
{
$this->expectException(\Exception::class);
- $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.');
+ $this->expectExceptionMessage(
+ 'The product that was requested doesn\'t exist. Verify the product and try again.'
+ );
$this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media'
. '/' . 'wrong-sku';
@@ -592,7 +596,9 @@ public function testUpdateThrowsExceptionIfThereIsNoImageWithGivenId()
public function testDeleteThrowsExceptionIfTargetProductDoesNotExist()
{
$this->expectException(\Exception::class);
- $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.');
+ $this->expectExceptionMessage(
+ 'The product that was requested doesn\'t exist. Verify the product and try again.'
+ );
$this->deleteServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media/9999';
$requestData = [
@@ -782,6 +788,6 @@ public function testAddProductVideo()
$this->assertEquals(1, $updatedImage['position']);
$this->assertEquals(0, $updatedImage['disabled']);
$this->assertStringStartsWith('/t/e/test_image', $updatedImage['file']);
- $this->assertEquals($videoContent, array_intersect($updatedImage, $videoContent));
+ $this->assertEquals($videoContent, array_intersect_key($updatedImage, $videoContent));
}
}
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
index 1b18949b0ac5b..d77737edadafa 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
@@ -25,6 +25,7 @@
use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
use Magento\Integration\Api\AdminTokenServiceInterface;
use Magento\Store\Api\Data\StoreInterface;
+use Magento\Store\Api\StoreWebsiteRelationInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreRepository;
use Magento\Store\Model\Website;
@@ -254,6 +255,59 @@ public function testUpdateWithDeleteWebsites()
);
}
+ /**
+ * Test removing association between product and website 1 then check url rewrite removed
+ * Assign website back and check rewrite generated
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/product_two_websites.php
+ */
+ public function testUpdateRewriteWithChangeWebsites()
+ {
+ /** @var Website $website */
+ $website = $this->loadWebsiteByCode('test');
+
+ $productBuilder[ProductInterface::SKU] = 'simple-on-two-websites';
+ $productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = [
+ 'website_ids' => [
+ $website->getId(),
+ ],
+ ];
+ $objectManager = Bootstrap::getObjectManager();
+ /** @var StoreWebsiteRelationInterface $storeWebsiteRelation */
+ $storeWebsiteRelation = $objectManager->get(StoreWebsiteRelationInterface::class);
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+ $baseWebsite = $this->loadWebsiteByCode('base');
+ $storeIds = $storeWebsiteRelation->getStoreByWebsiteId($baseWebsite->getId());
+ $product = $productRepository->get($productBuilder[ProductInterface::SKU], false, reset($storeIds));
+ $this->assertStringContainsString(
+ $product->getUrlKey() . '.html',
+ $product->getProductUrl()
+ );
+
+ $this->updateProduct($productBuilder);
+
+ $product->setRequestPath('');
+ $this->assertStringNotContainsString(
+ $product->getUrlKey() . '.html',
+ $product->getProductUrl()
+ );
+ $productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = [
+ 'website_ids' => [
+ $website->getId(),
+ $baseWebsite->getId(),
+ ],
+ ];
+
+ $this->updateProduct($productBuilder);
+ $product->setRequestPath('');
+ $this->assertStringContainsString(
+ $product->getUrlKey() . '.html',
+ $product->getProductUrl()
+ );
+ }
+
/**
* Test removing all website associations
*
@@ -264,7 +318,7 @@ public function testDeleteAllWebsiteAssociations()
$productBuilder[ProductInterface::SKU] = 'unique-simple-azaza';
$websitesData = [
- 'website_ids' => []
+ 'website_ids' => [],
];
$productBuilder[ProductInterface::EXTENSION_ATTRIBUTES_KEY] = $websitesData;
$response = $this->updateProduct($productBuilder);
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryAggregationsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryAggregationsTest.php
new file mode 100644
index 0000000000000..a0f184507a9aa
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/CategoriesQuery/CategoryAggregationsTest.php
@@ -0,0 +1,56 @@
+graphQlQuery($query);
+ $this->assertArrayNotHasKey('errors', $response);
+ $this->assertArrayHasKey('products', $response);
+ $this->assertArrayHasKey('aggregations', $response['products']);
+
+ $customAggregation = array_values(array_filter(
+ $response['products']['aggregations'],
+ function ($a) {
+ return in_array($a['attribute_code'], ['test_attribute_1', 'test_attribute_2']);
+ }
+ ));
+ $this->assertCount(2, $customAggregation);
+ $this->assertEquals('test_attribute_2', $customAggregation[0]['attribute_code']);
+ $this->assertEquals('test_attribute_1', $customAggregation[1]['attribute_code']);
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductPriceTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductPriceTest.php
index b6c4b55dc1d23..ceb52354fa6eb 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductPriceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductPriceTest.php
@@ -15,6 +15,8 @@
use Magento\ConfigurableProduct\Api\LinkManagementInterface;
use Magento\ConfigurableProduct\Model\LinkManagement;
use Magento\Customer\Model\Group;
+use Magento\Integration\Api\CustomerTokenServiceInterface;
+use Magento\GraphQl\Customer\LockCustomer;
use Magento\Framework\ObjectManager\ObjectManager;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -27,11 +29,23 @@ class ProductPriceTest extends GraphQlAbstract
/** @var ProductRepositoryInterface $productRepository */
private $productRepository;
+ /**
+ * @var CustomerTokenServiceInterface
+ */
+ private $customerTokenService;
+
+ /**
+ * @var LockCustomer
+ */
+ private $lockCustomer;
+
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
/** @var ProductRepositoryInterface $productRepository */
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class);
+ $this->lockCustomer = $this->objectManager->get(LockCustomer::class);
}
/**
@@ -235,10 +249,20 @@ public function testMultipleProductTypes()
* Simple products with special price and tier price with % discount
*
* @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ * @param int $customerGroup
+ * @param array $expectedPriceRange
+ * @param array $expectedTierPrices
+ * @param array $customerData
+ * @param bool $isTierPriceExists
+ * @dataProvider priceDataProvider
*/
- public function testSimpleProductsWithSpecialPriceAndTierPrice()
- {
+ public function testSimpleProductsWithSpecialPriceAndTierPrice(
+ int $customerGroup,
+ array $expectedPriceRange,
+ array $expectedTierPrices,
+ array $customerData
+ ) {
$skus = ["simple1", "simple2"];
$tierPriceFactory = $this->objectManager->get(ProductTierPriceInterfaceFactory::class);
@@ -249,7 +273,7 @@ public function testSimpleProductsWithSpecialPriceAndTierPrice()
$tierPrices[] = $tierPriceFactory->create(
[
'data' => [
- 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL,
+ 'customer_group_id' => $customerGroup,
'qty' => 2
]
]
@@ -260,97 +284,137 @@ public function testSimpleProductsWithSpecialPriceAndTierPrice()
$simpleProduct->setTierPrices($tierPrices);
$this->productRepository->save($simpleProduct);
}
+
+ $headerMap = [];
+ if (!empty($customerData)) {
+ $customerToken = $this->customerTokenService->createCustomerAccessToken(
+ $customerData['username'],
+ $customerData['password']
+ );
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ }
+
$query = $this->getProductQuery($skus);
- $result = $this->graphQlQuery($query);
+ $result = $this->graphQlQuery($query, [], '', $headerMap);
$this->assertArrayNotHasKey('errors', $result);
$this->assertCount(2, $result['products']['items']);
- $expectedPriceRange = [
- "simple1" => [
- "minimum_price" => [
- "regular_price" => [
- "value" => 10
- ],
- "final_price" => [
- "value" => 5.99
+ foreach ($result['products']['items'] as $product) {
+ $this->assertNotEmpty($product['price_range']);
+ $this->assertNotEmpty($product['price_tiers']);
+ $this->assertPrices($expectedPriceRange[$product['sku']], $product['price_range']);
+ $this->assertResponseFields($product['price_tiers'], $expectedTierPrices[$product['sku']]);
+ }
+ }
+
+ /**
+ * Data provider for prices
+ *
+ * @return array
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function priceDataProvider() : array
+ {
+ return [
+ [
+ 'customer_group' => Group::CUST_GROUP_ALL,
+ 'expected_price_range' => [
+ "simple1" => [
+ "minimum_price" => [
+ "regular_price" => ["value" => 10],
+ "final_price" => ["value" => 5.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 40.1]
+ ],
+ "maximum_price" => [
+ "regular_price" => ["value" => 10],
+ "final_price" => ["value" => 5.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 40.1]
+ ]
],
- "discount" => [
- "amount_off" => 4.01,
- "percent_off" => 40.1
+ "simple2" => [
+ "minimum_price" => [
+ "regular_price" => ["value" => 20],
+ "final_price" => ["value" => 15.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 20.05]
+ ],
+ "maximum_price" => [
+ "regular_price" => ["value" => 20],
+ "final_price" => ["value" => 15.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 20.05]
+ ]
]
],
- "maximum_price" => [
- "regular_price" => [
- "value" => 10
+ 'expected_tier_prices' => [
+ "simple1" => [
+ 0 => [
+ 'discount' =>['amount_off' => 1, 'percent_off' => 10],
+ 'final_price' =>['value'=> 9],
+ 'quantity' => 2
+ ]
],
- "final_price" => [
- "value" => 5.99
- ],
- "discount" => [
- "amount_off" => 4.01,
- "percent_off" => 40.1
+ "simple2" => [
+ 0 => [
+ 'discount' =>['amount_off' => 2, 'percent_off' => 10],
+ 'final_price' =>['value'=> 18],
+ 'quantity' => 2
+ ]
]
- ]
+ ],
+ 'customer_data' => []
],
- "simple2" => [
- "minimum_price" => [
- "regular_price" => [
- "value" => 20
- ],
- "final_price" => [
- "value" => 15.99
+ [
+ 'customer_group' => 1,
+ 'expected_price_range' => [
+ "simple1" => [
+ "minimum_price" => [
+ "regular_price" => ["value" => 10],
+ "final_price" => ["value" => 5.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 40.1]
+ ],
+ "maximum_price" => [
+ "regular_price" => ["value" => 10],
+ "final_price" => ["value" => 5.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 40.1]
+ ]
],
- "discount" => [
- "amount_off" => 4.01,
- "percent_off" => 20.05
+ "simple2" => [
+ "minimum_price" => [
+ "regular_price" => ["value" => 20],
+ "final_price" => ["value" => 15.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 20.05]
+ ],
+ "maximum_price" => [
+ "regular_price" => ["value" => 20],
+ "final_price" => ["value" => 15.99],
+ "discount" => ["amount_off" => 4.01, "percent_off" => 20.05]
+ ]
]
],
- "maximum_price" => [
- "regular_price" => [
- "value" => 20
+ 'expected_tier_prices' => [
+ "simple1" => [
+ 0 => [
+ 'discount' =>['amount_off' => 1, 'percent_off' => 10],
+ 'final_price' =>['value'=> 9],
+ 'quantity' => 2
+ ]
],
- "final_price" => [
- "value" => 15.99
- ],
- "discount" => [
- "amount_off" => 4.01,
- "percent_off" => 20.05
+ "simple2" => [
+ 0 => [
+ 'discount' =>['amount_off' => 2, 'percent_off' => 10],
+ 'final_price' =>['value'=> 18],
+ 'quantity' => 2
+ ]
]
- ]
- ]
- ];
- $expectedTierPrices = [
- "simple1" => [
- 0 => [
- 'discount' =>[
- 'amount_off' => 1,
- 'percent_off' => 10
- ],
- 'final_price' =>['value'=> 9],
- 'quantity' => 2
+ ],
+ 'customer_data' => [
+ 'username' => 'customer@example.com',
+ 'password' => 'password'
]
],
- "simple2" => [
- 0 => [
- 'discount' =>[
- 'amount_off' => 2,
- 'percent_off' => 10
- ],
- 'final_price' =>['value'=> 18],
- 'quantity' => 2
- ]
-
- ]
];
-
- foreach ($result['products']['items'] as $product) {
- $this->assertNotEmpty($product['price_range']);
- $this->assertNotEmpty($product['price_tiers']);
- $this->assertPrices($expectedPriceRange[$product['sku']], $product['price_range']);
- $this->assertResponseFields($product['price_tiers'], $expectedTierPrices[$product['sku']]);
- }
}
+
/**
* Check the pricing for a grouped product with simple products having special price set
*
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
index 1da0a7e08332b..6c64539e38cb2 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php
@@ -173,7 +173,6 @@ private function compareFilterNames(array $a, array $b)
*/
public function testLayeredNavigationForConfigurableProducts()
{
- CacheCleaner::cleanAll();
$attributeCode = 'test_configurable';
/** @var Config $eavConfig */
@@ -277,7 +276,7 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi
*/
public function testFilterProductsByDropDownCustomAttribute()
{
- CacheCleaner::cleanAll();
+ CacheCleaner::clean(['eav']);
$attributeCode = 'second_test_configurable';
$optionValue = $this->getDefaultAttributeOptionValue($attributeCode);
$query = <<setRedirectType('301');
$urlRewriteModel->setId($urlRewriteModel->getId());
$urlRewriteModel->save();
-
- ObjectManager::getInstance()->get(\Magento\TestFramework\Helper\CacheCleaner::class)->cleanAll();
//modifying query by adding spaces to avoid getting cached values.
$this->queryUrlAndAssertResponse(
(int) $product->getEntityId(),
- $customUrl,
+ $customUrl . ' ',
$actualUrls->getRequestPath(),
strtoupper($actualUrls->getEntityType()),
301
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php
index 72d35fdd51b96..db9a12e654a2c 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php
@@ -327,6 +327,45 @@ public function testPlaceOrderOfCustomerCart()
$this->graphQlMutation($query);
}
+ /**
+ * Test place order with gift message options
+ *
+ * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php
+ * @magentoConfigFixture default_store carriers/flatrate/active 1
+ * @magentoConfigFixture default_store carriers/tablerate/active 1
+ * @magentoConfigFixture default_store carriers/freeshipping/active 1
+ * @magentoConfigFixture default_store payment/banktransfer/active 1
+ * @magentoConfigFixture default_store payment/cashondelivery/active 1
+ * @magentoConfigFixture default_store payment/checkmo/active 1
+ * @magentoConfigFixture default_store payment/purchaseorder/active 1
+ * @magentoConfigFixture sales/gift_options/allow_order 1
+ * @magentoConfigFixture default_store customer/create_account/auto_group_assign 1
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_gift_options.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php
+ */
+ public function testPlaceOrderWithGiftMessage()
+ {
+ $reservedOrderId = 'test_quote';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+
+ $query = $this->getQuery($maskedQuoteId);
+ $response = $this->graphQlMutation($query);
+
+ self::assertArrayHasKey('placeOrder', $response);
+ self::assertArrayHasKey('order_number', $response['placeOrder']['order']);
+ self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']);
+ $orderIncrementId = $response['placeOrder']['order']['order_number'];
+ $order = $this->orderFactory->create();
+ $order->loadByIncrementId($orderIncrementId);
+ $this->assertNotEmpty($order->getGiftMessageId());
+ }
+
/**
* @param string $maskedQuoteId
* @return string
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php
index 7a14aca72d83f..e3c77a40a470a 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php
@@ -8,6 +8,9 @@
namespace Magento\GraphQl\Quote\Guest;
use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId;
+use Magento\Quote\Model\Quote\Address;
+use Magento\Quote\Model\QuoteFactory;
+use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -21,10 +24,22 @@ class SetGuestEmailOnCartTest extends GraphQlAbstract
*/
private $getMaskedQuoteIdByReservedOrderId;
+ /**
+ * @var QuoteFactory
+ */
+ private $quoteFactory;
+
+ /**
+ * @var QuoteResource
+ */
+ private $quoteResource;
+
protected function setUp(): void
{
$objectManager = Bootstrap::getObjectManager();
$this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class);
+ $this->quoteFactory = $objectManager->get(QuoteFactory::class);
+ $this->quoteResource = $objectManager->get(QuoteResource::class);
}
/**
@@ -43,6 +58,33 @@ public function testSetGuestEmailOnCart()
$this->assertEquals($email, $response['setGuestEmailOnCart']['cart']['email']);
}
+ /**
+ * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php
+ */
+ public function testSetGuestEmailOnCartWithDifferentEmailAddress()
+ {
+ $reservedOrderId = 'test_quote';
+ $secondEmail = 'attempt2@example.com';
+ $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
+
+ $email = 'attempt1@example.com';
+ $query = $this->getQuery($maskedQuoteId, $email);
+ $this->graphQlMutation($query);
+
+ $query = $this->getQuery($maskedQuoteId, $secondEmail);
+ $this->graphQlMutation($query);
+
+ $quote = $this->quoteFactory->create();
+ $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id');
+ $addresses = $quote->getAddressesCollection();
+ $this->assertEquals(2, $addresses->count());
+ foreach ($addresses as $address) {
+ if ($address->getAddressType() === Address::ADDRESS_TYPE_SHIPPING) {
+ $this->assertEquals($secondEmail, $address->getEmail());
+ }
+ }
+ }
+
/**
* _security
* @magentoApiDataFixture Magento/Customer/_files/customer.php
diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php
index 11e07d081636e..efa7341c36a40 100644
--- a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php
@@ -7,28 +7,44 @@
namespace Magento\GroupedProduct\Api;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\Indexer\Model\Config;
+use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Framework\Webapi\Rest\Request;
class ProductLinkRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstract
{
const SERVICE_NAME = 'catalogProductLinkRepositoryV1';
const SERVICE_VERSION = 'V1';
const RESOURCE_PATH = '/V1/products/';
+ const SERVICE_NAME_SEARCH = 'searchV1';
+ const RESOURCE_PATH_SEARCH = '/V1/search/';
/**
* @var \Magento\Framework\ObjectManagerInterface
*/
protected $objectManager;
+ /**
+ * @var array
+ */
+ private $indexersState;
+
+ /**
+ * @var mixed
+ */
+ private $indexerRegistry;
+
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
+ $this->indexerRegistry = $this->objectManager->get(IndexerRegistry::class);
}
/**
* @magentoApiDataFixture Magento/Catalog/_files/product_simple_duplicated.php
* @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped.php
*/
- public function testSave()
+ public function testSave(): void
{
$productSku = 'grouped-product';
$linkType = 'associated';
@@ -46,7 +62,7 @@ public function testSave()
$serviceInfo = [
'rest' => [
'resourcePath' => self::RESOURCE_PATH . $productSku . '/links',
- 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
+ 'httpMethod' => Request::HTTP_METHOD_PUT,
],
'soap' => [
'service' => self::SERVICE_NAME,
@@ -64,4 +80,106 @@ public function testSave()
});
$this->assertEquals($productData, $actual[2]);
}
+
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped.php
+ */
+ public function testLinkWithScheduledIndex(): void
+ {
+ $this->setIndexScheduled();
+ $productSkuGrouped = 'grouped-product';
+ $productSimple = 'simple-1';
+ $linkType = 'associated';
+ $productData = [
+ 'sku' => $productSkuGrouped,
+ 'link_type' => $linkType,
+ 'linked_product_type' => 'simple',
+ 'linked_product_sku' => $productSimple,
+ 'position' => 3,
+ 'extension_attributes' => [
+ 'qty' => (float) 300.0000,
+ ],
+ ];
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH . $productSkuGrouped . '/links',
+ 'httpMethod' => Request::HTTP_METHOD_PUT,
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME . 'Save',
+ ],
+ ];
+ $this->_webApiCall($serviceInfo, ['entity' => $productData]);
+
+ $searchCriteria = $this->buildSearchCriteria($productSimple);
+ $serviceInfo = $this->buildSearchServiceInfo($searchCriteria);
+ $response = $this->_webApiCall($serviceInfo, $searchCriteria);
+ $this->assertArrayHasKey('search_criteria', $response);
+ $this->assertArrayHasKey('items', $response);
+ $this->assertGreaterThan(1, count($response['items']));
+ $this->assertGreaterThan(0, $response['items'][0]['id']);
+ $this->restoreIndexMode();
+ }
+
+ /**
+ * @param string $productSku
+ * @return array
+ */
+ private function buildSearchCriteria(string $productSku): array
+ {
+ return [
+ 'searchCriteria' => [
+ 'request_name' => 'quick_search_container',
+ 'filter_groups' => [
+ [
+ 'filters' => [
+ [
+ 'field' => 'search_term',
+ 'value' => $productSku,
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @param array $searchCriteria
+ * @return array
+ */
+ private function buildSearchServiceInfo(array $searchCriteria): array
+ {
+ return [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH_SEARCH . '?' . http_build_query($searchCriteria),
+ 'httpMethod' => Request::HTTP_METHOD_GET
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME_SEARCH,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME_SEARCH . 'Search'
+ ]
+ ];
+ }
+
+ private function setIndexScheduled(): void
+ {
+ $indexerListIds = $this->objectManager->get(Config::class)->getIndexers();
+ foreach ($indexerListIds as $indexerId) {
+ $indexer = $this->indexerRegistry->get($indexerId['indexer_id']);
+ $this->indexersState[$indexerId['indexer_id']] = $indexer->isScheduled();
+ $indexer->setScheduled(true);
+ }
+ }
+
+ private function restoreIndexMode(): void
+ {
+ foreach ($this->indexersState as $indexerId => $state) {
+ $this->indexerRegistry->get($indexerId)->setScheduled($state);
+ }
+ }
}
diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php
index 7900ae45e2f3d..bb2a4e68212cf 100644
--- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php
@@ -7,6 +7,11 @@
namespace Magento\Quote\Api;
+use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
+use Magento\Framework\Webapi\Rest\Request;
+use Magento\Integration\Api\CustomerTokenServiceInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\ObjectManager;
use Magento\TestFramework\TestCase\WebapiAbstract;
/**
@@ -15,13 +20,22 @@
class CartAddingItemsTest extends WebapiAbstract
{
/**
- * @var \Magento\TestFramework\ObjectManager
+ * @var ObjectManager
*/
protected $objectManager;
+ /**
+ * @var ProductResource
+ */
+ private $productResource;
+
+ /**
+ * @inheritDoc
+ */
protected function setUp(): void
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->productResource = $this->objectManager->get(ProductResource::class);
}
/**
@@ -36,9 +50,9 @@ public function testPriceForCreatingQuoteFromEmptyCart()
$this->_markTestAsRestOnly();
// Get customer ID token
- /** @var \Magento\Integration\Api\CustomerTokenServiceInterface $customerTokenService */
+ /** @var CustomerTokenServiceInterface $customerTokenService */
$customerTokenService = $this->objectManager->create(
- \Magento\Integration\Api\CustomerTokenServiceInterface::class
+ CustomerTokenServiceInterface::class
);
$token = $customerTokenService->createCustomerAccessToken(
'customer_one_address@test.com',
@@ -49,7 +63,7 @@ public function testPriceForCreatingQuoteFromEmptyCart()
$serviceInfo = [
'rest' => [
'resourcePath' => '/V1/carts/mine',
- 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
+ 'httpMethod' => Request::HTTP_METHOD_POST,
'token' => $token
]
];
@@ -58,13 +72,6 @@ public function testPriceForCreatingQuoteFromEmptyCart()
$this->assertGreaterThan(0, $quoteId);
// Adding item to the cart
- $serviceInfoForAddingProduct = [
- 'rest' => [
- 'resourcePath' => '/V1/carts/mine/items',
- 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
- 'token' => $token
- ]
- ];
$requestData = [
'cartItem' => [
'quote_id' => $quoteId,
@@ -72,7 +79,7 @@ public function testPriceForCreatingQuoteFromEmptyCart()
'qty' => 1
]
];
- $item = $this->_webApiCall($serviceInfoForAddingProduct, $requestData);
+ $item = $this->_webApiCall($this->getServiceInfoAddToCart($token), $requestData);
$this->assertNotEmpty($item);
$this->assertEquals(10, $item['price']);
@@ -80,7 +87,7 @@ public function testPriceForCreatingQuoteFromEmptyCart()
$serviceInfoForGettingPaymentInfo = [
'rest' => [
'resourcePath' => '/V1/carts/mine/payment-information',
- 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+ 'httpMethod' => Request::HTTP_METHOD_GET,
'token' => $token
]
];
@@ -92,4 +99,137 @@ public function testPriceForCreatingQuoteFromEmptyCart()
$quote->load($quoteId);
$quote->delete();
}
+
+ /**
+ * Test qty for cart after adding grouped product with custom qty.
+ *
+ * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped_with_simple.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_one_address.php
+ * @return void
+ */
+ public function testAddToCartGroupedCustomQuantity(): void
+ {
+ $this->_markTestAsRestOnly();
+
+ $firstProductId = $this->productResource->getIdBySku('simple_11');
+ $secondProductId = $this->productResource->getIdBySku('simple_22');
+ $qtyData = [$firstProductId => 2, $secondProductId => 4];
+
+ // Get customer ID token
+ /** @var CustomerTokenServiceInterface $customerTokenService */
+ $customerTokenService = $this->objectManager->create(CustomerTokenServiceInterface::class);
+ $token = $customerTokenService->createCustomerAccessToken(
+ 'customer_one_address@test.com',
+ 'password'
+ );
+
+ // Creating empty cart for registered customer.
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => '/V1/carts/mine',
+ 'httpMethod' => Request::HTTP_METHOD_POST,
+ 'token' => $token
+ ]
+ ];
+
+ $quoteId = $this->_webApiCall($serviceInfo, ['customerId' => 999]); // customerId 999 will get overridden
+ $this->assertGreaterThan(0, $quoteId);
+
+ // Adding item to the cart
+ $productOptionData = [
+ 'extension_attributes' => [
+ 'grouped_options' => [
+ ['id' => $firstProductId, 'qty' => $qtyData[$firstProductId]],
+ ['id' => $secondProductId, 'qty' => $qtyData[$secondProductId]],
+ ]
+ ]
+ ];
+ $requestData = [
+ 'cartItem' => [
+ 'quote_id' => $quoteId,
+ 'sku' => 'grouped',
+ 'qty' => 1,
+ 'product_option' => $productOptionData
+ ]
+ ];
+ $response = $this->_webApiCall($this->getServiceInfoAddToCart($token), $requestData);
+ $this->assertArrayHasKey('product_option', $response);
+ $this->assertEquals($response['product_option'], $productOptionData);
+
+ /** @var CartRepositoryInterface $cartRepository */
+ $cartRepository = $this->objectManager->get(CartRepositoryInterface::class);
+ $quote = $cartRepository->get($quoteId);
+
+ foreach ($quote->getAllItems() as $item) {
+ $this->assertEquals($qtyData[$item->getProductId()], $item->getQty());
+ }
+ }
+
+ /**
+ * Test adding grouped product when qty for grouped_options not specified.
+ *
+ * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped_with_simple.php
+ * @magentoApiDataFixture Magento/Customer/_files/customer_one_address.php
+ * @return void
+ */
+ public function testAddToCartGroupedCustomQuantityNotAllParamsSpecified(): void
+ {
+ $this->_markTestAsRestOnly();
+
+ $productId = $this->productResource->getIdBySku('simple_11');
+
+ // Get customer ID token
+ /** @var CustomerTokenServiceInterface $customerTokenService */
+ $customerTokenService = $this->objectManager->create(CustomerTokenServiceInterface::class);
+ $token = $customerTokenService->createCustomerAccessToken(
+ 'customer_one_address@test.com',
+ 'password'
+ );
+
+ // Creating empty cart for registered customer.
+ $serviceInfo = [
+ 'rest' => ['resourcePath' => '/V1/carts/mine', 'httpMethod' => Request::HTTP_METHOD_POST, 'token' => $token]
+ ];
+
+ $quoteId = $this->_webApiCall($serviceInfo, ['customerId' => 999]); // customerId 999 will get overridden
+ $this->assertGreaterThan(0, $quoteId);
+
+ // Adding item to the cart
+ $requestData = [
+ 'cartItem' => [
+ 'quote_id' => $quoteId,
+ 'sku' => 'grouped',
+ 'qty' => 1,
+ 'product_option' => [
+ 'extension_attributes' => [
+ 'grouped_options' => [
+ ['id' => $productId],
+ ]
+ ]
+ ]
+ ]
+ ];
+
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('Please specify id and qty for grouped options.');
+
+ $this->_webApiCall($this->getServiceInfoAddToCart($token), $requestData);
+ }
+
+ /**
+ * Returns service info add to cart
+ *
+ * @param string $token
+ * @return array
+ */
+ private function getServiceInfoAddToCart(string $token): array
+ {
+ return [
+ 'rest' => [
+ 'resourcePath' => '/V1/carts/mine/items',
+ 'httpMethod' => Request::HTTP_METHOD_POST,
+ 'token' => $token
+ ]
+ ];
+ }
}
diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php
index 373ad64ba39d4..1054706819e95 100644
--- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php
@@ -5,8 +5,13 @@
*/
namespace Magento\Quote\Api;
+use Magento\Catalog\Model\Product;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\CatalogInventory\Model\Stock;
+use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\QuoteIdMask;
+use Magento\Quote\Model\QuoteIdMaskFactory;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\ObjectManager;
use Magento\TestFramework\TestCase\WebapiAbstract;
@@ -40,14 +45,14 @@ protected function setUp(): void
*/
public function testGetList()
{
- /** @var \Magento\Quote\Model\Quote $quote */
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
$cartId = $quote->getId();
- /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
- $quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
- ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ /** @var QuoteIdMask $quoteIdMask */
+ $quoteIdMask = Bootstrap::getObjectManager()
+ ->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
@@ -92,17 +97,17 @@ public function testGetList()
*/
public function testAddItem()
{
- /** @var \Magento\Catalog\Model\Product $product */
- $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class)->load(2);
+ /** @var Product $product */
+ $product = $this->objectManager->create(Product::class)->load(2);
$productSku = $product->getSku();
- /** @var \Magento\Quote\Model\Quote $quote */
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
$quote->load('test_order_1', 'reserved_order_id');
$cartId = $quote->getId();
- /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
- $quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
- ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ /** @var QuoteIdMask $quoteIdMask */
+ $quoteIdMask = Bootstrap::getObjectManager()
+ ->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
@@ -141,20 +146,20 @@ public function testAddItem()
*/
public function testRemoveItem()
{
- /** @var \Magento\Quote\Model\Quote $quote */
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
$cartId = $quote->getId();
- /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
- $quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
- ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ /** @var QuoteIdMask $quoteIdMask */
+ $quoteIdMask = Bootstrap::getObjectManager()
+ ->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
$cartId = $quoteIdMask->getMaskedId();
- $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class);
+ $product = $this->objectManager->create(Product::class);
$productId = $product->getIdBySku('simple_one');
$product->load($productId);
$itemId = $quote->getItemByProduct($product)->getId();
@@ -175,7 +180,7 @@ public function testRemoveItem()
"itemId" => $itemId,
];
$this->assertTrue($this->_webApiCall($serviceInfo, $requestData));
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ $quote = $this->objectManager->create(Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
$this->assertFalse($quote->hasProductId($productId));
}
@@ -189,20 +194,20 @@ public function testRemoveItem()
public function testUpdateItem(array $stockData, string $errorMessage = null)
{
$this->updateStockData('simple_one', $stockData);
- /** @var \Magento\Quote\Model\Quote $quote */
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
$cartId = $quote->getId();
- /** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
- $quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
- ->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
+ /** @var QuoteIdMask $quoteIdMask */
+ $quoteIdMask = Bootstrap::getObjectManager()
+ ->create(QuoteIdMaskFactory::class)
->create();
$quoteIdMask->load($cartId, 'quote_id');
//Use masked cart Id
$cartId = $quoteIdMask->getMaskedId();
- $product = $this->objectManager->create(\Magento\Catalog\Model\Product::class);
+ $product = $this->objectManager->create(Product::class);
$productId = $product->getIdBySku('simple_one');
$product->load($productId);
$itemId = $quote->getItemByProduct($product)->getId();
@@ -229,7 +234,7 @@ public function testUpdateItem(array $stockData, string $errorMessage = null)
$this->expectExceptionMessage($errorMessage);
}
$this->_webApiCall($serviceInfo, $requestData);
- $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class);
+ $quote = $this->objectManager->create(Quote::class);
$quote->load('test_order_item_with_items', 'reserved_order_id');
$this->assertTrue($quote->hasProductId(1));
$item = $quote->getItemByProduct($product);
@@ -237,6 +242,62 @@ public function testUpdateItem(array $stockData, string $errorMessage = null)
$this->assertEquals($itemId, $item->getItemId());
}
+ /**
+ * Verifies that store id for quote and quote item is being changed accordingly to the requested store code
+ *
+ * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php
+ * @magentoApiDataFixture Magento/Store/_files/second_store.php
+ */
+ public function testUpdateItemWithChangingStoreId()
+ {
+ /** @var Quote $quote */
+ $quote = $this->objectManager->create(Quote::class);
+ $quote->load('test_order_item_with_items', 'reserved_order_id');
+ $cartId = $quote->getId();
+
+ /** @var QuoteIdMask $quoteIdMask */
+ $quoteIdMask = Bootstrap::getObjectManager()
+ ->create(QuoteIdMaskFactory::class)
+ ->create();
+ $quoteIdMask->load($cartId, 'quote_id');
+ $cartId = $quoteIdMask->getMaskedId();
+
+ $product = $this->objectManager->create(Product::class);
+ $productId = $product->getIdBySku('simple');
+ $product->load($productId);
+ $itemId = $quote->getItemByProduct($product)->getId();
+ $serviceInfo = [
+ 'rest' => [
+ 'resourcePath' => self::RESOURCE_PATH . $cartId . '/items/' . $itemId,
+ 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
+ ],
+ 'soap' => [
+ 'service' => self::SERVICE_NAME,
+ 'serviceVersion' => self::SERVICE_VERSION,
+ 'operation' => self::SERVICE_NAME . 'Save',
+ ],
+ ];
+
+ $requestData['cartItem']['qty'] = 5;
+ if (TESTS_WEB_API_ADAPTER === self::ADAPTER_SOAP) {
+ $requestData['cartItem'] += [
+ 'quote_id' => $cartId,
+ 'itemId' => $itemId,
+ ];
+ }
+ $this->_webApiCall($serviceInfo, $requestData, null, 'fixture_second_store');
+ $quote = $this->objectManager->create(Quote::class);
+ $quote->load('test_order_item_with_items', 'reserved_order_id');
+ $this->assertTrue($quote->hasProductId(1));
+ $item = $quote->getItemByProduct($product);
+ /** @var StoreManagerInterface $storeManager */
+ $storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $storeId = $storeManager->getStore('fixture_second_store')
+ ->getId();
+ $this->assertEquals($storeId, $quote->getStoreId());
+ $this->assertEquals($storeId, $item->getStoreId());
+ }
+
/**
* @return array
*/
diff --git a/dev/tests/integration/_files/Magento/TestModuleMview/etc/module.xml b/dev/tests/integration/_files/Magento/TestModuleMview/etc/module.xml
new file mode 100644
index 0000000000000..9808d90ace49c
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMview/etc/module.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMview/etc/mview.xml b/dev/tests/integration/_files/Magento/TestModuleMview/etc/mview.xml
new file mode 100644
index 0000000000000..1cabda7a626bf
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMview/etc/mview.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/_files/Magento/TestModuleMview/registration.php b/dev/tests/integration/_files/Magento/TestModuleMview/registration.php
new file mode 100644
index 0000000000000..5c5453c1bd413
--- /dev/null
+++ b/dev/tests/integration/_files/Magento/TestModuleMview/registration.php
@@ -0,0 +1,12 @@
+getPath(ComponentRegistrar::MODULE, 'Magento_TestModuleMview') === null) {
+ ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_TestModuleMview', __DIR__);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Backend/App/Area/FrontNameResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/App/Area/FrontNameResolverTest.php
new file mode 100644
index 0000000000000..979e8db19efb9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Backend/App/Area/FrontNameResolverTest.php
@@ -0,0 +1,49 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->create(
+ FrontNameResolver::class
+ );
+ $_SERVER['HTTP_HOST'] = 'localhost';
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoConfigFixture current_store web/unsecure/base_url http://example.com/
+ */
+ public function testIsHostBackend()
+ {
+ $this->assertTrue($this->model->isHostBackend());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php
index 7e94484961f9e..56a07034bd490 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php
@@ -6,15 +6,20 @@
namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery;
+use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery;
use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Gallery\UpdateHandler;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Framework\Registry;
+use Magento\Store\Api\StoreRepositoryInterface;
+use Magento\Store\Model\Store;
use Magento\TestFramework\Helper\Bootstrap;
/**
* @magentoAppArea adminhtml
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ContentTest extends \PHPUnit\Framework\TestCase
{
@@ -35,6 +40,16 @@ class ContentTest extends \PHPUnit\Framework\TestCase
*/
private $dataPersistor;
+ /**
+ * @var StoreRepositoryInterface
+ */
+ private $storeRepository;
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
+
/**
* @inheritdoc
*/
@@ -51,6 +66,8 @@ protected function setUp(): void
$this->block->setElement($gallery);
$this->registry = Bootstrap::getObjectManager()->get(Registry::class);
$this->dataPersistor = Bootstrap::getObjectManager()->get(DataPersistorInterface::class);
+ $this->storeRepository = Bootstrap::getObjectManager()->create(StoreRepositoryInterface::class);
+ $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
}
public function testGetUploader()
@@ -120,6 +137,119 @@ public function getImagesAndImageTypesDataProvider()
];
}
+ /**
+ * Tests images positions in store view
+ *
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+ * @magentoDataFixture Magento/Store/_files/second_store.php
+ * @dataProvider imagesPositionStoreViewDataProvider
+ * @param string $addFromStore
+ * @param array $newImages
+ * @param string $viewFromStore
+ * @param array $expectedImages
+ * @return void
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function testImagesPositionStoreView(
+ string $addFromStore,
+ array $newImages,
+ string $viewFromStore,
+ array $expectedImages
+ ): void {
+ $storeId = (int)$this->storeRepository->get($addFromStore)->getId();
+ $product = $this->getProduct($storeId);
+ $images = $product->getData('media_gallery')['images'];
+ $images = array_merge($images, $newImages);
+ $product->setData('media_gallery', ['images' => $images]);
+ $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class);
+ $updateHandler->execute($product);
+ $storeId = (int)$this->storeRepository->get($viewFromStore)->getId();
+ $product = $this->getProduct($storeId);
+ $this->registry->register('current_product', $product);
+ $actualImages = array_map(
+ function ($item) {
+ return [
+ 'file' => $item['file'],
+ 'label' => $item['label'],
+ 'position' => $item['position'],
+ ];
+ },
+ json_decode($this->block->getImagesJson(), true)
+ );
+ $this->assertEquals($expectedImages, array_values($actualImages));
+ }
+
+ /**
+ * @return array[]
+ */
+ public function imagesPositionStoreViewDataProvider(): array
+ {
+ return [
+ [
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'position' => 2,
+ 'label' => 'New Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image'
+ ]
+ ],
+ 'default',
+ [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'label' => 'Image Alt Text',
+ 'position' => 1,
+ ],
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'label' => null,
+ 'position' => 2,
+ ],
+ ]
+ ],
+ [
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'position' => 2,
+ 'label' => 'New Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image'
+ ]
+ ],
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'label' => 'Image Alt Text',
+ 'position' => 1,
+ ],
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'label' => 'New Image Alt Text',
+ 'position' => 2,
+ ],
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Returns product for testing.
+ *
+ * @param int $storeId
+ * @param string $sku
+ * @return ProductInterface
+ */
+ private function getProduct(int $storeId = Store::DEFAULT_STORE_ID, string $sku = 'simple'): ProductInterface
+ {
+ return $this->productRepository->get($sku, false, $storeId, true);
+ }
+
/**
* Prepare product, and set it to registry and data persistor.
*
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php
index b57969280cdf3..bcec3168c7885 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php
@@ -9,6 +9,7 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product\Gallery\UpdateHandler;
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\View\LayoutInterface;
@@ -392,6 +393,107 @@ public function galleryImagesOnStoreViewDataProvider(): array
];
}
+ /**
+ * Tests images positions in store view
+ *
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+ * @magentoDataFixture Magento/Store/_files/second_store.php
+ * @magentoConfigFixture default/web/url/catalog_media_url_format image_optimization_parameters
+ * @dataProvider imagesPositionStoreViewDataProvider
+ * @param string $addFromStore
+ * @param array $newImages
+ * @param string $viewFromStore
+ * @param array $expectedImages
+ * @return void
+ */
+ public function testImagesPositionStoreView(
+ string $addFromStore,
+ array $newImages,
+ string $viewFromStore,
+ array $expectedImages
+ ): void {
+ $storeId = (int)$this->storeRepository->get($addFromStore)->getId();
+ $product = $this->getProduct($storeId);
+ $images = $product->getData('media_gallery')['images'];
+ $images = array_merge($images, $newImages);
+ $product->setData('media_gallery', ['images' => $images]);
+ $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class);
+ $updateHandler->execute($product);
+ $storeId = (int)$this->storeRepository->get($viewFromStore)->getId();
+ $product = $this->getProduct($storeId);
+ $this->block->setData('product', $product);
+ $actualImages = array_map(
+ function ($item) {
+ return [
+ 'img' => parse_url($item['img'], PHP_URL_PATH),
+ 'caption' => $item['caption'],
+ 'position' => $item['position'],
+ ];
+ },
+ $this->serializer->unserialize($this->block->getGalleryImagesJson())
+ );
+ $this->assertEquals($expectedImages, array_values($actualImages));
+ }
+
+ /**
+ * @return array[]
+ */
+ public function imagesPositionStoreViewDataProvider(): array
+ {
+ return [
+ [
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'position' => 2,
+ 'label' => 'New Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image'
+ ]
+ ],
+ 'default',
+ [
+ [
+ 'img' => '/media/catalog/product/m/a/magento_image.jpg',
+ 'caption' => 'Image Alt Text',
+ 'position' => 1,
+ ],
+ [
+ 'img' => '/media/catalog/product/m/a/magento_small_image.jpg',
+ 'caption' => 'Simple Product',
+ 'position' => 2,
+ ],
+ ]
+ ],
+ [
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'position' => 2,
+ 'label' => 'New Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image'
+ ]
+ ],
+ 'fixture_second_store',
+ [
+ [
+ 'img' => '/media/catalog/product/m/a/magento_image.jpg',
+ 'caption' => 'Image Alt Text',
+ 'position' => 1,
+ ],
+ [
+ 'img' => '/media/catalog/product/m/a/magento_small_image.jpg',
+ 'caption' => 'New Image Alt Text',
+ 'position' => 2,
+ ],
+ ]
+ ]
+ ];
+ }
+
/**
* Updates product gallery images and saves product.
*
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php
new file mode 100644
index 0000000000000..91a54d8fc13fa
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php
@@ -0,0 +1,324 @@
+productRepository = $objectManager->get(ProductRepositoryInterface::class);
+ $this->productHelper = $objectManager->get(ProductHelper::class);
+ $this->dataObjectFactory = $objectManager->get(DataObjectFactory::class);
+ $layout = $objectManager->get(LayoutInterface::class);
+ $this->localeResolver = $objectManager->get(ResolverInterface::class);
+ $this->defaultLocale = $this->localeResolver->getLocale();
+ $this->block = $layout->createBlock(
+ Date::class,
+ 'product.info.options.date',
+ [
+ 'data' => [
+ 'template' => 'Magento_Catalog::product/view/options/type/date.phtml'
+ ]
+ ]
+ );
+ $layout->createBlock(
+ Render::class,
+ 'product.price.render.default',
+ [
+ 'data' => [
+ 'price_render_handle' => 'catalog_product_prices',
+ 'use_link_for_as_low_as' => true,
+ ],
+ ]
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function tearDown(): void
+ {
+ $this->localeResolver->setLocale($this->defaultLocale);
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @param array $data
+ * @param array $expected
+ * @dataProvider toHtmlWithDropDownDataProvider
+ */
+ public function testToHtmlWithDropDown(array $data, array $expected): void
+ {
+ $this->prepareBlock($data);
+ $this->assertXPaths($expected);
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @magentoConfigFixture current_store catalog/custom_options/use_calendar 1
+ * @param array $data
+ * @param array $expected
+ * @param string|null $locale
+ * @dataProvider toHtmlWithCalendarDataProvider
+ */
+ public function testToHtmlWithCalendar(array $data, array $expected, ?string $locale = null): void
+ {
+ if ($locale) {
+ $this->localeResolver->setLocale($locale);
+ }
+ $this->prepareBlock($data);
+ $this->assertXPaths($expected);
+ }
+
+ /**
+ * @param array $expected
+ */
+ private function assertXPaths(array $expected): void
+ {
+ $html = $this->block->toHtml();
+ $domXpath = new \DOMXPath($this->getHtmlDocument($html));
+ foreach ($expected as $xpath => $value) {
+ $xpath = strtr($xpath, ['{id}' => $this->block->getOption()->getId()]);
+ $nodes = $domXpath->query(strtr($xpath, ['{id}' => $this->block->getOption()->getId()]));
+ $this->assertEquals(1, $nodes->count(), 'Cannot find element \'' . $xpath . '"\' in the HTML');
+ $this->assertEquals($value, $nodes->item(0)->getAttribute('value'));
+ }
+ }
+
+ /**
+ * @param array $data
+ */
+ private function prepareBlock(array $data): void
+ {
+ /** @var Product $product */
+ $product = $this->productRepository->get('simple');
+ $this->block->setProduct($product);
+ $option = $this->getDateTimeOption($product);
+ $this->block->setOption($option);
+ $buyRequest = $this->dataObjectFactory->create();
+ $buyRequest->setData(
+ [
+ 'qty' => 1,
+ 'options' => [
+ $option->getId() => $data
+ ],
+ ]
+ );
+ $this->productHelper->prepareProductOptions($product, $buyRequest);
+ }
+
+ /**
+ * @param Product $product
+ * @return Option|null
+ */
+ private function getDateTimeOption(Product $product): ?Option
+ {
+ $option = null;
+ foreach ($product->getOptions() as $customOption) {
+ if ($customOption->getType() === Option::OPTION_TYPE_DATE_TIME) {
+ $option = $customOption;
+ break;
+ }
+ }
+ return $option;
+ }
+
+ /**
+ * @param string $source
+ * @return \DOMDocument
+ */
+ private function getHtmlDocument(string $source): \DOMDocument
+ {
+ $page =<<
+
+
+
+ Title
+
+
+$source
+
+
+HTML;
+ $domDocument = new \DOMDocument('1.0', 'UTF-8');
+ libxml_use_internal_errors(true);
+ $domDocument->loadHTML($page);
+ libxml_use_internal_errors(false);
+ return $domDocument;
+ }
+
+ /**
+ * @return array
+ */
+ public function toHtmlWithDropDownDataProvider(): array
+ {
+ return [
+ [
+ [
+ 'month' => '3',
+ 'day' => '5',
+ 'year' => '2020',
+ 'hour' => '2',
+ 'minute' => '15',
+ 'day_part' => 'am',
+ 'date_internal' => '2020-09-30 02:15:00'
+ ],
+ [
+ '//select[@id="options_{id}_year"]/option[@selected]' => '2020',
+ '//select[@id="options_{id}_month"]/option[@selected]' => '3',
+ '//select[@id="options_{id}_day"]/option[@selected]' => '5',
+ '//select[@id="options_{id}_hour"]/option[@selected]' => '2',
+ '//select[@id="options_{id}_minute"]/option[@selected]' => '15',
+ '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am',
+ ]
+ ],
+ [
+ [
+ 'date' => '09/30/2022',
+ 'hour' => '2',
+ 'minute' => '15',
+ 'day_part' => 'am',
+ 'date_internal' => '2020-09-30 02:15:00'
+ ],
+ [
+ '//select[@id="options_{id}_year"]/option[@selected]' => '2020',
+ '//select[@id="options_{id}_month"]/option[@selected]' => '9',
+ '//select[@id="options_{id}_day"]/option[@selected]' => '30',
+ '//select[@id="options_{id}_hour"]/option[@selected]' => '2',
+ '//select[@id="options_{id}_minute"]/option[@selected]' => '15',
+ '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am',
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @return array
+ */
+ public function toHtmlWithCalendarDataProvider(): array
+ {
+ return [
+ [
+ [
+ 'month' => '3',
+ 'day' => '5',
+ 'year' => '2020',
+ 'hour' => '2',
+ 'minute' => '15',
+ 'day_part' => 'am',
+ 'date_internal' => '2020-09-30 02:15:00'
+ ],
+ [
+ '//input[@id="options_{id}_date"]' => '3/5/2020',
+ '//select[@id="options_{id}_hour"]/option[@selected]' => '2',
+ '//select[@id="options_{id}_minute"]/option[@selected]' => '15',
+ '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am',
+ ]
+ ],
+ [
+ [
+ 'date' => '09/30/2022',
+ 'hour' => '2',
+ 'minute' => '15',
+ 'day_part' => 'am',
+ 'date_internal' => '2020-09-30 02:15:00'
+ ],
+ [
+ '//input[@id="options_{id}_date"]' => '9/30/2020',
+ '//select[@id="options_{id}_hour"]/option[@selected]' => '2',
+ '//select[@id="options_{id}_minute"]/option[@selected]' => '15',
+ '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am',
+ ]
+ ],
+ [
+ [
+ 'month' => '3',
+ 'day' => '5',
+ 'year' => '2020',
+ 'hour' => '2',
+ 'minute' => '15',
+ 'day_part' => 'am',
+ 'date_internal' => '2020-09-30 02:15:00'
+ ],
+ [
+ '//input[@id="options_{id}_date"]' => '05/03/2020',
+ '//select[@id="options_{id}_hour"]/option[@selected]' => '2',
+ '//select[@id="options_{id}_minute"]/option[@selected]' => '15',
+ '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am',
+ ],
+ 'fr_FR'
+ ],
+ [
+ [
+ 'date' => '09/30/2022',
+ 'hour' => '2',
+ 'minute' => '15',
+ 'day_part' => 'am',
+ 'date_internal' => '2020-09-30 02:15:00'
+ ],
+ [
+ '//input[@id="options_{id}_date"]' => '30/09/2020',
+ '//select[@id="options_{id}_hour"]/option[@selected]' => '2',
+ '//select[@id="options_{id}_minute"]/option[@selected]' => '15',
+ '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am',
+ ],
+ 'fr_FR'
+ ]
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php
index 7032199e9fc4c..8b18f89542494 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/ProductTest.php
@@ -138,9 +138,6 @@ public function testSaveActionAndDuplicateWithUrlPathAttribute()
$urlPathAttribute = $product->getCustomAttribute('url_path');
$this->assertEquals($urlPathAttribute->getValue(), $product->getSku());
- // clean cache
- CacheCleaner::cleanAll();
-
// dispatch Save&Duplicate action and check it
$this->assertSaveAndDuplicateAction($product);
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ConfigTest.php
index 36379adcee601..8320ef979180f 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ConfigTest.php
@@ -31,7 +31,6 @@ protected function setUp(): void
public function testGetEntityAttributeCodes()
{
$entityType = 'catalog_product';
- CacheCleaner::cleanAll();
$this->assertEquals(
$this->config->getEntityAttributeCodes($entityType),
$this->config->getEntityAttributeCodes($entityType)
@@ -42,7 +41,6 @@ public function testGetAttribute()
{
$entityType = 'catalog_product';
$attributeCode = 'color';
- CacheCleaner::cleanAll();
$this->assertEquals(
$this->config->getAttribute($entityType, $attributeCode),
$this->config->getAttribute($entityType, $attributeCode)
@@ -52,7 +50,6 @@ public function testGetAttribute()
public function testGetEntityType()
{
$entityType = 'catalog_product';
- CacheCleaner::cleanAll();
$this->assertEquals(
$this->config->getEntityType($entityType),
$this->config->getEntityType($entityType)
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php
index 8e11efa8790cf..5cfa07cf5d402 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Indexer/Category/ProductTest.php
@@ -245,36 +245,6 @@ public function testCatalogCategoryProductIndexInvalidateAfterDelete(): void
$this->assertEquals(StateInterface::STATUS_INVALID, $status);
}
- /**
- * Test invalidate reindex after change product position on category
- *
- * @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Catalog/_files/category_with_different_price_products.php
- *
- * @return void
- */
- public function testCatalogCategoryProductIndexInvalidateAfterChangeProductPosition(): void
- {
- $this->indexer->setScheduled(true);
- $indexerShouldBeValid = $this->indexer->isValid();
-
- $category = $this->getCategoryByName->execute('Category 999');
-
- $category->setPostedProducts([
- $this->productResource->getIdBySku('simple1000') => 1,
- $this->productResource->getIdBySku('simple1001') => 2
- ]);
-
- $this->categoryResource->save($category);
-
- $state = $this->indexer->getState();
- $state->loadByIndexer($this->indexer->getId());
- $status = $state->getStatus();
-
- $this->assertTrue($indexerShouldBeValid);
- $this->assertEquals(StateInterface::STATUS_INVALID, $status);
- }
-
/**
* @param int $count
* @return Category[]
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Source/CountryofmanufactureTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Source/CountryofmanufactureTest.php
index 33e82e9f6ddcc..8b8f54af2d387 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Source/CountryofmanufactureTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Attribute/Source/CountryofmanufactureTest.php
@@ -24,7 +24,6 @@ protected function setUp(): void
public function testGetAllOptions()
{
- CacheCleaner::cleanAll();
$allOptions = $this->model->getAllOptions();
$cachedAllOptions = $this->model->getAllOptions();
$this->assertEquals($allOptions, $cachedAllOptions);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php
index 481ec6aeac0f2..d20bf2907c780 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php
@@ -593,6 +593,102 @@ public function updateImageDataProvider(): array
];
}
+ /**
+ * Tests that images are added correctly
+ *
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+ * @magentoDataFixture Magento/Store/_files/second_store.php
+ * @dataProvider addImagesDataProvider
+ * @param string $addFromStore
+ * @param array $newImages
+ * @param string $viewFromStore
+ * @param array $expectedImages
+ * @param array $select
+ * @return void
+ */
+ public function testAddImages(
+ string $addFromStore,
+ array $newImages,
+ string $viewFromStore,
+ array $expectedImages,
+ array $select = ['file', 'label', 'position']
+ ): void {
+ $storeId = (int)$this->storeRepository->get($addFromStore)->getId();
+ $product = $this->getProduct($storeId);
+ $images = $product->getData('media_gallery')['images'];
+ $images = array_merge($images, $newImages);
+ $product->setData('media_gallery', ['images' => $images]);
+ $this->updateHandler->execute($product);
+ $storeId = (int)$this->storeRepository->get($viewFromStore)->getId();
+ $product = $this->getProduct($storeId);
+ $actualImages = array_map(
+ function (\Magento\Framework\DataObject $item) use ($select) {
+ return $item->toArray($select);
+ },
+ $product->getMediaGalleryImages()->getItems()
+ );
+ $this->assertEquals($expectedImages, array_values($actualImages));
+ }
+
+ /**
+ * @return array[]
+ */
+ public function addImagesDataProvider(): array
+ {
+ return [
+ [
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'position' => 2,
+ 'label' => 'New Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image'
+ ]
+ ],
+ 'default',
+ [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'label' => 'Image Alt Text',
+ 'position' => 1,
+ ],
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'label' => null,
+ 'position' => 2,
+ ],
+ ]
+ ],
+ [
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'position' => 2,
+ 'label' => 'New Image Alt Text',
+ 'disabled' => 0,
+ 'media_type' => 'image'
+ ]
+ ],
+ 'fixture_second_store',
+ [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'label' => 'Image Alt Text',
+ 'position' => 1,
+ ],
+ [
+ 'file' => '/m/a/magento_small_image.jpg',
+ 'label' => 'New Image Alt Text',
+ 'position' => 2,
+ ],
+ ]
+ ]
+ ];
+ }
+
/**
* Check product image link and product image exist
*
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php
index 5b9c7b267f188..7d9e9a48093cb 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Attribute/Entity/AttributeTest.php
@@ -52,7 +52,6 @@ class AttributeTest extends \PHPUnit\Framework\TestCase
*/
protected function setUp(): void
{
- CacheCleaner::cleanAll();
$this->objectManager = Bootstrap::getObjectManager();
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
$this->attributeRepository = $this->objectManager->get(AttributeRepository::class);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/CategoryTest.php
index c57e981f772de..66e117a61ed2c 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/CategoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/CategoryTest.php
@@ -10,14 +10,21 @@
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Model\Category as CategoryModel;
+use Magento\Catalog\Model\Indexer\Category\Product as CategoryProductIndexer;
+use Magento\Catalog\Model\Indexer\Product\Category as ProductCategoryIndexer;
use Magento\Catalog\Model\ResourceModel\Category as CategoryResource;
use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
+use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
+use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
+use Magento\Framework\Indexer\IndexerInterface;
+use Magento\Framework\Indexer\IndexerRegistry;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\UrlInterface;
+use Magento\Indexer\Cron\UpdateMview;
use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
@@ -26,6 +33,7 @@
* Tests category resource model
*
* @see \Magento\Catalog\Model\ResourceModel\Category
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CategoryTest extends TestCase
{
@@ -54,6 +62,11 @@ class CategoryTest extends TestCase
/** @var WriteInterface */
private $mediaDirectory;
+ /**
+ * @var ProductResource
+ */
+ private $productResource;
+
/**
* @inheritdoc
*/
@@ -68,6 +81,7 @@ protected function setUp(): void
$this->categoryCollection = $this->objectManager->get(CategoryCollectionFactory::class)->create();
$this->filesystem = $this->objectManager->get(Filesystem::class);
$this->mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
+ $this->productResource = Bootstrap::getObjectManager()->get(ProductResource::class);
}
/**
@@ -116,6 +130,128 @@ public function testAddImageForCategory(): void
$this->assertFileExists($this->mediaDirectory->getAbsolutePath($imageRelativePath));
}
+ /**
+ * Test that adding or removing products in a category should not trigger full reindex in scheduled update mode
+ *
+ * @magentoAppArea adminhtml
+ * @magentoAppIsolation enabled
+ * @magentoDbIsolation disabled
+ * @magentoDataFixture Magento/Catalog/_files/category_with_three_products.php
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @magentoDataFixture Magento/Catalog/_files/catalog_category_product_reindex_all.php
+ * @magentoDataFixture Magento/Catalog/_files/catalog_product_category_reindex_all.php
+ * @magentoDataFixture Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php
+ * @dataProvider catalogProductChangesWithScheduledUpdateDataProvider
+ * @param array $products
+ * @return void
+ */
+ public function testCatalogProductChangesWithScheduledUpdate(array $products): void
+ {
+ // products are ordered by entity_id DESC because their positions are same and equal to 0
+ $initialProducts = ['simple1002', 'simple1001', 'simple1000'];
+ $defaultStoreId = (int) $this->storeManager->getDefaultStoreView()->getId();
+ $category = $this->getCategory(['name' => 'Category 999']);
+ $expectedProducts = array_keys($products);
+ $productIdsBySkus = $this->productResource->getProductsIdsBySkus($expectedProducts);
+ $postedProducts = [];
+ foreach ($products as $sku => $position) {
+ $postedProducts[$productIdsBySkus[$sku]] = $position;
+ }
+ $category->setPostedProducts($postedProducts);
+ $this->categoryResource->save($category);
+ // Indices should not be invalidated when adding/removing/reordering products in a category.
+ $categoryProductIndexer = $this->getIndexer(CategoryProductIndexer::INDEXER_ID);
+ $this->assertTrue(
+ $categoryProductIndexer->isValid(),
+ '"Indexed category/products association" indexer should not be invalidated.'
+ );
+ $productCategoryIndexer = $this->getIndexer(ProductCategoryIndexer::INDEXER_ID);
+ $this->assertTrue(
+ $productCategoryIndexer->isValid(),
+ '"Indexed product/categories association" indexer should not be invalidated.'
+ );
+ // catalog products is not update until partial reindex occurs
+ $collection = $this->getCategoryProducts($category, $defaultStoreId);
+ $this->assertEquals($initialProducts, $collection->getColumnValues('sku'));
+ // Execute MVIEW cron handler for cron job "indexer_update_all_views"
+ /** @var $mViewCron UpdateMview */
+ $mViewCron = $this->objectManager->create(UpdateMview::class);
+ $mViewCron->execute();
+ $collection = $this->getCategoryProducts($category, $defaultStoreId);
+ $this->assertEquals($expectedProducts, $collection->getColumnValues('sku'));
+ }
+
+ /**
+ * @return array
+ */
+ public function catalogProductChangesWithScheduledUpdateDataProvider(): array
+ {
+ return [
+ 'change products position' => [
+ [
+ 'simple1002' => 1,
+ 'simple1000' => 2,
+ 'simple1001' => 3,
+ ]
+ ],
+ 'Add new product' => [
+ [
+ 'simple1002' => 1,
+ 'simple1000' => 2,
+ 'simple-1' => 3,
+ 'simple1001' => 4,
+ ]
+ ],
+ 'Delete product' => [
+ [
+ 'simple1002' => 1,
+ 'simple1000' => 2,
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @param CategoryModel $category
+ * @param int $defaultStoreId
+ * @return ProductCollection
+ */
+ private function getCategoryProducts(CategoryModel $category, int $defaultStoreId)
+ {
+ /** @var ProductCollection $collection */
+ $collection = $this->objectManager->create(ProductCollection::class);
+ $collection->setStoreId($defaultStoreId);
+ $collection->addCategoryFilter($category);
+ $collection->addAttributeToSort('position');
+ return $collection;
+ }
+
+ /**
+ * @param array $filters
+ * @return CategoryModel
+ */
+ private function getCategory(array $filters): CategoryModel
+ {
+ /** @var CategoryCollection $categoryCollection */
+ $categoryCollection = $this->objectManager->create(CategoryCollection::class);
+ foreach ($filters as $field => $value) {
+ $categoryCollection->addFieldToFilter($field, $value);
+ }
+
+ return $categoryCollection->getFirstItem();
+ }
+
+ /**
+ * @param string $indexerId
+ * @return IndexerInterface
+ */
+ private function getIndexer(string $indexerId): IndexerInterface
+ {
+ /** @var IndexerRegistry $indexerRegistry */
+ $indexerRegistry = $this->objectManager->get(IndexerRegistry::class);
+ return $indexerRegistry->get($indexerId);
+ }
+
/**
* Prepare image url for image data
*
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
index 8ad346af068b4..d5e7d94ec0cae 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/CategoriesTest.php
@@ -39,7 +39,6 @@ public function testModifyMeta()
{
$inputMeta = include __DIR__ . '/_files/input_meta_for_categories.php';
$expectedCategories = include __DIR__ . '/_files/expected_categories.php';
- CacheCleaner::cleanAll();
$this->assertCategoriesInMeta($expectedCategories, $this->object->modifyMeta($inputMeta));
// Verify cached data
$this->assertCategoriesInMeta($expectedCategories, $this->object->modifyMeta($inputMeta));
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_product_reindex_all.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_product_reindex_all.php
new file mode 100644
index 0000000000000..3dfba9266cddc
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_category_product_reindex_all.php
@@ -0,0 +1,16 @@
+get(IndexerRegistry::class);
+
+$model = $indexRegistry->get(CategoryProductIndexer::INDEXER_ID);
+$model->reindexAll();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_product_category_reindex_all.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_product_category_reindex_all.php
new file mode 100644
index 0000000000000..6143933ba3d6c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/catalog_product_category_reindex_all.php
@@ -0,0 +1,16 @@
+get(IndexerRegistry::class);
+
+$model = $indexRegistry->get(ProductCategoryIndexer::INDEXER_ID);
+$model->reindexAll();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/enable_catalog_product_reindex_schedule_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/enable_catalog_product_reindex_schedule_rollback.php
index 429f89abb6ae7..8909b258b9f9c 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/enable_catalog_product_reindex_schedule_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/enable_catalog_product_reindex_schedule_rollback.php
@@ -5,8 +5,14 @@
*/
declare(strict_types=1);
-use Magento\Catalog\Model\Indexer\Product\Price\Processor;
+use Magento\Framework\Indexer\IndexerRegistry;
use Magento\TestFramework\Helper\Bootstrap;
-$indexerProcessor = Bootstrap::getObjectManager()->get(Processor::class);
-$indexerProcessor->getIndexer()->setScheduled(false);
+/** @var IndexerRegistry $indexRegistry */
+$indexRegistry = Bootstrap::getObjectManager()->get(IndexerRegistry::class);
+
+$model = $indexRegistry->get('catalog_category_product');
+$model->setScheduled(false);
+
+$model = $indexRegistry->get('catalog_product_category');
+$model->setScheduled(false);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attributes.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attributes.php
new file mode 100644
index 0000000000000..8764e12916d8b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attributes.php
@@ -0,0 +1,165 @@
+get(Config::class);
+
+/** @var ProductAttributeRepositoryInterface $attributeRepository */
+$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class);
+/** @var ProductAttributeInterfaceFactory $attributeFactory */
+$attributeFactory = $objectManager->get(ProductAttributeInterfaceFactory::class);
+
+/** @var $installer EavSetup */
+$installer = $objectManager->get(EavSetup::class);
+$attributeSetId = $installer->getAttributeSetId(Product::ENTITY, 'Default');
+$groupId = $installer->getDefaultAttributeGroupId(Product::ENTITY, $attributeSetId);
+
+/** @var WebsiteRepositoryInterface $websiteRepository */
+$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class);
+$baseWebsite = $websiteRepository->get('base');
+
+for ($i = 1; $i <= 2; $i++) {
+ $attributeModel = $attributeFactory->create();
+ $attributeModel->setData(
+ [
+ 'attribute_code' => 'test_attribute_' . $i,
+ 'entity_type_id' => $installer->getEntityTypeId(Product::ENTITY),
+ 'is_global' => 1,
+ 'is_user_defined' => 1,
+ 'frontend_input' => 'select',
+ 'is_unique' => 0,
+ 'is_required' => 0,
+ 'is_searchable' => 1,
+ 'is_visible_in_advanced_search' => 1,
+ 'is_comparable' => 1,
+ 'is_filterable' => 1,
+ 'is_filterable_in_search' => 1,
+ 'is_used_for_promo_rules' => 0,
+ 'is_html_allowed_on_front' => 1,
+ 'is_visible_on_front' => 1,
+ 'used_in_product_listing' => 1,
+ 'used_for_sort_by' => 1,
+ 'frontend_label' => ['Test Attribute ' . $i],
+ 'backend_type' => 'int',
+ 'option' => [
+ 'value' => ['option_0' => ['Option 1'], 'option_1' => ['Option 2']],
+ 'order' => ['option_0' => 1, 'option_1' => 2],
+ ],
+ 'default' => ['option_0'],
+ 'position' => 3 - $i
+ ]
+ );
+ $attribute = $attributeRepository->save($attributeModel);
+ $installer->addAttributeToGroup(Product::ENTITY, $attributeSetId, $groupId, $attribute->getId());
+}
+
+CacheCleaner::cleanAll();
+$eavConfig->clear();
+
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+/** @var ProductInterfaceFactory $productInterfaceFactory */
+$productInterfaceFactory = $objectManager->get(ProductInterfaceFactory::class);
+
+/** @var Product $product */
+$product = $productInterfaceFactory->create();
+$product->setTypeId(Type::TYPE_SIMPLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setName('Simple Product1')
+ ->setSku('simple1')
+ ->setTaxClassId('none')
+ ->setDescription('description')
+ ->setShortDescription('short description')
+ ->setOptionsContainer('container1')
+ ->setMsrpDisplayActualPriceType(SourceType::TYPE_IN_CART)
+ ->setPrice(10)
+ ->setWeight(1)
+ ->setMetaTitle('meta title')
+ ->setMetaKeyword('meta keyword')
+ ->setMetaDescription('meta description')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$baseWebsite->getId()])
+ ->setCategoryIds([])
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setSpecialPrice('5.99');
+$simple1 = $productRepository->save($product);
+
+/** @var Product $product */
+$product = $productInterfaceFactory->create();
+$product->setTypeId(Type::TYPE_SIMPLE)
+ ->setAttributeSetId($product->getDefaultAttributeSetId())
+ ->setName('Simple Product2')
+ ->setSku('simple2')
+ ->setTaxClassId('none')
+ ->setDescription('description')
+ ->setShortDescription('short description')
+ ->setOptionsContainer('container1')
+ ->setMsrpDisplayActualPriceType(SourceType::TYPE_ON_GESTURE)
+ ->setPrice(20)
+ ->setWeight(1)
+ ->setMetaTitle('meta title')
+ ->setMetaKeyword('meta keyword')
+ ->setMetaDescription('meta description')
+ ->setVisibility(Visibility::VISIBILITY_BOTH)
+ ->setStatus(Status::STATUS_ENABLED)
+ ->setWebsiteIds([$baseWebsite->getId()])
+ ->setCategoryIds([])
+ ->setStockData(['use_config_manage_stock' => 1, 'qty' => 50, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
+ ->setSpecialPrice('15.99');
+$simple2 = $productRepository->save($product);
+
+/** @var CategoryInterfaceFactory $categoryInterfaceFactory */
+$categoryInterfaceFactory = $objectManager->get(CategoryInterfaceFactory::class);
+
+$category = $categoryInterfaceFactory->create();
+$category->isObjectNew(true);
+$category->setId(3334)
+ ->setCreatedAt('2014-06-23 09:50:07')
+ ->setName('Category 1')
+ ->setParentId(2)
+ ->setPath('1/2/333')
+ ->setLevel(2)
+ ->setAvailableSortBy(['position', 'name'])
+ ->setDefaultSortBy('name')
+ ->setIsActive(true)
+ ->setPosition(1)
+ ->setPostedProducts(
+ [
+ $simple1->getId() => 10,
+ $simple2->getId() => 11
+ ]
+ );
+$category->save();
+
+/** @var Collection $indexerCollection */
+$indexerCollection = $objectManager->get(Collection::class);
+$indexerCollection->load();
+/** @var Indexer $indexer */
+foreach ($indexerCollection->getItems() as $indexer) {
+ $indexer->reindexAll();
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attributes_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attributes_rollback.php
new file mode 100644
index 0000000000000..49e2b549552e6
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_with_layered_navigation_attributes_rollback.php
@@ -0,0 +1,63 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+
+foreach (['simple1', 'simple2'] as $sku) {
+ try {
+ $product = $productRepository->get($sku, false, null, true);
+ $productRepository->delete($product);
+ } catch (NoSuchEntityException $exception) {
+ //Product already removed
+ }
+}
+
+/** @var CategoryRepositoryInterface $categoryRepository */
+$categoryRepository = $objectManager->get(CategoryRepositoryInterface::class);
+/** @var GetCategoryByName $getCategoryByName */
+$getCategoryByName = $objectManager->get(GetCategoryByName::class);
+$category = $getCategoryByName->execute('Category 1');
+try {
+ if ($category->getId()) {
+ $categoryRepository->delete($category);
+ }
+} catch (NoSuchEntityException $exception) {
+ //Category already removed
+}
+
+$eavConfig = $objectManager->get(Config::class);
+/** @var ProductAttributeRepositoryInterface $attributeRepository */
+$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class);
+
+try {
+ for ($i = 1; $i <= 2; $i++) {
+ $attribute = $attributeRepository->get('test_attribute_' . $i);
+ $attributeRepository->delete($attribute);
+ }
+} catch (NoSuchEntityException $exception) {
+ //Attribute already removed
+}
+$eavConfig->clear();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
index 01a6bfe7b39b6..3ca6754c77767 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -3330,4 +3330,89 @@ public function testUpdateImageByNameNotPrefixedWithSlash()
$imageItems = $product->getMediaGalleryImages()->getItems();
$this->assertCount(0, $imageItems);
}
+
+ /**
+ * Tests that images are imported correctly
+ *
+ * @magentoDataFixture mediaImportImageFixture
+ * @magentoDataFixture Magento/Store/_files/core_fixturestore.php
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+ * @dataProvider importImagesDataProvider
+ * @magentoAppIsolation enabled
+ * @param string $importFile
+ * @param string $productSku
+ * @param string $storeCode
+ * @param array $expectedImages
+ * @param array $select
+ */
+ public function testImportImages(
+ string $importFile,
+ string $productSku,
+ string $storeCode,
+ array $expectedImages,
+ array $select = ['file', 'label', 'position']
+ ): void {
+ $this->importDataForMediaTest($importFile);
+ $product = $this->getProductBySku($productSku, $storeCode);
+ $actualImages = array_map(
+ function (\Magento\Framework\DataObject $item) use ($select) {
+ return $item->toArray($select);
+ },
+ $product->getMediaGalleryImages()->getItems()
+ );
+ $this->assertEquals($expectedImages, array_values($actualImages));
+ }
+
+ /**
+ * @return array[]
+ */
+ public function importImagesDataProvider(): array
+ {
+ return [
+ [
+ 'import_media_additional_images_storeview.csv',
+ 'simple',
+ 'default',
+ [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'label' => 'Image Alt Text',
+ 'position' => 1
+ ],
+ [
+ 'file' => '/m/a/magento_additional_image_one.jpg',
+ 'label' => null,
+ 'position' => 2
+ ],
+ [
+ 'file' => '/m/a/magento_additional_image_two.jpg',
+ 'label' => null,
+ 'position' => 3
+ ],
+ ]
+ ],
+ [
+ 'import_media_additional_images_storeview.csv',
+ 'simple',
+ 'fixturestore',
+ [
+ [
+ 'file' => '/m/a/magento_image.jpg',
+ 'label' => 'Image Alt Text',
+ 'position' => 1
+ ],
+ [
+ 'file' => '/m/a/magento_additional_image_one.jpg',
+ 'label' => 'Additional Image Label One',
+ 'position' => 2
+ ],
+ [
+ 'file' => '/m/a/magento_additional_image_two.jpg',
+ 'label' => 'Additional Image Label Two',
+ 'position' => 3
+ ],
+ ]
+ ]
+ ];
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv
new file mode 100644
index 0000000000000..ed8755a73fcb1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv
@@ -0,0 +1,2 @@
+"sku","store_view_code","additional_images","additional_image_labels"
+"simple","fixturestore","magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two"
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php
index 0432649455abe..e3e3d3e3972e5 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteTest.php
@@ -85,6 +85,7 @@ public function productDataProvider(): array
'sku' => 'test-product',
'name' => 'test product',
'price' => 150,
+ 'website_ids' => [1]
],
'expected_data' => [
[
@@ -104,6 +105,7 @@ public function productDataProvider(): array
'name' => 'test product',
'price' => 150,
'url_key' => 'test-product-url-key',
+ 'website_ids' => [1]
],
'expected_data' => [
[
@@ -123,6 +125,7 @@ public function productDataProvider(): array
'name' => 'test product',
'price' => 150,
'url_key' => 'test-product-url-key',
+ 'website_ids' => [1]
],
'expected_data' => [],
],
@@ -201,6 +204,7 @@ public function existingUrlKeyProvider(): array
'name' => 'test-simple-product',
'price' => 150,
'url_key' => 'simple-product',
+ 'store_ids' => [1]
],
'with_autogenerated_existing_product_url_key' => [
'type_id' => Type::TYPE_SIMPLE,
@@ -208,6 +212,7 @@ public function existingUrlKeyProvider(): array
'sku' => 'test-simple-product',
'name' => 'simple product',
'price' => 150,
+ 'store_ids' => [1]
],
'with_specified_existing_category_url_key' => [
'type_id' => Type::TYPE_SIMPLE,
@@ -216,6 +221,7 @@ public function existingUrlKeyProvider(): array
'name' => 'test-simple-product',
'price' => 150,
'url_key' => 'category-1',
+ 'store_ids' => [1]
],
'with_autogenerated_existing_category_url_key' => [
'type_id' => Type::TYPE_SIMPLE,
@@ -223,6 +229,7 @@ public function existingUrlKeyProvider(): array
'sku' => 'test-simple-product',
'name' => 'category 1',
'price' => 150,
+ 'store_ids' => [1]
],
],
];
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php
index c3efd660792c0..82631220730de 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserverTest.php
@@ -5,25 +5,46 @@
*/
namespace Magento\CatalogUrlRewrite\Observer;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\UrlRewrite\Model\UrlFinderInterface;
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
-use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
+use PHPUnit\Framework\TestCase;
/**
* @magentoAppArea adminhtml
* @magentoDbIsolation disabled
*/
-class ProductProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase
+class ProductProcessUrlRewriteSavingObserverTest extends TestCase
{
- /** @var \Magento\Framework\ObjectManagerInterface */
- protected $objectManager;
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $objectManager;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
+ /**
+ * @var ProductRepositoryInterface
+ */
+ private $productRepository;
/**
* Set up
*/
protected function setUp(): void
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
}
/**
@@ -32,8 +53,8 @@ protected function setUp(): void
*/
private function getActualResults(array $filter)
{
- /** @var \Magento\UrlRewrite\Model\UrlFinderInterface $urlFinder */
- $urlFinder = $this->objectManager->get(\Magento\UrlRewrite\Model\UrlFinderInterface::class);
+ /** @var UrlFinderInterface $urlFinder */
+ $urlFinder = $this->objectManager->get(UrlFinderInterface::class);
$actualResults = [];
foreach ($urlFinder->findAllByData($filter) as $url) {
$actualResults[] = [
@@ -53,16 +74,14 @@ private function getActualResults(array $filter)
*/
public function testUrlKeyHasChangedInGlobalContext()
{
- /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/
- $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
- /** @var \Magento\Catalog\Model\Product $product*/
- $product = $productRepository->get('product1');
+ $testStore1 = $this->storeManager->getStore('default');
+ $testStore4 = $this->storeManager->getStore('test');
- /** @var StoreManagerInterface $storeManager */
- $storeManager = $this->objectManager->get(StoreManagerInterface::class);
- $storeManager->setCurrentStore(0);
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+
+ /** @var Product $product*/
+ $product = $this->productRepository->get('product1');
- $testStore = $storeManager->getStore('test');
$productFilter = [
UrlRewrite::ENTITY_TYPE => 'product',
];
@@ -73,14 +92,14 @@ public function testUrlKeyHasChangedInGlobalContext()
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => 1,
+ 'store_id' => $testStore1->getId(),
],
[
'request_path' => "product-1.html",
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => $testStore->getId(),
+ 'store_id' => $testStore4->getId(),
],
];
$actual = $this->getActualResults($productFilter);
@@ -91,7 +110,7 @@ public function testUrlKeyHasChangedInGlobalContext()
$product->setData('save_rewrites_history', true);
$product->setUrlKey('new-url');
$product->setUrlPath('new-path');
- $product->save();
+ $this->productRepository->save($product);
$expected = [
[
@@ -99,28 +118,28 @@ public function testUrlKeyHasChangedInGlobalContext()
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => 1,
+ 'store_id' => $testStore1->getId(),
],
[
'request_path' => "new-url.html",
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => $testStore->getId(),
+ 'store_id' => $testStore4->getId(),
],
[
'request_path' => "product-1.html",
'target_path' => "new-url.html",
'is_auto_generated' => 0,
'redirect_type' => 301,
- 'store_id' => 1,
+ 'store_id' => $testStore1->getId(),
],
[
'request_path' => "product-1.html",
'target_path' => "new-url.html",
'is_auto_generated' => 0,
'redirect_type' => 301,
- 'store_id' => $testStore->getId(),
+ 'store_id' => $testStore4->getId(),
],
];
@@ -136,16 +155,13 @@ public function testUrlKeyHasChangedInGlobalContext()
*/
public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection()
{
- /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/
- $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
- /** @var \Magento\Catalog\Model\Product $product*/
- $product = $productRepository->get('product1');
+ $testStore1 = $this->storeManager->getStore('default');
+ $testStore4 = $this->storeManager->getStore('test');
- /** @var StoreManagerInterface $storeManager */
- $storeManager = $this->objectManager->get(StoreManagerInterface::class);
- $storeManager->setCurrentStore(1);
+ $this->storeManager->setCurrentStore($testStore1);
- $testStore = $storeManager->getStore('test');
+ /** @var Product $product*/
+ $product = $this->productRepository->get('product1');
$productFilter = [
UrlRewrite::ENTITY_TYPE => 'product',
@@ -154,7 +170,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection()
$product->setData('save_rewrites_history', true);
$product->setUrlKey('new-url');
$product->setUrlPath('new-path');
- $product->save();
+ $this->productRepository->save($product);
$expected = [
[
@@ -162,21 +178,21 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection()
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => 1,
+ 'store_id' => $testStore1->getId(),
],
[
'request_path' => "product-1.html",
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => $testStore->getId(),
+ 'store_id' => $testStore4->getId(),
],
[
'request_path' => "product-1.html",
'target_path' => "new-url.html",
'is_auto_generated' => 0,
'redirect_type' => 301,
- 'store_id' => 1,
+ 'store_id' => $testStore1->getId(),
],
];
@@ -192,16 +208,13 @@ public function testUrlKeyHasChangedInStoreviewContextWithPermanentRedirection()
*/
public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirection()
{
- /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository*/
- $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
- /** @var \Magento\Catalog\Model\Product $product*/
- $product = $productRepository->get('product1');
+ $testStore1 = $this->storeManager->getStore('default');
+ $testStore4 = $this->storeManager->getStore('test');
- /** @var StoreManagerInterface $storeManager */
- $storeManager = $this->objectManager->get(StoreManagerInterface::class);
- $storeManager->setCurrentStore(1);
+ $this->storeManager->setCurrentStore(1);
- $testStore = $storeManager->getStore('test');
+ /** @var Product $product*/
+ $product = $this->productRepository->get('product1');
$productFilter = [
UrlRewrite::ENTITY_TYPE => 'product',
@@ -210,7 +223,7 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio
$product->setData('save_rewrites_history', false);
$product->setUrlKey('new-url');
$product->setUrlPath('new-path');
- $product->save();
+ $this->productRepository->save($product);
$expected = [
[
@@ -218,17 +231,402 @@ public function testUrlKeyHasChangedInStoreviewContextWithoutPermanentRedirectio
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => 1,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ],
+ ];
+
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+ }
+
+ /**
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php
+ * @magentoAppIsolation enabled
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testAddAndRemoveProductFromWebsite()
+ {
+ $testStore1 = $this->storeManager->getStore('default');
+ $testStore2 = $this->storeManager->getStore('fixture_second_store');
+ $testStore3 = $this->storeManager->getStore('fixture_third_store');
+ $testStore4 = $this->storeManager->getStore('test');
+
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+
+ /** @var Product $product*/
+ $product = $this->productRepository->get('product1');
+
+ $productFilter = [
+ UrlRewrite::ENTITY_TYPE => 'product',
+ ];
+
+ //Product in 1st website. Should result in being in 1st and 4th stores.
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ],
+ ];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+
+ //Add product to websites corresponding to all 4 stores.
+ //Rewrites should be present for all stores.
+ $product->setWebsiteIds(
+ array_unique(
+ [
+ $testStore1->getWebsiteId(),
+ $testStore2->getWebsiteId(),
+ $testStore3->getWebsiteId(),
+ $testStore4->getWebsiteId(),
+ ]
+ )
+ );
+ $this->productRepository->save($product);
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore2->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore3->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ]
+ ];
+
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+
+ //Remove product from stores 1 and 4 and leave assigned to stores 2 and 3.
+ $product->setWebsiteIds(
+ array_unique(
+ [
+ $testStore2->getWebsiteId(),
+ $testStore3->getWebsiteId(),
+ ]
+ )
+ );
+ $this->productRepository->save($product);
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore2->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore3->getId(),
+ ],
+ ];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+ }
+
+ /**
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testChangeVisibilityGlobalScope()
+ {
+ $testStore1 = $this->storeManager->getStore('default');
+ $testStore2 = $this->storeManager->getStore('fixture_second_store');
+ $testStore3 = $this->storeManager->getStore('fixture_third_store');
+ $testStore4 = $this->storeManager->getStore('test');
+
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+
+ /** @var Product $product*/
+ $product = $this->productRepository->get('product1');
+
+ $productFilter = [
+ UrlRewrite::ENTITY_TYPE => 'product',
+ ];
+
+ //Product in 1st website. Should result in being in 1st and 4th stores.
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ]
+ ];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+
+ //Set product to be not visible at global scope
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+ $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE);
+ $this->productRepository->save($product);
+ $this->assertEmpty($this->getActualResults($productFilter));
+
+ //Add product to websites corresponding to all 4 stores.
+ //Rewrites should not be present as the product is hidden
+ //at the global scope.
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+ $product->setWebsiteIds(
+ array_unique(
+ [
+ $testStore1->getWebsiteId(),
+ $testStore2->getWebsiteId(),
+ $testStore3->getWebsiteId(),
+ $testStore4->getWebsiteId(),
+ ]
+ )
+ );
+ $this->productRepository->save($product);
+ $expected = [];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+
+ //Set product to be visible at global scope
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+ $product->setVisibility(Visibility::VISIBILITY_BOTH);
+ $this->productRepository->save($product);
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore2->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore3->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ],
+ ];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+ }
+
+ /**
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_rewrite_multistore.php
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
+ public function testChangeVisibilityLocalScope()
+ {
+ $testStore1 = $this->storeManager->getStore('default');
+ $testStore2 = $this->storeManager->getStore('fixture_second_store');
+ $testStore3 = $this->storeManager->getStore('fixture_third_store');
+ $testStore4 = $this->storeManager->getStore('test');
+
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+
+ /** @var Product $product*/
+ $product = $this->productRepository->get('product1');
+
+ $productFilter = [
+ UrlRewrite::ENTITY_TYPE => 'product',
+ ];
+
+ //Product in 1st website. Should result in being in 1st and 4th stores.
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ],
+ ];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+
+ //Set product to be not visible at store 4 scope
+ //Rewrite should only be present for store 1
+ $this->storeManager->setCurrentStore($testStore4);
+ $product = $this->productRepository->get('product1');
+ $product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE);
+ $this->productRepository->save($product);
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ ];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+ self::assertCount(count($expected), $actual);
+
+ //Add product to websites corresponding to all 4 stores.
+ //Rewrites should be present for stores 1,2 and 3.
+ //No rewrites should be present for store 4 as that is not visible
+ //at local scope.
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+ $product = $this->productRepository->get('product1');
+ $product->getExtensionAttributes()->setWebsiteIds(
+ array_unique(
+ [
+ $testStore1->getWebsiteId(),
+ $testStore2->getWebsiteId(),
+ $testStore3->getWebsiteId(),
+ $testStore4->getWebsiteId()
+ ],
+ )
+ );
+ $this->productRepository->save($product);
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore2->getId(),
],
[
'request_path' => "product-1.html",
'target_path' => "catalog/product/view/id/" . $product->getId(),
'is_auto_generated' => 1,
'redirect_type' => 0,
- 'store_id' => $testStore->getId(),
+ 'store_id' => $testStore3->getId(),
],
];
+ $actual = $this->getActualResults($productFilter);
+ foreach ($expected as $row) {
+ $this->assertContains($row, $actual);
+ }
+ //Set product to be visible at store 4 scope only
+ $this->storeManager->setCurrentStore($testStore4);
+ $product = $this->productRepository->get('product1');
+ $product->setVisibility(Visibility::VISIBILITY_BOTH);
+ $this->productRepository->save($product);
+ $expected = [
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore1->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore2->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore3->getId(),
+ ],
+ [
+ 'request_path' => "product-1.html",
+ 'target_path' => "catalog/product/view/id/" . $product->getId(),
+ 'is_auto_generated' => 1,
+ 'redirect_type' => 0,
+ 'store_id' => $testStore4->getId(),
+ ],
+ ];
$actual = $this->getActualResults($productFilter);
foreach ($expected as $row) {
$this->assertContainsEquals($row, $actual);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Model/Product/UpdateProductWebsiteUrlRewritesTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Model/Product/UpdateProductWebsiteUrlRewritesTest.php
new file mode 100644
index 0000000000000..f958027f413e3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Plugin/Catalog/Model/Product/UpdateProductWebsiteUrlRewritesTest.php
@@ -0,0 +1,72 @@
+action = $objectManager->get(Action::class);
+ $this->storeWebsiteRelation = $objectManager->get(StoreWebsiteRelationInterface::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_with_url_key.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_store_group_and_store.php
+ */
+ public function testUpdateUrlRewrites()
+ {
+ /** @var Website $website */
+ $websiteRepository = Bootstrap::getObjectManager()->get(WebsiteRepository::class);
+ $website = $websiteRepository->get('test');
+ $productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
+ $product = $productRepository->get('simple1', false, null, true);
+ $this->action->updateWebsites([$product->getId()], [$website->getId()], 'add');
+ $storeIds = $this->storeWebsiteRelation->getStoreByWebsiteId($website->getId());
+
+ $this->assertStringContainsString(
+ $product->getUrlKey() . '.html',
+ $product->setStoreId(reset($storeIds))->getProductUrl()
+ );
+
+ $this->action->updateWebsites([$product->getId()], [$website->getId()], 'remove');
+ $product->setRequestPath('');
+ $url = $product->setStoreId(reset($storeIds))->getProductUrl();
+ $this->assertStringNotContainsString(
+ $product->getUrlKey() . '.html',
+ $url
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php
index 4cb4b00d08a84..e54ce16051d60 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php
@@ -12,6 +12,8 @@
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Exception\InputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\ShippingAssignmentInterface;
use Magento\TestFramework\Helper\Bootstrap;
@@ -120,6 +122,51 @@ public function testDifferentAddresses(bool $swapShipping): void
$this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation);
}
+ /**
+ * Test save address information with customer custom address attribute for quote
+ *
+ * @return void
+ *
+ * @throws LocalizedException
+ * @throws NoSuchEntityException
+ * @magentoDataFixture Magento/Sales/_files/quote.php
+ * @magentoDataFixture Magento/Customer/_files/customer_address_with_custom_text_attribute.php
+ */
+ public function testSaveAddressInformationWithCustomerCustomAddressAttribute(): void
+ {
+ $carts = $this->cartRepo->getList(
+ $this->searchCriteria->addFilter('reserved_order_id', 'test01')->create()
+ )->getItems();
+ $currentQuote = array_pop($carts);
+ $guestCustomer = $this->customerRepo->get('JohnDoe@mail.com');
+
+ $customerCustomAddressAttribute = $guestCustomer->getCustomAttributes();
+
+ /** @var ShippingAssignmentInterface $shippingAssignment */
+ $shippingAssignment = $currentQuote->getExtensionAttributes()->getShippingAssignments()[0];
+ $shippingAddress = $shippingAssignment->getShipping()->getAddress();
+ $billingAddress = $currentQuote->getBillingAddress();
+
+ if ($customerCustomAddressAttribute) {
+ $shippingAddress->setCustomAttributes($customerCustomAddressAttribute);
+ $billingAddress->setCustomAttributes($customerCustomAddressAttribute);
+ }
+
+ /** @var ShippingInformationInterface $shippingInformation */
+ $shippingInformation = $this->shippingFactory->create();
+ $shippingInformation->setBillingAddress($billingAddress);
+ $shippingInformation->setShippingAddress($shippingAddress);
+ $shippingInformation->setShippingMethodCode('flatrate');
+ $shippingInformation->setShippingCarrierCode('flatrate');
+ /** @var QuoteIdMask $idMask */
+ $idMask = $this->maskFactory->create();
+ $idMask->load($currentQuote->getId(), 'quote_id');
+
+ $paymentDetails = $this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation);
+ $this->assertNotEmpty($paymentDetails);
+ $this->assertEquals($currentQuote->getGrandTotal(), $paymentDetails->getTotals()->getSubtotal());
+ }
+
/**
* Different variations for addresses test.
*
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 f0ba56f7179aa..4c003c209e3c2 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
@@ -67,7 +67,7 @@
$iDate++;
break;
case ProductCustomOptionInterface::OPTION_GROUP_FILE:
- $value = 'test.jpg';
+ $value = null;
break;
default:
$value = 'test';
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AddressMetadataTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AddressMetadataTest.php
index 647c386e2b784..4443d170e388a 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/AddressMetadataTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AddressMetadataTest.php
@@ -19,7 +19,6 @@ class AddressMetadataTest extends \PHPUnit\Framework\TestCase
protected function setUp(): void
{
- CacheCleaner::cleanAll();
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$objectManager->configure(
[
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php
index c3e08b5294679..63d7019ee4f61 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/CustomerMetadataTest.php
@@ -29,7 +29,6 @@ class CustomerMetadataTest extends \PHPUnit\Framework\TestCase
protected function setUp(): void
{
- CacheCleaner::cleanAll();
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$objectManager->configure(
[\Magento\Framework\Api\ExtensionAttribute\Config\Reader::class => [
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Group/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Group/ResolverTest.php
new file mode 100644
index 0000000000000..0f85a94f639d1
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Group/ResolverTest.php
@@ -0,0 +1,26 @@
+create(Resolver::class);
+ $groupId = $resolver->resolve($customerId);
+ $this->assertEquals($groupId, $expectedGroupId);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php
new file mode 100644
index 0000000000000..ebe4ad76405ef
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php
@@ -0,0 +1,96 @@
+create(Config::class)
+ ->getEntityType('customer');
+
+/** @var $attributeSet Set */
+$attributeSet = Bootstrap::getObjectManager()
+ ->create(Set::class);
+
+$select = Bootstrap::getObjectManager()->create(
+ Attribute::class,
+ [
+ 'data' => [
+ 'frontend_input' => 'text',
+ 'frontend_label' => ['test_text_attribute'],
+ 'sort_order' => 1,
+ 'backend_type' => 'varchar',
+ 'is_user_defined' => 1,
+ 'is_system' => 0,
+ 'is_used_in_grid' => 1,
+ 'is_required' => '0',
+ 'is_visible' => 1,
+ 'used_in_forms' => [
+ 'customer_address_edit',
+ 'adminhtml_customer_address'
+ ],
+ 'attribute_set_id' => $entityType->getDefaultAttributeSetId(),
+ 'attribute_group_id' => $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()),
+ 'entity_type_id' => $entityType->getId(),
+ 'default_value' => '',
+ ],
+ ]
+);
+$select->setAttributeCode('test_text_attribute');
+$select->save();
+
+$customer = $objectManager
+ ->create(Customer::class);
+$customer->setWebsiteId(1)
+ ->setEntityId(1)
+ ->setEntityTypeId($entityType->getId())
+ ->setAttributeSetId($entityType->getDefaultAttributeSetId())
+ ->setEmail('JohnDoe@mail.com')
+ ->setPassword('password')
+ ->setGroupId(1)
+ ->setStoreId(1)
+ ->setIsActive(1)
+ ->setFirstname('John')
+ ->setLastname('Doe')
+ ->setGender(2)
+ ->setTestTextAttribute('123');
+$customer->isObjectNew(true);
+// Create address
+$address = $objectManager->create(Address::class);
+// default_billing and default_shipping information would not be saved, it is needed only for simple check
+$address->addData(
+ [
+ 'firstname' => 'Charles',
+ 'lastname' => 'Alston',
+ 'street' => '3781 Neuport Lane',
+ 'city' => 'Panola',
+ 'country_id' => 'US',
+ 'region_id' => '51',
+ 'postcode' => '30058',
+ 'telephone' => '770-322-3514',
+ 'default_billing' => 1,
+ 'default_shipping' => 1,
+ ]
+);
+// Assign customer and address
+$customer->addAddress($address);
+$customer->save();
+// Mark last address as default billing and default shipping for current customer
+$customer->setDefaultBilling($address->getId());
+$customer->setDefaultShipping($address->getId());
+$customer->save();
+
+$objectManager->get(Registry::class)->unregister('_fixture/Magento_ImportExport_Customer');
+$objectManager->get(Registry::class)->register('_fixture/Magento_ImportExport_Customer', $customer);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php
new file mode 100644
index 0000000000000..3b276b77fbed5
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php
@@ -0,0 +1,33 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var $attribute Attribute */
+$attribute = Bootstrap::getObjectManager()->create(
+ Attribute::class
+);
+$attribute->loadByCode('customer', 'test_text_attribute');
+$attribute->delete();
+
+/** @var Customer $customer */
+$customer = Bootstrap::getObjectManager()
+ ->create(Customer::class);
+$customer->setWebsiteId(1);
+$customer->loadByEmail('JohnDoe@mail.com');
+$customer->delete();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php
index f9a1d2923e5be..552040489e253 100644
--- a/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php
+++ b/dev/tests/integration/testsuite/Magento/Dhl/Model/CarrierTest.php
@@ -17,19 +17,22 @@
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Quote\Model\Quote\Address\RateResult\Error;
use Magento\Shipping\Model\Shipment\Request;
+use Magento\Shipping\Model\Simplexml\Element as ShippingElement;
use Magento\Shipping\Model\Tracking\Result\Status;
use Magento\Store\Model\ScopeInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\HTTP\AsyncClientInterfaceMock;
-use Magento\Shipping\Model\Simplexml\Element as ShippingElement;
+use PHPUnit\Framework\TestCase;
/**
* Test for DHL integration.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class CarrierTest extends \PHPUnit\Framework\TestCase
+class CarrierTest extends TestCase
{
+ private const PRODUCT_NAME_SPECIAL_CHARS = 'Φυστίκι Ψημένο με Αλάτι Συσκευασία';
+
/**
* @var Carrier
*/
@@ -254,10 +257,16 @@ private function assertTrackingResult($expectedTrackingData, $trackingResults):
* @param string $origCountryId
* @param string $expectedRegionCode
* @param string $destCountryId
+ * @param bool|null $isProductNameContainsSpecialChars
+ * @return void
* @dataProvider requestToShipmentDataProvider
*/
- public function testRequestToShip(string $origCountryId, string $expectedRegionCode, string $destCountryId): void
- {
+ public function testRequestToShip(
+ string $origCountryId,
+ string $expectedRegionCode,
+ string $destCountryId,
+ bool $isProductNameContainsSpecialChars = false
+ ): void {
$this->config->setValue(
'shipping/origin/country_id',
$origCountryId,
@@ -274,6 +283,8 @@ public function testRequestToShip(string $origCountryId, string $expectedRegionC
)
]
);
+ $productName = $isProductNameContainsSpecialChars ? self::PRODUCT_NAME_SPECIAL_CHARS : 'item_name';
+
//phpcs:enable Magento2.Functions.DiscouragedFunction
$request = new Request(
[
@@ -291,7 +302,7 @@ public function testRequestToShip(string $origCountryId, string $expectedRegionC
],
'items' => [
'item1' => [
- 'name' => 'item_name',
+ 'name' => $productName,
],
],
],
@@ -329,10 +340,15 @@ public function testRequestToShip(string $origCountryId, string $expectedRegionC
$requestElement->Request->ServiceHeader->MessageReference = 'MAGE_SHIP_28TO32_Char_CHECKED';
$requestElement->Request->ServiceHeader->MessageTime = 'currentTime';
$requestElement->ShipmentDetails->Date = 'currentTime';
- $this->assertXmlStringEqualsXmlString(
- $this->getExpectedLabelRequestXml($origCountryId, $destCountryId, $expectedRegionCode),
- $requestElement->asXML()
+
+ $expectedLabelRequest = $this->getExpectedLabelRequestXml(
+ $origCountryId,
+ $destCountryId,
+ $expectedRegionCode,
+ $isProductNameContainsSpecialChars
);
+
+ $this->assertXmlStringEqualsXmlString($expectedLabelRequest, $requestElement->asXML());
}
/**
@@ -351,7 +367,10 @@ public function requestToShipmentDataProvider(): array
],
[
'DE', 'EU', 'DE'
- ]
+ ],
+ [
+ 'GB', 'EU', 'US', true
+ ],
];
}
@@ -361,12 +380,14 @@ public function requestToShipmentDataProvider(): array
* @param string $origCountryId
* @param string $destCountryId
* @param string $regionCode
+ * @param bool $isProductNameContainsSpecialChars
* @return string
*/
private function getExpectedLabelRequestXml(
string $origCountryId,
string $destCountryId,
- string $regionCode
+ string $regionCode,
+ bool $isProductNameContainsSpecialChars
): string {
$countryNames = [
'US' => 'United States Of America',
@@ -387,6 +408,10 @@ private function getExpectedLabelRequestXml(
$expectedRequestElement->Shipper->CountryName = $countryNames[$origCountryId];
$expectedRequestElement->RegionCode = $regionCode;
+ if ($isProductNameContainsSpecialChars) {
+ $expectedRequestElement->ShipmentDetails->Pieces->Piece->PieceContents = self::PRODUCT_NAME_SPECIAL_CHARS;
+ }
+
return $expectedRequestElement->asXML();
}
diff --git a/dev/tests/integration/testsuite/Magento/Directory/Block/DataTest.php b/dev/tests/integration/testsuite/Magento/Directory/Block/DataTest.php
index ea2368a35c2f2..ccc1ea3f2f0b9 100644
--- a/dev/tests/integration/testsuite/Magento/Directory/Block/DataTest.php
+++ b/dev/tests/integration/testsuite/Magento/Directory/Block/DataTest.php
@@ -22,7 +22,6 @@ protected function setUp(): void
public function testGetCountryHtmlSelect()
{
- CacheCleaner::cleanAll();
$result = $this->block->getCountryHtmlSelect();
$resultTwo = $this->block->getCountryHtmlSelect();
$this->assertEquals($result, $resultTwo);
@@ -30,7 +29,6 @@ public function testGetCountryHtmlSelect()
public function testGetRegionHtmlSelect()
{
- CacheCleaner::cleanAll();
$result = $this->block->getRegionHtmlSelect();
$resultTwo = $this->block->getRegionHtmlSelect();
$this->assertEquals($result, $resultTwo);
diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php
index a2865d52517fa..85bf40b342fb5 100644
--- a/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Eav/Model/ConfigTest.php
@@ -33,7 +33,6 @@ protected function setUp(): void
public function testGetEntityAttributeCodes()
{
$entityType = 'test';
- CacheCleaner::cleanAll();
$entityAttributeCodes1 = $this->config->getEntityAttributeCodes($entityType);
$this->assertEquals(
[
@@ -60,7 +59,6 @@ public function testGetEntityAttributeCodesWithObject()
$testEntityType = Bootstrap::getObjectManager()->create(\Magento\Eav\Model\Entity\Type::class)
->loadByCode('test');
$attributeSetId = $testEntityType->getDefaultAttributeSetId();
- CacheCleaner::cleanAll();
$object = new DataObject(
[
'attribute_set_id' => $attributeSetId,
@@ -86,7 +84,6 @@ public function testGetEntityAttributeCodesWithObject()
public function testGetAttributes()
{
$entityType = 'test';
- CacheCleaner::cleanAll();
$attributes1 = $this->config->getAttributes($entityType);
$expectedAttributeCodes = [
'attribute_for_search_1',
@@ -111,7 +108,6 @@ public function testGetAttributes()
public function testGetAttribute()
{
$entityType = 'test';
- CacheCleaner::cleanAll();
$attribute1 = $this->config->getAttribute($entityType, 'attribute_for_search_1');
$this->assertEquals('attribute_for_search_1', $attribute1->getAttributeCode());
$this->assertEquals('varchar', $attribute1->getBackendType());
@@ -153,8 +149,7 @@ public function testGetAttributeWithCacheUserDefinedAttribute()
$config = Bootstrap::getObjectManager()->create(\Magento\Eav\Model\Config::class);
$updatedAttribute = $config->getAttribute($entityType, 'foo');
$this->assertEquals('foo', $updatedAttribute->getFrontendLabel());
- // Clean cache
- CacheCleaner::cleanAll();
+ CacheCleaner::clean(['eav']);
$config = Bootstrap::getObjectManager()->create(\Magento\Eav\Model\Config::class);
// Check that attribute data has changed
$updatedAttributeAfterCacheClean = $config->getAttribute($entityType, 'foo');
diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php
index b27e81129e1c3..cb7d288deb75a 100644
--- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php
+++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/Attribute/Frontend/DefaultFrontendTest.php
@@ -55,7 +55,6 @@ class DefaultFrontendTest extends \PHPUnit\Framework\TestCase
protected function setUp(): void
{
- CacheCleaner::cleanAll();
$this->objectManager = Bootstrap::getObjectManager();
$this->defaultFrontend = $this->objectManager->get(DefaultFrontend::class);
diff --git a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeLoaderTest.php b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeLoaderTest.php
index 7a0c66e7e2db2..817c37bca528b 100644
--- a/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeLoaderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Eav/Model/Entity/AttributeLoaderTest.php
@@ -32,7 +32,6 @@ class AttributeLoaderTest extends \PHPUnit\Framework\TestCase
protected function setUp(): void
{
- CacheCleaner::cleanAll();
$this->objectManager = Bootstrap::getObjectManager();
$this->attributeLoader = $this->objectManager->get(AttributeLoader::class);
$entityType = $this->objectManager->create(\Magento\Eav\Model\Entity\Type::class)
diff --git a/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template_new_user_notification.php b/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template_new_user_notification.php
new file mode 100644
index 0000000000000..d742eb1a01414
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Email/Model/_files/email_template_new_user_notification.php
@@ -0,0 +1,21 @@
+create(\Magento\Email\Model\Template::class);
+$template->setOptions(['area' => 'test area', 'store' => 1]);
+$templateText = '{{trans "New User Notification Custom Text %first_name, ' .
+ '%last_name" first_name=$user.firstname last_name=$user.lastname}}';
+$template->setData(
+ [
+ 'template_text' => $templateText,
+ 'template_code' => 'New User Notification Custom Code',
+ 'template_type' => \Magento\Email\Model\Template::TYPE_TEXT,
+ 'orig_template_code' => 'admin_emails_new_user_notification_template'
+ ]
+);
+$template->save();
diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Config/InitialTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Config/InitialTest.php
index 43203d38e308f..2b3ebc217ab7b 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/App/Config/InitialTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/App/Config/InitialTest.php
@@ -24,7 +24,6 @@ protected function setUp(): void
public function testGetMetadata()
{
- CacheCleaner::cleanAll();
$this->assertEquals(
$this->objectManager->create(Config::class)->getMetadata(),
$this->objectManager->create(Config::class)->getMetadata()
@@ -37,7 +36,6 @@ public function testGetMetadata()
*/
public function testGetData($scope)
{
- CacheCleaner::cleanAll();
$this->assertEquals(
$this->objectManager->create(Config::class)->getData($scope),
$this->objectManager->create(Config::class)->getData($scope)
diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/ObjectManager/ConfigLoaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/ObjectManager/ConfigLoaderTest.php
index 1a2b8772c2dc5..ed4e15bd78a0c 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/App/ObjectManager/ConfigLoaderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/App/ObjectManager/ConfigLoaderTest.php
@@ -24,7 +24,6 @@ protected function setUp(): void
public function testLoad()
{
- CacheCleaner::cleanAll();
$data = $this->object->load('global');
$this->assertNotEmpty($data);
$cachedData = $this->object->load('global');
diff --git a/dev/tests/integration/testsuite/Magento/Framework/App/Route/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Framework/App/Route/ConfigTest.php
index ff37b7d847921..d0c8c6083caee 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/App/Route/ConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/App/Route/ConfigTest.php
@@ -28,7 +28,6 @@ protected function setUp(): void
*/
public function testGetRouteFrontName($route, $scope)
{
- CacheCleaner::cleanAll();
$this->assertEquals(
$this->objectManager->create(Config::class)->getRouteFrontName($route, $scope),
$this->objectManager->create(Config::class)->getRouteFrontName($route, $scope)
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
index 857c068efe188..b02ccdb21e687 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
@@ -17,7 +17,9 @@ class ConfigTest extends \PHPUnit\Framework\TestCase
*/
public function testGetTopics()
{
- $topics = $this->getConfigInstance(__DIR__ . '/_files/valid_communication.xml')->getTopics();
+ $topics = $this->getConfigInstance(
+ [__DIR__ . '/_files/valid_communication.xml', __DIR__ . '/_files/valid_communication_extra.xml']
+ )->getTopics();
$expectedParsedTopics = include __DIR__ . '/_files/valid_communication_expected.php';
$this->assertEquals($expectedParsedTopics, $topics);
}
@@ -29,9 +31,11 @@ public function testGetTopics()
public function testGetTopicsNumeric()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Service method specified in the definition of topic "customerDeletedNumbers" is not av');
+ $this->expectExceptionMessage(
+ 'Service method specified in the definition of topic "customerDeletedNumbers" is not av'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/valid_communication_numeric.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/valid_communication_numeric.xml'])->getTopics();
}
// @codingStandardsIgnoreStart
@@ -58,7 +62,7 @@ public function testGetTopicsNumericInvalid()
$this->expectException(\Magento\Framework\Exception\LocalizedException::class);
$this->expectExceptionMessage('The XML in file "0" is invalid:');
- $this->getConfigInstance(__DIR__ . '/_files/invalid_communication_numeric.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/invalid_communication_numeric.xml'])->getTopics();
}
/**
@@ -66,7 +70,9 @@ public function testGetTopicsNumericInvalid()
*/
public function testGetTopic()
{
- $topics = $this->getConfigInstance(__DIR__ . '/_files/valid_communication.xml')->getTopic('customerCreated');
+ $topics = $this->getConfigInstance(
+ [__DIR__ . '/_files/valid_communication.xml', __DIR__ . '/_files/valid_communication_extra.xml']
+ )->getTopic('customerCreated');
$expectedParsedTopics = include __DIR__ . '/_files/valid_communication_expected.php';
$this->assertEquals($expectedParsedTopics['customerCreated'], $topics);
}
@@ -80,7 +86,7 @@ public function testGetTopicInvalidName()
$this->expectException(\Magento\Framework\Exception\LocalizedException::class);
$this->expectExceptionMessage('Topic "invalidTopic" is not configured.');
- $this->getConfigInstance(__DIR__ . '/_files/valid_communication.xml')->getTopic('invalidTopic');
+ $this->getConfigInstance([__DIR__ . '/_files/valid_communication.xml'])->getTopic('invalidTopic');
}
/**
@@ -88,9 +94,11 @@ public function testGetTopicInvalidName()
public function testGetTopicsExceptionMissingRequest()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Either "request" or "schema" attribute must be specified for topic "customerUpdated"');
+ $this->expectExceptionMessage(
+ 'Either "request" or "schema" attribute must be specified for topic "customerUpdated"'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/communication_missing_request.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_missing_request.xml'])->getTopics();
}
/**
@@ -100,7 +108,7 @@ public function testGetTopicsExceptionNotExistingServiceMethod()
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Service method specified in the definition of topic "customerRetrieved" is not');
- $this->getConfigInstance(__DIR__ . '/_files/communication_not_existing_service_method.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_not_existing_service_method.xml'])->getTopics();
}
/**
@@ -110,7 +118,7 @@ public function testGetTopicsExceptionNotExistingService()
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Service method specified in the definition of topic "customerRetrieved" is not');
- $this->getConfigInstance(__DIR__ . '/_files/communication_not_existing_service.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_not_existing_service.xml'])->getTopics();
}
/**
@@ -118,9 +126,11 @@ public function testGetTopicsExceptionNotExistingService()
public function testGetTopicsExceptionNoAttributes()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Either "request" or "schema" attribute must be specified for topic "customerRetrieved"');
+ $this->expectExceptionMessage(
+ 'Either "request" or "schema" attribute must be specified for topic "customerRetrieved"'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/communication_no_attributes.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_no_attributes.xml'])->getTopics();
}
/**
@@ -128,9 +138,11 @@ public function testGetTopicsExceptionNoAttributes()
public function testGetTopicsExceptionInvalidResponseSchema()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Response schema definition for topic "customerUpdated" should reference existing');
+ $this->expectExceptionMessage(
+ 'Response schema definition for topic "customerUpdated" should reference existing'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/communication_response_not_existing_service.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_response_not_existing_service.xml'])->getTopics();
}
/**
@@ -138,9 +150,11 @@ public function testGetTopicsExceptionInvalidResponseSchema()
public function testGetTopicsExceptionInvalidRequestSchema()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Request schema definition for topic "customerUpdated" should reference existing');
+ $this->expectExceptionMessage(
+ 'Request schema definition for topic "customerUpdated" should reference existing'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/communication_request_not_existing_service.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_request_not_existing_service.xml'])->getTopics();
}
/**
@@ -148,9 +162,12 @@ public function testGetTopicsExceptionInvalidRequestSchema()
public function testGetTopicsExceptionMultipleHandlersSynchronousMode()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Topic "customerDeleted" is configured for synchronous requests, that is why it must');
+ $this->expectExceptionMessage(
+ 'Topic "customerDeleted" is configured for synchronous requests, that is why it must'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/communication_multiple_handlers_synchronous_mode.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_multiple_handlers_synchronous_mode.xml'])
+ ->getTopics();
}
/**
@@ -158,9 +175,11 @@ public function testGetTopicsExceptionMultipleHandlersSynchronousMode()
public function testGetTopicsExceptionInvalidHandler()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Service method specified in the definition of handler "customHandler" for topic "custo');
+ $this->expectExceptionMessage(
+ 'Service method specified in the definition of handler "customHandler" for topic "custo'
+ );
- $this->getConfigInstance(__DIR__ . '/_files/communication_not_existing_handler_method.xml')->getTopics();
+ $this->getConfigInstance([__DIR__ . '/_files/communication_not_existing_handler_method.xml'])->getTopics();
}
/**
@@ -168,10 +187,12 @@ public function testGetTopicsExceptionInvalidHandler()
public function testGetTopicsExceptionInvalidTopicNameInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Topic name "customerAdded" and attribute "name" = "customerCreated" must be equal');
+ $this->expectExceptionMessage(
+ 'Topic name "customerAdded" and attribute "name" = "customerCreated" must be equal'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_invalid_topic_name.php'
)->getTopics();
}
@@ -184,7 +205,7 @@ public function testGetTopicsExceptionTopicWithoutDataInEnv()
$this->expectExceptionMessage('Topic "customerCreated" must contain data');
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_topic_without_data.php'
)->getTopics();
}
@@ -197,7 +218,7 @@ public function testGetTopicsExceptionTopicWithMissedKeysInEnv()
$this->expectExceptionMessage('Topic "customerCreated" has missed keys: [response]');
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_topic_with_missed_keys.php'
)->getTopics();
}
@@ -210,7 +231,7 @@ public function testGetTopicsExceptionTopicWithExcessiveKeysInEnv()
$this->expectExceptionMessage('Topic "customerCreated" has excessive keys: [some_incorrect_key]');
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_topic_with_excessive_keys.php'
)->getTopics();
}
@@ -220,10 +241,12 @@ public function testGetTopicsExceptionTopicWithExcessiveKeysInEnv()
public function testGetTopicsExceptionTopicWithNonMatchedNameInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Topic name "customerDeleted" and attribute "name" = "customerRemoved" must be equal');
+ $this->expectExceptionMessage(
+ 'Topic name "customerDeleted" and attribute "name" = "customerRemoved" must be equal'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_with_non_matched_name.php'
)->getTopics();
}
@@ -233,10 +256,12 @@ public function testGetTopicsExceptionTopicWithNonMatchedNameInEnv()
public function testGetTopicsExceptionMultipleHandlersSynchronousModeInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Topic "customerDeleted" is configured for synchronous requests, that is why it must');
+ $this->expectExceptionMessage(
+ 'Topic "customerDeleted" is configured for synchronous requests, that is why it must'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_multiple_handlers_synchronous_mode.php'
)->getTopics();
}
@@ -246,10 +271,12 @@ public function testGetTopicsExceptionMultipleHandlersSynchronousModeInEnv()
public function testGetTopicsExceptionInvalidRequestSchemaInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Request schema definition for topic "customerCreated" should reference existing service');
+ $this->expectExceptionMessage(
+ 'Request schema definition for topic "customerCreated" should reference existing service'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_request_not_existing_service.php'
)->getTopics();
}
@@ -259,10 +286,12 @@ public function testGetTopicsExceptionInvalidRequestSchemaInEnv()
public function testGetTopicsExceptionInvalidResponseSchemaInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Response schema definition for topic "customerCreated" should reference existing type o');
+ $this->expectExceptionMessage(
+ 'Response schema definition for topic "customerCreated" should reference existing type o'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_response_not_existing_service.php'
)->getTopics();
}
@@ -272,10 +301,12 @@ public function testGetTopicsExceptionInvalidResponseSchemaInEnv()
public function testGetTopicsExceptionInvalidMethodInHandlerInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Service method specified in the definition of handler "customerCreatedFirst" for topic');
+ $this->expectExceptionMessage(
+ 'Service method specified in the definition of handler "customerCreatedFirst" for topic'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_not_existing_handler_method.php'
)->getTopics();
}
@@ -285,10 +316,12 @@ public function testGetTopicsExceptionInvalidMethodInHandlerInEnv()
public function testGetTopicsExceptionWithDisabledHandlerInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Disabled handler "default" for topic "customerCreated" cannot be added to the config fi');
+ $this->expectExceptionMessage(
+ 'Disabled handler "default" for topic "customerCreated" cannot be added to the config fi'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_with_disabled_handler.php'
)->getTopics();
}
@@ -298,10 +331,12 @@ public function testGetTopicsExceptionWithDisabledHandlerInEnv()
public function testGetTopicsExceptionIncorrectRequestSchemaTypeInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Request schema type for topic "customerCreated" must be "object_interface" or "service_');
+ $this->expectExceptionMessage(
+ 'Request schema type for topic "customerCreated" must be "object_interface" or "service_'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_incorrect_request_schema_type.php'
)->getTopics();
}
@@ -311,10 +346,12 @@ public function testGetTopicsExceptionIncorrectRequestSchemaTypeInEnv()
public function testGetTopicsExceptionIsNotBooleanTypeOfIsSynchronousInEnv()
{
$this->expectException(\LogicException::class);
- $this->expectExceptionMessage('The attribute "is_synchronous" for topic "customerCreated" should have the value of the');
+ $this->expectExceptionMessage(
+ 'The attribute "is_synchronous" for topic "customerCreated" should have the value of the'
+ );
$this->getConfigInstance(
- __DIR__ . '/_files/valid_communication.xml',
+ [__DIR__ . '/_files/valid_communication.xml'],
__DIR__ . '/_files/communication_is_synchronous_is_not_boolean.php'
)->getTopics();
}
@@ -322,16 +359,20 @@ public function testGetTopicsExceptionIsNotBooleanTypeOfIsSynchronousInEnv()
/**
* Create config instance initialized with configuration from $configFilePath
*
- * @param string $configFilePath
+ * @param array $configFilePaths
* @param string|null $envConfigFilePath
* @return \Magento\Framework\Communication\ConfigInterface
*/
- protected function getConfigInstance($configFilePath, $envConfigFilePath = null)
+ protected function getConfigInstance($configFilePaths, $envConfigFilePath = null)
{
$fileResolver = $this->getMockForAbstractClass(\Magento\Framework\Config\FileResolverInterface::class);
+ $fileResolverResult = [];
+ foreach ($configFilePaths as $configFilePath) {
+ $fileResolverResult[] = file_get_contents($configFilePath);
+ }
$fileResolver->expects($this->any())
->method('get')
- ->willReturn([file_get_contents($configFilePath)]);
+ ->willReturn($fileResolverResult);
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
$xmlReader = $objectManager->create(
\Magento\Framework\Communication\Config\Reader\XmlReader::class,
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_expected.php b/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_expected.php
index e384e779de74d..447197e10808e 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_expected.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_expected.php
@@ -33,6 +33,10 @@
'type' => \Magento\Customer\Api\CustomerRepositoryInterface::class,
'method' => 'delete',
],
+ 'customerCreatedExtra' => [
+ 'type' => \Magento\Customer\Api\CustomerRepositoryInterface::class,
+ 'method' => 'save',
+ ],
'saveNameNotDisabled' => [
'type' => \Magento\Customer\Api\CustomerRepositoryInterface::class,
'method' => 'save',
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_extra.xml b/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_extra.xml
new file mode 100644
index 0000000000000..fad468a5d288a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_extra.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_input.php b/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_input.php
index e099d5d45c877..082984ff15aaf 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_input.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Communication/_files/valid_communication_input.php
@@ -35,6 +35,10 @@
'type' => \Magento\Customer\Api\CustomerRepositoryInterface::class,
'method' => 'delete',
],
+ 'customerCreatedExtra' => [
+ 'type' => \Magento\Customer\Api\CustomerRepositoryInterface::class,
+ 'method' => 'save',
+ ],
'saveNameNotDisabled' => [
'type' => \Magento\Customer\Api\CustomerRepositoryInterface::class,
'method' => 'save',
diff --git a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php
index 6e3391bd8959f..a93e8c198336e 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/DB/Adapter/Pdo/MysqlTest.php
@@ -27,7 +27,6 @@ protected function setUp(): void
set_error_handler(null);
$this->resourceConnection = Bootstrap::getObjectManager()
->get(ResourceConnection::class);
- CacheCleaner::cleanAll();
}
protected function tearDown(): void
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Mview/View/ChangelogTest.php b/dev/tests/integration/testsuite/Magento/Framework/Mview/View/ChangelogTest.php
index ba2225fbe5eac..b77807a11da9b 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Mview/View/ChangelogTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Mview/View/ChangelogTest.php
@@ -6,6 +6,7 @@
namespace Magento\Framework\Mview\View;
use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Mview\View;
/**
* Test Class for \Magento\Framework\Mview\View\Changelog
@@ -123,6 +124,54 @@ public function testClear()
$this->assertEquals(1, $this->model->getVersion()); //the same that a table is empty
}
+ /**
+ * Create entity table for MView
+ *
+ * @param string $tableName
+ * @return void
+ */
+ private function createEntityTable(string $tableName)
+ {
+ $table = $this->resource->getConnection()->newTable(
+ $tableName
+ )->addColumn(
+ 'entity_id',
+ \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
+ null,
+ ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
+ 'Version ID'
+ )->addColumn(
+ 'additional_column',
+ \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
+ null,
+ ['unsigned' => true, 'nullable' => false, 'default' => '0'],
+ 'Entity ID'
+ );
+ $this->resource->getConnection()->createTable($table);
+ }
+
+ public function testAdditionalColumns()
+ {
+ $tableName = 'test_mview_table';
+ $this->createEntityTable($tableName);
+ $view = $this->objectManager->create(View::class);
+ $view->load('test_view_with_additional_columns');
+ $view->subscribe();
+ $this->connection->insert($tableName, ['entity_id' => 12, 'additional_column' => 13]);
+ $select = $this->connection->select()
+ ->from($view->getChangelog()->getName(), ['entity_id', 'test_additional_column']);
+ $actual = $this->connection->fetchAll($select);
+ $this->assertEquals(
+ [
+ 'entity_id' => "12",
+ 'test_additional_column' => "13"
+ ],
+ reset($actual)
+ );
+ $this->connection->dropTable($tableName);
+ $this->connection->dropTable($view->getChangelog()->getName());
+ }
+
/**
* Test for getList() method
*
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Reflection/MethodsMapTest.php b/dev/tests/integration/testsuite/Magento/Framework/Reflection/MethodsMapTest.php
index 585933422edb8..cbe3c552544d4 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Reflection/MethodsMapTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Reflection/MethodsMapTest.php
@@ -24,7 +24,6 @@ protected function setUp(): void
public function testGetMethodsMap()
{
- CacheCleaner::cleanAll();
$data = $this->object->getMethodsMap(\Magento\Framework\Reflection\MethodsMap::class);
$this->assertArrayHasKey('getMethodsMap', $data);
$cachedData = $this->object->getMethodsMap(\Magento\Framework\Reflection\MethodsMap::class);
@@ -33,7 +32,6 @@ public function testGetMethodsMap()
public function testGetMethodParams()
{
- CacheCleaner::cleanAll();
$data = $this->object->getMethodParams(
\Magento\Framework\Reflection\MethodsMap::class,
'getMethodParams'
diff --git a/dev/tests/integration/testsuite/Magento/Framework/TranslateTest.php b/dev/tests/integration/testsuite/Magento/Framework/TranslateTest.php
index 5b48a9169c577..b216831bde33e 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/TranslateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/TranslateTest.php
@@ -94,7 +94,6 @@ protected function setUp(): void
public function testLoadData()
{
$data = $this->translate->loadData(null, true)->getData();
- CacheCleaner::cleanAll();
$this->translate->loadData()->getData();
$dataCached = $this->translate->loadData()->getData();
$this->assertEquals($data, $dataCached);
diff --git a/dev/tests/integration/testsuite/Magento/Framework/View/Element/UiComponent/Config/Provider/TemplateTest.php b/dev/tests/integration/testsuite/Magento/Framework/View/Element/UiComponent/Config/Provider/TemplateTest.php
index f58882ee51493..92a51b85c8c4b 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/View/Element/UiComponent/Config/Provider/TemplateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/View/Element/UiComponent/Config/Provider/TemplateTest.php
@@ -49,7 +49,6 @@ public function testGetTemplate()
\Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea('adminhtml');
$this->objectManager->get(\Magento\Framework\View\DesignInterface::class)
->setDesignTheme('FrameworkViewUiComponent/default');
- CacheCleaner::cleanAll();
$resultOne = $this->model->getTemplate('test.xml');
$resultTwo = $this->model->getTemplate('test.xml');
diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php
new file mode 100644
index 0000000000000..c870fa53c5e39
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php
@@ -0,0 +1,33 @@
+get(QuoteFactory::class);
+/** @var QuoteResource $quoteResource */
+$quoteResource = $objectManager->get(QuoteResource::class);
+/** @var CartRepositoryInterface $cartRepository */
+$cartRepository = $objectManager->get(CartRepositoryInterface::class);
+
+
+/** @var \Magento\GiftMessage\Model\Message $message */
+$message = $objectManager->create(\Magento\GiftMessage\Model\Message::class);
+$message->setSender('Romeo');
+$message->setRecipient('Mercutio');
+$message->setMessage('I thought all for the best.');
+$message->save();
+
+$quote = $quoteFactory->create();
+$quoteResource->load($quote, 'test_quote', 'reserved_order_id');
+$quote->setGiftMessageId($message->getId());
+$cartRepository->save($quote);
diff --git a/dev/tests/integration/testsuite/Magento/Multishipping/Block/Checkout/OverviewTest.php b/dev/tests/integration/testsuite/Magento/Multishipping/Block/Checkout/OverviewTest.php
index 5009f210404d0..182505cba4a61 100644
--- a/dev/tests/integration/testsuite/Magento/Multishipping/Block/Checkout/OverviewTest.php
+++ b/dev/tests/integration/testsuite/Magento/Multishipping/Block/Checkout/OverviewTest.php
@@ -6,28 +6,58 @@
namespace Magento\Multishipping\Block\Checkout;
+use Magento\Catalog\Model\ProductRepository;
+use Magento\Checkout\Block\Cart\Item\Renderer;
+use Magento\Framework\App\Area;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\View\Element\RendererList;
+use Magento\Framework\View\LayoutInterface;
+use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\Quote\Item;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\Xpath;
+use PHPUnit\Framework\TestCase;
+
/**
- * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * Verify default items template
*/
-class OverviewTest extends \PHPUnit\Framework\TestCase
+class OverviewTest extends TestCase
{
/**
- * @var \Magento\Multishipping\Block\Checkout\Overview
+ * @var Overview
+ */
+ private $block;
+
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $objectManager;
+
+ /**
+ * @var Quote
+ */
+ private $quote;
+
+ /**
+ * @var ProductRepository
*/
- protected $_block;
+ private $product;
/**
- * @var \Magento\Framework\ObjectManagerInterface
+ * @var Item
*/
- protected $_objectManager;
+ private $item;
+ /**
+ * @inheritdoc
+ */
protected function setUp(): void
{
- \Magento\TestFramework\Helper\Bootstrap::getInstance()->loadArea(\Magento\Framework\App\Area::AREA_FRONTEND);
- $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $this->_block = $this->_objectManager->get(\Magento\Framework\View\LayoutInterface::class)
+ Bootstrap::getInstance()->loadArea(Area::AREA_FRONTEND);
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)
->createBlock(
- \Magento\Multishipping\Block\Checkout\Overview::class,
+ Overview::class,
'checkout_overview',
[
'data' => [
@@ -37,33 +67,49 @@ protected function setUp(): void
]
);
- $this->_block->addChild('renderer.list', \Magento\Framework\View\Element\RendererList::class);
- $this->_block->getChildBlock(
+ $this->block->addChild('renderer.list', RendererList::class);
+ $this->block->getChildBlock(
'renderer.list'
)->addChild(
'default',
- \Magento\Checkout\Block\Cart\Item\Renderer::class,
+ Renderer::class,
['template' => 'cart/item/default.phtml']
);
+ $this->quote = $this->objectManager->create(Quote::class);
+ $this->product = $this->objectManager->create(ProductRepository::class);
+ $this->item = $this->objectManager->create(Item::class);
}
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ */
public function testGetRowItemHtml()
{
- /** @var $item \Magento\Quote\Model\Quote\Item */
- $item = $this->_objectManager->create(\Magento\Quote\Model\Quote\Item::class);
- /** @var $product \Magento\Catalog\Model\Product */
- $product = $this->_objectManager->create(\Magento\Catalog\Model\Product::class);
- $product->load(1);
- $item->setProduct($product);
- /** @var $quote \Magento\Quote\Model\Quote */
- $quote = $this->_objectManager->create(\Magento\Quote\Model\Quote::class);
- $item->setQuote($quote);
+ $product = $this->product->get('simple');
+ $item = $this->item->setProduct($product);
+ $item->setQuote($this->quote);
// assure that default renderer was obtained
$this->assertEquals(
1,
- \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
+ Xpath::getElementsCountForXpath(
'//*[contains(@class,"product") and contains(@class,"name")]/a',
- $this->_block->getRowItemHtml($item)
+ $this->block->getRowItemHtml($item)
+ )
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/customer_quote_with_items_simple_product_options.php
+ */
+ public function testLinkOptionalProductFileItemHtml()
+ {
+ $quote = $this->quote->load('customer_quote_product_custom_options', 'reserved_order_id');
+ $item = current($quote->getAllItems());
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ '//dd/a[contains(text(), "test.jpg")]',
+ $this->block->getRowItemHtml($item)
)
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php
index 97f59e94d9cfe..719d78b07ca3c 100644
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/Plugin/PluginTest.php
@@ -205,4 +205,22 @@ public function testCustomerWithZeroStoreIdIsSubscribed()
$this->assertEquals($customer->getId(), (int)$subscriber->getCustomerId());
$this->assertEquals($currentStore, (int)$subscriber->getStoreId());
}
+
+ /**
+ * Test get list customer, which have more then 2 subscribes in newsletter_subscriber.
+ *
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Newsletter/_files/subscribers.php
+ */
+ public function testCustomerWithTwoNewsLetterSubscriptions()
+ {
+ /** @var \Magento\Framework\Api\SearchCriteriaBuilder $searchBuilder */
+ $searchBuilder = Bootstrap::getObjectManager()->create(\Magento\Framework\Api\SearchCriteriaBuilder::class);
+ $searchCriteria = $searchBuilder->addFilter('entity_id', 1)->create();
+ $items = $this->customerRepository->getList($searchCriteria)->getItems();
+ /** @var \Magento\Customer\Api\Data\CustomerInterface $customer */
+ $customer = $items[0];
+ $extensionAttributes = $customer->getExtensionAttributes();
+ $this->assertTrue($extensionAttributes->getIsSubscribed());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php b/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php
index 287564722ced6..b28788bc649a1 100644
--- a/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/Model/ObserverTest.php
@@ -93,7 +93,6 @@ public function testProcessPortuguese()
$secondStore = $storeRepository->get('fixture_second_store');
// check if Portuguese language is specified for the second store
- CacheCleaner::cleanAll();
$storeResolver = $this->_objectManager->get(Resolver::class);
$storeResolver->emulate($secondStore->getId());
$this->assertEquals('pt_BR', $storeResolver->getLocale());
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
index 081cae5f98ee5..94cf6ef108474 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php
@@ -737,4 +737,28 @@ private function getCustomerDataArray(): array
CustomerInterface::WEBSITE_ID => 1,
];
}
+
+ /**
+ * @magentoConfigFixture current_store sales/minimum_order/active 1
+ * @magentoConfigFixture current_store sales/minimum_order/amount 5
+ * @magentoConfigFixture current_store sales/minimum_order/tax_including 1
+ * @magentoConfigFixture current_store sales/minimum_order/include_discount_amount 1
+ * @magentoConfigFixture current_store tax/calculation/price_includes_tax 1
+ * @magentoConfigFixture current_store tax/calculation/apply_after_discount 1
+ * @magentoConfigFixture current_store tax/calculation/cross_border_trade_enabled 1
+ * @magentoDataFixture Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition.php
+ * @magentoDataFixture Magento/Tax/_files/tax_rule_region_1_al.php
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_taxable_product_and_customer.php
+ */
+ public function testValidateMinimumAmountWithPriceInclTaxAndDiscount()
+ {
+ /** @var $quote \Magento\Quote\Model\Quote */
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_taxable_product');
+ $quote->setCouponCode('CART_FIXED_DISCOUNT_5');
+ $quote->collectTotals();
+ $this->assertEquals(-5, $quote->getShippingAddress()->getBaseDiscountAmount());
+ $this->assertEquals(9.3, $quote->getShippingAddress()->getBaseSubtotal());
+ $this->assertEquals(5, $quote->getShippingAddress()->getBaseGrandTotal());
+ $this->assertTrue($quote->validateMinimumAmount());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php b/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php
index 9358d761b28b2..ffe2a4d6ca1c5 100644
--- a/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php
+++ b/dev/tests/integration/testsuite/Magento/ReleaseNotification/Controller/Adminhtml/Dashboard/IndexTest.php
@@ -34,6 +34,7 @@ protected function setUp(): void
protected function tearDown(): void
{
$this->objectManager->removeSharedInstance(ContentProviderInterface::class);
+ CacheCleaner::clean(['layout']);
parent::tearDown();
}
@@ -44,7 +45,6 @@ public function testExecute()
{
$content = include __DIR__ . '/../../../_files/validContent.php';
- CacheCleaner::cleanAll();
$this->contentProviderMock->expects($this->any())
->method('getContent')
->willReturn($content);
@@ -62,7 +62,6 @@ public function testExecute()
public function testExecuteEmptyContent()
{
- CacheCleaner::cleanAll();
$this->contentProviderMock->expects($this->any())
->method('getContent')
->willReturn('[]');
@@ -77,7 +76,6 @@ public function testExecuteEmptyContent()
public function testExecuteFalseContent()
{
- CacheCleaner::cleanAll();
$this->contentProviderMock->expects($this->any())
->method('getContent')
->willReturn(false);
diff --git a/dev/tests/integration/testsuite/Magento/Review/Ui/DataProvider/Product/ReviewDataProviderTest.php b/dev/tests/integration/testsuite/Magento/Review/Ui/DataProvider/Product/ReviewDataProviderTest.php
new file mode 100644
index 0000000000000..5cd594a37d0e0
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Review/Ui/DataProvider/Product/ReviewDataProviderTest.php
@@ -0,0 +1,138 @@
+ 'review_listing_data_source',
+ 'primaryFieldName' => 'review_id',
+ 'requestFieldName' => 'entity_id',
+ ];
+
+ /**
+ * @var ObjectManagerInterface
+ */
+ private $objectManager;
+
+ /**
+ * @inheritDoc
+ */
+ protected function setUp(): void
+ {
+ $this->objectManager = Bootstrap::getObjectManager();
+ }
+
+ /**
+ * Sorting dataProvider test
+ *
+ * @magentoDataFixture Magento/Review/_files/different_reviews.php
+ * @dataProvider sortingDataProvider
+ *
+ * @param string $field
+ * @param string $direction
+ * @param array $expectedSortedTitles
+ * @return void
+ */
+ public function testSorting(string $field, string $direction, array $expectedSortedTitles): void
+ {
+ $request = $this->objectManager->create(RequestInterface::class);
+ $request->setParam('current_product_id', 1);
+
+ $dataProvider = $this->objectManager->create(
+ ReviewDataProvider::class,
+ array_merge($this->modelParams, ['request' => $request])
+ );
+ $dataProvider->addOrder($field, $direction);
+ $result = $dataProvider->getData();
+
+ $this->assertEquals($this->getItemsField($result, 'title'), $expectedSortedTitles);
+ }
+
+ /**
+ * Return items field data
+ *
+ * @param array $arrItems
+ * @param string $field
+ * @return array
+ */
+ private function getItemsField(array $arrItems, string $field): array
+ {
+ $data = [];
+ foreach ($arrItems['items'] as $review) {
+ $data[] = $review[$field];
+ }
+
+ return $data;
+ }
+
+ /**
+ * DataProvider for testSorting
+ *
+ * @return array
+ */
+ public function sortingDataProvider(): array
+ {
+ return [
+ 'sort by title field ascending' => [
+ 'title',
+ 'asc',
+ ['1 filter second review', '2 filter first review', 'Review Summary'],
+ ],
+ 'sort by title field descending' => [
+ 'title',
+ 'desc',
+ ['Review Summary', '2 filter first review', '1 filter second review'],
+ ],
+ ];
+ }
+
+ /**
+ * Filter dataProvider test
+ *
+ * @magentoDataFixture Magento/Review/_files/different_reviews.php
+ *
+ * @return void
+ */
+ public function testFilter(): void
+ {
+ $searchTitle = '2 filter first review';
+
+ $request = $this->objectManager->create(RequestInterface::class);
+ $request->setParam('current_product_id', 1);
+
+ /** @var ReviewDataProvider $dataProvider */
+ $dataProvider = $this->objectManager->create(
+ ReviewDataProvider::class,
+ array_merge($this->modelParams, ['request' => $request])
+ );
+
+ /** @var Filter $filter */
+ $filter = $this->objectManager->create(Filter::class);
+ $filter->setField('title')
+ ->setValue($searchTitle);
+
+ $dataProvider->addFilter($filter);
+ $result = $dataProvider->getData();
+
+ $this->assertEquals($this->getItemsField($result, 'title'), [$searchTitle]);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php
index b6aa44bac1c4d..529b491269643 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php
@@ -16,6 +16,7 @@
use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId;
use Magento\TestFramework\TestCase\AbstractBackendController;
+use Magento\Wishlist\Model\Wishlist;
/**
* Class checks create order load block controller.
@@ -201,6 +202,35 @@ public function testMoveFromOrderToShoppingCart(): void
$this->quoteIdsToRemove[] = $customerCart->getId();
}
+ /**
+ * Check that Wishlist item is deleted after it has been added to Order.
+ *
+ * @return void
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist_with_simple_product.php
+ */
+ public function testAddProductToOrderFromWishList(): void
+ {
+ /** @var Wishlist $wishlist */
+ $wishlist = $this->_objectManager->create(Wishlist::class);
+ $wishlistItems = $wishlist->loadByCustomerId(1)->getItemCollection();
+ $this->assertCount(1, $wishlistItems);
+
+ $post = $this->hydratePost([
+ 'sidebar' => [
+ 'add_wishlist_item' => [
+ $wishlistItems->getFirstItem()->getId() => 1,
+ ],
+ ],
+ ]);
+ $params = $this->hydrateParams();
+ $this->dispatchWitParams($params, $post);
+
+ $wishlistItems->clear()->load();
+ $this->assertEmpty($wishlistItems);
+ $quoteItems = $this->session->getQuote()->getItemsCollection();
+ $this->assertCount(1, $quoteItems);
+ }
+
/**
* Check customer quotes
*
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php
index 3c26a53424d81..a96eac375e9f1 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php
@@ -97,15 +97,21 @@ protected function prepareRequest(array $postParams = [], array $params = []): v
* @param array $items
* @param string $commentText
* @param bool $doShipment
+ * @param bool $sendEmail
* @return array
*/
- protected function hydratePost(array $items, string $commentText = '', $doShipment = false): array
- {
+ protected function hydratePost(
+ array $items,
+ string $commentText = '',
+ $doShipment = false,
+ $sendEmail = false
+ ): array {
return [
'invoice' => [
'items' => $items,
'comment_text' => $commentText,
- 'do_shipment' => $doShipment
+ 'do_shipment' => $doShipment,
+ 'send_email' => $sendEmail
],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php
index d00b4c784110c..027fc3d88ee49 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php
@@ -58,6 +58,26 @@ public function testSendEmailOnInvoiceSave(): void
$this->dispatch('backend/sales/order_invoice/save');
$invoice = $this->getInvoiceByOrder($order);
$this->checkSuccess($invoice, 2);
+ $this->assertNull($this->transportBuilder->getSentMessage());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testSendEmailOnInvoiceSaveEmailCopyOfInvoice(): void
+ {
+ $order = $this->getOrder('100000001');
+ $itemId = $order->getItemsCollection()->getFirstItem()->getId();
+ $post = $this->hydratePost([$itemId => 2], "", false, "1");
+ $this->prepareRequest(
+ $post,
+ ['order_id' => $order->getEntityId()]
+ );
+ $this->dispatch('backend/sales/order_invoice/save');
+ $invoice = $this->getInvoiceByOrder($order);
+ $this->checkSuccess($invoice, 2);
$message = $this->transportBuilder->getSentMessage();
$this->assertNotNull($message);
$subject = __('Invoice for your %1 order', $order->getStore()->getFrontendName())->render();
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Report/RuleTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Report/RuleTest.php
new file mode 100644
index 0000000000000..58b82375cfbe9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/ResourceModel/Report/RuleTest.php
@@ -0,0 +1,37 @@
+create(Order::class);
+ $order->loadByIncrementId($orderIncrementId)
+ ->setCouponRuleName($ruleName)
+ ->save();
+ /** @var Rule $reportResource */
+ $reportResource = $objectManager->create(Rule::class);
+ $reportResource->aggregate();
+ $this->assertContains($ruleName, $reportResource->getUniqRulesNamesList());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php
index 4ed096fa4418a..eebf5ea638d05 100644
--- a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php
@@ -14,6 +14,9 @@
use Magento\SalesRule\Model\Coupon;
use Magento\SalesRule\Model\ResourceModel\Coupon\Usage;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
+use Magento\TestFramework\MessageQueue\PreconditionFailedException;
+use Magento\TestFramework\MessageQueue\PublisherConsumerController;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -23,9 +26,20 @@
* @magentoAppArea frontend
* @magentoDbIsolation enabled
* @magentoAppIsolation enabled
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CouponUsagesTest extends TestCase
{
+ /**
+ * @var PublisherConsumerController
+ */
+ private $publisherConsumerController;
+
+ /**
+ * @var array
+ */
+ private $consumers = ['sales.rule.update.coupon.usage'];
+
/**
* @var ObjectManagerInterface
*/
@@ -61,12 +75,42 @@ protected function setUp(): void
$this->couponUsage = $this->objectManager->get(DataObject::class);
$this->quoteManagement = $this->objectManager->get(QuoteManagement::class);
$this->orderService = $this->objectManager->get(OrderService::class);
+
+ $this->publisherConsumerController = Bootstrap::getObjectManager()->create(
+ PublisherConsumerController::class,
+ [
+ 'consumers' => $this->consumers,
+ 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt",
+ 'maxMessages' => 100,
+ 'appInitParams' => Bootstrap::getInstance()->getAppInitParams()
+ ]
+ );
+ try {
+ $this->publisherConsumerController->startConsumers();
+ } catch (EnvironmentPreconditionException $e) {
+ $this->markTestSkipped($e->getMessage());
+ } catch (PreconditionFailedException $e) {
+ $this->fail(
+ $e->getMessage()
+ );
+ }
+ parent::setUp();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->publisherConsumerController->stopConsumers();
+ parent::tearDown();
}
/**
* Test increasing coupon usages after after order placing and decreasing after order cancellation.
*
* @magentoDataFixture Magento/SalesRule/_files/coupons_limited_order.php
+ * @magentoDbIsolation disabled
*/
public function testSubmitQuoteAndCancelOrder()
{
@@ -74,6 +118,8 @@ public function testSubmitQuoteAndCancelOrder()
$couponCode = 'one_usage';
$reservedOrderId = 'test01';
+ $this->publisherConsumerController->startConsumers();
+
/** @var Coupon $coupon */
$coupon = $this->objectManager->get(Coupon::class);
$coupon->loadByCode($couponCode);
@@ -83,6 +129,7 @@ public function testSubmitQuoteAndCancelOrder()
// Make sure coupon usages value is incremented then order is placed.
$order = $this->quoteManagement->submit($quote);
+ sleep(10); // timeout to processing Magento queue
$this->usage->loadByCustomerCoupon($this->couponUsage, $customerId, $coupon->getId());
$coupon->loadByCode($couponCode);
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition.php
new file mode 100644
index 0000000000000..6ce2e0789e478
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition.php
@@ -0,0 +1,49 @@
+get(Collection::class);
+$customerGroupIds = $groupCollection->getAllIds();
+/** @var Rule $salesRule */
+$salesRule = $objectManager->create(Rule::class);
+$salesRule->setData(
+ [
+ 'name' => 'cart_rule_with_coupon_5_off_no_condition',
+ 'is_active' => 1,
+ 'customer_group_ids' => $groupCollection->getAllIds(),
+ 'coupon_type' => Rule::COUPON_TYPE_SPECIFIC,
+ 'simple_action' => Rule::CART_FIXED_ACTION,
+ 'discount_amount' => 5,
+ 'discount_step' => 0,
+ 'stop_rules_processing' => 1,
+ 'website_ids' => [
+ $objectManager->get(StoreManagerInterface::class)->getWebsite()->getId(),
+ ],
+ 'store_labels' => [
+
+ 'store_id' => 0,
+ 'store_label' => 'cart_rule_with_coupon_5_off_no_condition',
+ ]
+ ]
+);
+$objectManager->get(\Magento\SalesRule\Model\ResourceModel\Rule::class)->save($salesRule);
+
+// Create coupon and assign "5$ fixed discount" rule to this coupon.
+$coupon = $objectManager->create(Coupon::class);
+$coupon->setRuleId($salesRule->getId())
+ ->setCode('CART_FIXED_DISCOUNT_5')
+ ->setType(0);
+$objectManager->get(CouponRepositoryInterface::class)->save($coupon);
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition_rollback.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition_rollback.php
new file mode 100644
index 0000000000000..7ecf7a915b1ea
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_with_coupon_5_off_no_condition_rollback.php
@@ -0,0 +1,36 @@
+get(Registry::class);
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class);
+$searchCriteria = $searchCriteriaBuilder->addFilter('name', 'cart_rule_with_coupon_5_off_no_condition')
+ ->create();
+/** @var RuleRepositoryInterface $ruleRepository */
+$ruleRepository = $objectManager->get(RuleRepositoryInterface::class);
+$items = $ruleRepository->getList($searchCriteria)
+ ->getItems();
+/** @var Rule $salesRule */
+$salesRule = array_pop($items);
+if ($salesRule !== null) {
+ $ruleRepository->deleteById($salesRule->getRuleId());
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolverTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolverTest.php
index 53c58cc693a6e..c20b0ed27bf52 100644
--- a/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreResolverTest.php
@@ -28,7 +28,6 @@ public function testGetStoreData()
$storeResolver = $this->objectManager->get(\Magento\Store\Model\StoreResolver::class);
$storesDataRead = $methodReadStoresData->invoke($storeResolver);
- CacheCleaner::cleanAll();
$storesData = $methodGetStoresData->invoke($storeResolver);
$storesDataCached = $methodGetStoresData->invoke($storeResolver);
$this->assertEquals($storesDataRead, $storesData);
diff --git a/dev/tests/integration/testsuite/Magento/Theme/Model/Theme/ThemeProviderTest.php b/dev/tests/integration/testsuite/Magento/Theme/Model/Theme/ThemeProviderTest.php
index 2f3d7065e5339..e2895a478b8d9 100644
--- a/dev/tests/integration/testsuite/Magento/Theme/Model/Theme/ThemeProviderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Theme/Model/Theme/ThemeProviderTest.php
@@ -33,7 +33,6 @@ protected function setUp(): void
$this->themeProviderOne = $objectManager->create(ThemeProvider::class);
$this->themeProviderTwo = clone $this->themeProviderOne;
$this->themeCollection = $objectManager->create(ThemeCollection::class);
- CacheCleaner::clean();
}
public function testGetThemeById()
diff --git a/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php
index 4bc1c5b55ade5..842912546ece8 100644
--- a/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php
+++ b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php
@@ -82,7 +82,6 @@ protected function tearDown(): void
*/
public function testProcess(string $content, string $translation)
{
- CacheCleaner::cleanAll();
$this->assertEquals($translation, $this->model->translate($content));
}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php
index a9ce1d4b181a9..6d635d01b2f48 100644
--- a/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/Model/StoreSwitcher/RewriteUrlTest.php
@@ -65,8 +65,8 @@ protected function setUp(): void
/**
* Test switching stores with non-existent cms pages and then redirecting to the homepage
*
- * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrite.php
* @magentoDataFixture Magento/Catalog/_files/category_product.php
+ * @magentoDataFixture Magento/UrlRewrite/_files/url_rewrite.php
* @magentoDbIsolation disabled
* @magentoAppIsolation enabled
* @return void
diff --git a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php
index 90b1706ed4e22..e20c4a4b60e62 100644
--- a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php
+++ b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php
@@ -7,18 +7,30 @@
namespace Magento\User\Model;
use Magento\Authorization\Model\Role;
+use Magento\Email\Model\ResourceModel\Template\Collection as TemplateCollection;
use Magento\Framework\App\CacheInterface;
+use Magento\Framework\App\Config\MutableScopeConfigInterface;
use Magento\Framework\Encryption\Encryptor;
+use Magento\Framework\Exception\AuthenticationException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\NotFoundException;
+use Magento\Framework\Exception\State\UserLockedException;
+use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Phrase;
use Magento\Framework\Stdlib\DateTime;
+use Magento\TestFramework\Bootstrap as TestFrameworkBootstrap;
+use Magento\TestFramework\Entity;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Mail\Template\TransportBuilderMock;
use Magento\User\Model\User as UserModel;
+use PHPUnit\Framework\TestCase;
/**
* @magentoAppArea adminhtml
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class UserTest extends \PHPUnit\Framework\TestCase
+class UserTest extends TestCase
{
/**
* @var UserModel
@@ -74,12 +86,12 @@ public function testCRUD()
)->setUsername(
'user2'
)->setPassword(
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_PASSWORD
)->setEmail(
'user@magento.com'
);
- $crud = new \Magento\TestFramework\Entity($this->_model, ['firstname' => '_New_name_']);
+ $crud = new Entity($this->_model, ['firstname' => '_New_name_']);
$crud->testCrud();
}
@@ -103,7 +115,7 @@ public function testLoadByUsername()
{
$this->_model->loadByUsername('non_existing_user');
$this->assertNull($this->_model->getId(), 'The admin user has an unexpected ID');
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->assertNotEmpty($this->_model->getId(), 'The admin user should have been loaded');
}
@@ -114,8 +126,8 @@ public function testLoadByUsername()
*/
public function testUpdateRoleOnSave()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
- $this->assertEquals(\Magento\TestFramework\Bootstrap::ADMIN_ROLE_NAME, $this->_model->getRole()->getRoleName());
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
+ $this->assertEquals(TestFrameworkBootstrap::ADMIN_ROLE_NAME, $this->_model->getRole()->getRoleName());
$this->_model->setRoleId(self::$_newRole->getId())->save();
$this->assertEquals('admin_role', $this->_model->getRole()->getRoleName());
}
@@ -137,9 +149,9 @@ public static function roleDataFixture()
*/
public function testSaveExtra()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->_model->saveExtra(['test' => 'val']);
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$extra = $this->_model->getExtra();
$this->assertEquals($extra['test'], 'val');
}
@@ -149,10 +161,10 @@ public function testSaveExtra()
*/
public function testGetRoles()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$roles = $this->_model->getRoles();
$this->assertCount(1, $roles);
- $this->assertEquals(\Magento\TestFramework\Bootstrap::ADMIN_ROLE_NAME, $this->_model->getRole()->getRoleName());
+ $this->assertEquals(TestFrameworkBootstrap::ADMIN_ROLE_NAME, $this->_model->getRole()->getRoleName());
$this->_model->setRoleId(self::$_newRole->getId())->save();
$roles = $this->_model->getRoles();
$this->assertCount(1, $roles);
@@ -164,10 +176,10 @@ public function testGetRoles()
*/
public function testGetRole()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$role = $this->_model->getRole();
$this->assertInstanceOf(Role::class, $role);
- $this->assertEquals(\Magento\TestFramework\Bootstrap::ADMIN_ROLE_NAME, $this->_model->getRole()->getRoleName());
+ $this->assertEquals(TestFrameworkBootstrap::ADMIN_ROLE_NAME, $this->_model->getRole()->getRoleName());
$this->_model->setRoleId(self::$_newRole->getId())->save();
$role = $this->_model->getRole();
$this->assertEquals(self::$_newRole->getId(), $role->getId());
@@ -178,7 +190,7 @@ public function testGetRole()
*/
public function testDeleteFromRole()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$roles = $this->_model->getRoles();
$this->_model->setRoleId(reset($roles))->deleteFromRole();
$role = $this->_model->getRole();
@@ -187,7 +199,7 @@ public function testDeleteFromRole()
public function testRoleUserExists()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$role = $this->_model->getRole();
$this->_model->setRoleId($role->getId());
$this->assertTrue($this->_model->roleUserExists());
@@ -198,16 +210,16 @@ public function testRoleUserExists()
public function testGetCollection()
{
$this->assertInstanceOf(
- \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::class,
+ AbstractCollection::class,
$this->_model->getCollection()
);
}
public function testGetName()
{
- $firstname = \Magento\TestFramework\Bootstrap::ADMIN_FIRSTNAME;
- $lastname = \Magento\TestFramework\Bootstrap::ADMIN_LASTNAME;
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $firstname = TestFrameworkBootstrap::ADMIN_FIRSTNAME;
+ $lastname = TestFrameworkBootstrap::ADMIN_LASTNAME;
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->assertEquals("$firstname $lastname", $this->_model->getName());
$this->assertEquals("$firstname///$lastname", $this->_model->getName('///'));
}
@@ -226,11 +238,11 @@ public function testGetUninitializedAclRole()
*/
public function testAuthenticate()
{
- $this->assertFalse($this->_model->authenticate('User', \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD));
+ $this->assertFalse($this->_model->authenticate('User', TestFrameworkBootstrap::ADMIN_PASSWORD));
$this->assertTrue(
$this->_model->authenticate(
- \Magento\TestFramework\Bootstrap::ADMIN_NAME,
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_NAME,
+ TestFrameworkBootstrap::ADMIN_PASSWORD
)
);
}
@@ -242,11 +254,11 @@ public function testAuthenticate()
*/
public function testAuthenticateCaseInsensitive()
{
- $this->assertTrue($this->_model->authenticate('user', \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD));
+ $this->assertTrue($this->_model->authenticate('user', TestFrameworkBootstrap::ADMIN_PASSWORD));
$this->assertTrue(
$this->_model->authenticate(
- \Magento\TestFramework\Bootstrap::ADMIN_NAME,
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_NAME,
+ TestFrameworkBootstrap::ADMIN_PASSWORD
)
);
}
@@ -256,13 +268,13 @@ public function testAuthenticateCaseInsensitive()
*/
public function testAuthenticateInactiveUser()
{
- $this->expectException(\Magento\Framework\Exception\AuthenticationException::class);
+ $this->expectException(AuthenticationException::class);
$this->_model->load(1);
$this->_model->setIsActive(0)->save();
$this->_model->authenticate(
- \Magento\TestFramework\Bootstrap::ADMIN_NAME,
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_NAME,
+ TestFrameworkBootstrap::ADMIN_PASSWORD
);
}
@@ -272,14 +284,14 @@ public function testAuthenticateInactiveUser()
*/
public function testAuthenticateUserWithoutRole()
{
- $this->expectException(\Magento\Framework\Exception\AuthenticationException::class);
+ $this->expectException(AuthenticationException::class);
$this->_model->loadByUsername('customRoleUser');
$roles = $this->_model->getRoles();
$this->_model->setRoleId(reset($roles))->deleteFromRole();
$this->_model->authenticate(
'customRoleUser',
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_PASSWORD
);
}
@@ -289,13 +301,13 @@ public function testAuthenticateUserWithoutRole()
*/
public function testLoginsAreLogged()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$lognum = $this->_model->getLognum();
$beforeLogin = time();
$this->_model->login(
- \Magento\TestFramework\Bootstrap::ADMIN_NAME,
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_NAME,
+ TestFrameworkBootstrap::ADMIN_PASSWORD
)->reload();
$loginTime = strtotime($this->_model->getLogdate());
@@ -304,8 +316,8 @@ public function testLoginsAreLogged()
$beforeLogin = time();
$this->_model->login(
- \Magento\TestFramework\Bootstrap::ADMIN_NAME,
- \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD
+ TestFrameworkBootstrap::ADMIN_NAME,
+ TestFrameworkBootstrap::ADMIN_PASSWORD
)->reload();
$loginTime = strtotime($this->_model->getLogdate());
$this->assertTrue($beforeLogin <= $loginTime && $loginTime <= time());
@@ -314,11 +326,11 @@ public function testLoginsAreLogged()
public function testReload()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->_model->setFirstname('NewFirstName');
$this->assertEquals('NewFirstName', $this->_model->getFirstname());
$this->_model->reload();
- $this->assertEquals(\Magento\TestFramework\Bootstrap::ADMIN_FIRSTNAME, $this->_model->getFirstname());
+ $this->assertEquals(TestFrameworkBootstrap::ADMIN_FIRSTNAME, $this->_model->getFirstname());
}
/**
@@ -326,7 +338,7 @@ public function testReload()
*/
public function testHasAssigned2Role()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$role = $this->_model->hasAssigned2Role($this->_model);
$this->assertCount(1, $role);
$this->assertArrayHasKey('role_id', $role[0]);
@@ -348,7 +360,7 @@ public function testBeforeSaveRequiredFieldsValidation()
. 'Password is required field.' . PHP_EOL
. 'Invalid type given. String expected' . PHP_EOL
. 'Invalid type given. String, integer or float expected';
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+ $this->expectException(LocalizedException::class);
$this->expectExceptionMessage($expectedMessages);
$this->_model->setSomething('some_value');
@@ -402,7 +414,7 @@ public function testBeforeSavePasswordHash()
*/
public function testBeforeSavePasswordsDoNotMatch()
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+ $this->expectException(LocalizedException::class);
$this->expectExceptionMessage('Your password confirmation must match your password.');
$this->_model->setPassword('password2');
@@ -415,7 +427,7 @@ public function testBeforeSavePasswordsDoNotMatch()
*/
public function testBeforeSavePasswordTooShort()
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+ $this->expectException(LocalizedException::class);
$this->expectExceptionMessage('Your password must include both numeric and alphabetic characters.');
$this->_model->setPassword('123456');
@@ -429,7 +441,7 @@ public function testBeforeSavePasswordTooShort()
*/
public function testBeforeSavePasswordInsecure($password)
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+ $this->expectException(LocalizedException::class);
$this->expectExceptionMessage('Your password must include both numeric and alphabetic characters.');
$this->_model->setPassword($password);
@@ -446,7 +458,7 @@ public function beforeSavePasswordInsecureDataProvider()
*/
public function testBeforeSaveUserIdentityViolation()
{
- $this->expectException(\Magento\Framework\Exception\LocalizedException::class);
+ $this->expectException(LocalizedException::class);
$this->expectExceptionMessage('A user with the same user name or email already exists.');
$this->_model->setUsername('user');
@@ -479,12 +491,12 @@ public function testBeforeSaveValidationSuccess()
*/
public function testChangeResetPasswordLinkToken()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->_model->changeResetPasswordLinkToken('test');
$date = $this->_model->getRpTokenCreatedAt();
$this->assertNotNull($date);
$this->_model->save();
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->assertEquals('test', $this->_model->getRpToken());
$this->assertEquals(strtotime($date), strtotime($this->_model->getRpTokenCreatedAt()));
}
@@ -496,11 +508,11 @@ public function testChangeResetPasswordLinkToken()
*/
public function testIsResetPasswordLinkTokenExpired()
{
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->assertTrue($this->_model->isResetPasswordLinkTokenExpired());
$this->_model->changeResetPasswordLinkToken('test');
$this->_model->save();
- $this->_model->loadByUsername(\Magento\TestFramework\Bootstrap::ADMIN_NAME);
+ $this->_model->loadByUsername(TestFrameworkBootstrap::ADMIN_NAME);
$this->assertFalse($this->_model->isResetPasswordLinkTokenExpired());
$this->_model->setRpTokenCreatedAt($this->_dateTime->formatDate(time() - 60 * 60 * 2 + 2));
$this->assertFalse($this->_model->isResetPasswordLinkTokenExpired());
@@ -526,7 +538,7 @@ public function testGetSetHasAvailableResources()
public function testPerformIdentityCheck()
{
$this->_model->loadByUsername('adminUser');
- $passwordString = \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD;
+ $passwordString = TestFrameworkBootstrap::ADMIN_PASSWORD;
$this->_model->performIdentityCheck($passwordString);
}
@@ -537,7 +549,7 @@ public function testPerformIdentityCheck()
*/
public function testPerformIdentityCheckWrongPassword()
{
- $this->expectException(\Magento\Framework\Exception\AuthenticationException::class);
+ $this->expectException(AuthenticationException::class);
$this->_model->loadByUsername('adminUser');
$passwordString = 'wrongPassword';
@@ -555,14 +567,80 @@ public function testPerformIdentityCheckWrongPassword()
*/
public function testPerformIdentityCheckLockExpires()
{
- $this->expectException(\Magento\Framework\Exception\State\UserLockedException::class);
+ $this->expectException(UserLockedException::class);
$this->_model->loadByUsername('adminUser2');
- $this->_model->performIdentityCheck(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD);
+ $this->_model->performIdentityCheck(TestFrameworkBootstrap::ADMIN_PASSWORD);
$this->expectExceptionMessage(
'The account sign-in was incorrect or your account is disabled temporarily. '
. 'Please wait and try again later.'
);
}
+
+ /**
+ * Verify custom notification is sent when new user created
+ *
+ * @magentoDbIsolation enabled
+ * @magentoDataFixture Magento/Email/Model/_files/email_template_new_user_notification.php
+ */
+ public function testSendNotificationEmailsIfRequired()
+ {
+ /** @var MutableScopeConfigInterface $config */
+ $config = Bootstrap::getObjectManager()
+ ->get(MutableScopeConfigInterface::class);
+ $config->setValue(
+ 'admin/emails/new_user_notification_template',
+ $this->getCustomEmailTemplateIdForNewUserNotification()
+ );
+ $userModel = Bootstrap::getObjectManager()
+ ->create(User::class);
+ $userModel->setFirstname(
+ 'John'
+ )->setLastname(
+ 'Doe'
+ )->setUsername(
+ 'user2'
+ )->setPassword(
+ TestFrameworkBootstrap::ADMIN_PASSWORD
+ )->setEmail(
+ 'user@magento.com'
+ );
+ $userModel->save();
+ $userModel->sendNotificationEmailsIfRequired();
+ /** @var TransportBuilderMock $transportBuilderMock */
+ $transportBuilderMock = Bootstrap::getObjectManager()
+ ->get(TransportBuilderMock::class);
+ $sentMessage = $transportBuilderMock->getSentMessage();
+ $this->assertSame(
+ 'New User Notification Custom Text ' . $userModel->getFirstname() . ', ' . $userModel->getLastname(),
+ $sentMessage->getBodyText()
+ );
+ }
+
+ /**
+ * Return email template id for new user notification
+ *
+ * @return int|null
+ * @throws NotFoundException
+ */
+ private function getCustomEmailTemplateIdForNewUserNotification(): ?int
+ {
+ $templateId = null;
+ $templateCollection = Bootstrap::getObjectManager()
+ ->get(TemplateCollection::class);
+ $origTemplateCode = 'admin_emails_new_user_notification_template';
+ foreach ($templateCollection as $template) {
+ if ($template->getOrigTemplateCode() == $origTemplateCode) {
+ $templateId = (int) $template->getId();
+ }
+ }
+ if ($templateId === null) {
+ throw new NotFoundException(new Phrase(
+ 'Customized %templateCode% email template not found',
+ ['templateCode' => $origTemplateCode]
+ ));
+ }
+ return $templateId;
+ }
}
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js
index 11af4cdc219a9..87fb61873b653 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js
@@ -37,5 +37,15 @@ define([
searchObj.updatePreview();
expect(searchObj.updatePreview).toHaveBeenCalled();
});
+ it('set the proper keywordUpdated value on new search keyword', function () {
+ searchObj.value = 'keyword 1';
+ expect(searchObj.keywordUpdated).toEqual(false);
+ searchObj.apply('keyword 2');
+ expect(searchObj.keywordUpdated).toEqual(true);
+ searchObj.apply('keyword 2');
+ expect(searchObj.keywordUpdated).toEqual(false);
+ searchObj.apply('keyword 3');
+ expect(searchObj.keywordUpdated).toEqual(true);
+ });
});
});
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php
index 1c0f451de71dc..f57a29e9bda0d 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php
@@ -266,8 +266,10 @@ private function assertAutoloadRegistrar(\StdClass $json, $dir)
*/
private function assertNoVersionSpecified(\StdClass $json)
{
- $errorMessage = 'Version must not be specified in the root and package composer JSON files in Git';
- $this->assertObjectNotHasAttribute('version', $json, $errorMessage);
+ if (!in_array($json->name, self::$rootComposerModuleBlacklist)) {
+ $errorMessage = 'Version must not be specified in the root and package composer JSON files in Git';
+ $this->assertObjectNotHasAttribute('version', $json, $errorMessage);
+ }
}
/**
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
index aa1f8b1c259a4..65358cd785066 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
@@ -209,14 +209,14 @@ private function getFullWhitelist()
}
/**
- * Retrieves the lowest PHP version specified in composer.json of project.
+ * Retrieves the lowest and highest PHP version specified in composer.json of project.
*
- * @return string
+ * @return array
*/
- private function getLowestPhpVersion(): string
+ private function getTargetPhpVersions(): array
{
$composerJson = json_decode(file_get_contents(BP . '/composer.json'), true);
- $phpVersion = '7.0';
+ $versionsRange = [];
if (isset($composerJson['require']['php'])) {
$versions = explode('||', $composerJson['require']['php']);
@@ -232,12 +232,17 @@ private function getLowestPhpVersion(): string
//sort versions
usort($versions, 'version_compare');
- $lowestVersion = array_shift($versions);
- $versionParts = explode('.', $lowestVersion);
- $phpVersion = sprintf('%s.%s', $versionParts[0], $versionParts[1] ?? '0');
+ $versionsRange[] = array_shift($versions);
+ if (!empty($versions)) {
+ $versionsRange[] = array_pop($versions);
+ }
+ foreach ($versionsRange as $key => $version) {
+ $versionParts = explode('.', $versionsRange[$key]);
+ $versionsRange[$key] = sprintf('%s.%s', $versionParts[0], $versionParts[1] ?? '0');
+ }
}
- return $phpVersion;
+ return $versionsRange;
}
/**
@@ -408,7 +413,8 @@ public function testStrictTypes()
*/
public function testPhpCompatibility()
{
- $targetVersion = $this->getLowestPhpVersion();
+ $targetVersions = $this->getTargetPhpVersions();
+ $this->assertNotEmpty($targetVersions, 'No supported versions information in composer.json');
$reportFile = self::$reportDir . '/phpcompatibility_report.txt';
$rulesetDir = __DIR__ . '/_files/PHPCompatibilityMagento';
@@ -417,7 +423,11 @@ public function testPhpCompatibility()
}
$codeSniffer = new PhpCompatibility($rulesetDir, $reportFile, new Wrapper());
- $codeSniffer->setTestVersion($targetVersion);
+ if (count($targetVersions) > 1) {
+ $codeSniffer->setTestVersion($targetVersions[0] . '-' . $targetVersions[1]);
+ } else {
+ $codeSniffer->setTestVersion($targetVersions[0]);
+ }
$result = $codeSniffer->run(
$this->isFullScan() ? $this->getFullWhitelist() : self::getWhitelist(['php', 'phtml'])
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt
index 8ea162d3b1394..6b0007f58830b 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt
@@ -9,6 +9,7 @@ lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/Interceptor
lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php
dev/tests/integration/framework/deployTestModules.php
dev/tests/integration/testsuite/Magento/Framework/Code/Generator/AutoloaderTest.php
+dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/SimpleDirectiveTest.php
dev/tests/integration/testsuite/Magento/Framework/Session/ConfigTest.php
dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon
index d487fbe602acf..0bdb5861dbf33 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon
+++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/phpstan.neon
@@ -1,41 +1,41 @@
parameters:
- checkExplicitMixedMissingReturn: true
- checkPhpDocMissingReturn: true
- reportUnmatchedIgnoredErrors: false
- excludes_analyse:
- - %rootDir%/../../../lib/internal/Magento/Framework/ObjectManager/Test/Unit/*
- - %rootDir%/../../../*/_files/*
- - %rootDir%/../../../dev/tests/*/Fixtures/*
- - %rootDir%/../../../dev/tests/*/tmp/*
- - %rootDir%/../../../dev/tests/*/_generated/*
- - %rootDir%/../../../pub/*
- autoload_directories:
- - %rootDir%/../../../dev/tests/static/framework/tests/unit/testsuite/Magento
- - %rootDir%/../../../dev/tests/integration/framework/tests/unit/testsuite/Magento
- - %rootDir%/../../../dev/tests/api-functional/_files/Magento
- autoload_files:
- - %rootDir%/../../../dev/tests/static/framework/autoload.php
- - %rootDir%/../../../dev/tests/integration/framework/autoload.php
- - %rootDir%/../../../dev/tests/api-functional/framework/autoload.php
- - %rootDir%/../../../dev/tests/setup-integration/framework/autoload.php
- - %rootDir%/../../../dev/tests/static/framework/Magento/PhpStan/autoload.php
- ignoreErrors:
- # Ignore PHPStan\Rules\Classes\UnusedConstructorParametersRule
- - '#Constructor of class [a-zA-Z0-9\\_]+ has an unused parameter#'
- # Ignore setCustomErrorHandler function not found in bootstrap files
- - '#Function setCustomErrorHandler not found#'
- # Ignore 'return statement is missing' error when 'void' is present in return type list
- - '#Method (?:.*?) should return (?:.*?)void(?:.*?) but return statement is missing.#'
- # Ignore constants, defined dynamically.
- - '#Constant TESTS_WEB_API_ADAPTER not found.#'
- - '#Constant TESTS_BASE_URL not found.#'
- - '#Constant TESTS_XDEBUG_ENABLED not found.#'
- - '#Constant TESTS_XDEBUG_SESSION not found.#'
- - '#Constant INTEGRATION_TESTS_DIR not found.#'
- - '#Constant MAGENTO_MODULES_PATH not found.#'
- - '#Constant TESTS_MODULES_PATH not found.#'
- - '#Constant TESTS_INSTALLATION_DB_CONFIG_FILE not found.#'
- - '#Constant T_[A-Z_]+ not found.#'
+ checkExplicitMixedMissingReturn: true
+ checkPhpDocMissingReturn: true
+ reportUnmatchedIgnoredErrors: false
+ excludes_analyse:
+ - %rootDir%/../../../lib/internal/Magento/Framework/ObjectManager/Test/Unit/*
+ - %rootDir%/../../../*/_files/*
+ - %rootDir%/../../../dev/tests/*/Fixtures/*
+ - %rootDir%/../../../dev/tests/*/tmp/*
+ - %rootDir%/../../../dev/tests/*/_generated/*
+ - %rootDir%/../../../pub/*
+ autoload_directories:
+ - %rootDir%/../../../dev/tests/static/framework/tests/unit/testsuite/Magento
+ - %rootDir%/../../../dev/tests/integration/framework/tests/unit/testsuite/Magento
+ - %rootDir%/../../../dev/tests/api-functional/_files/Magento
+ autoload_files:
+ - %rootDir%/../../../dev/tests/static/framework/autoload.php
+ - %rootDir%/../../../dev/tests/integration/framework/autoload.php
+ - %rootDir%/../../../dev/tests/api-functional/framework/autoload.php
+ - %rootDir%/../../../dev/tests/setup-integration/framework/autoload.php
+ - %rootDir%/../../../dev/tests/static/framework/Magento/PhpStan/autoload.php
+ ignoreErrors:
+ # Ignore PHPStan\Rules\Classes\UnusedConstructorParametersRule
+ - '#Constructor of class [a-zA-Z0-9\\_]+ has an unused parameter#'
+ # Ignore setCustomErrorHandler function not found in bootstrap files
+ - '#Function setCustomErrorHandler not found#'
+ # Ignore 'return statement is missing' error when 'void' is present in return type list
+ - '#Method (?:.*?) should return (?:.*?)void(?:.*?) but return statement is missing.#'
+ # Ignore constants, defined dynamically.
+ - '#Constant TESTS_WEB_API_ADAPTER not found.#'
+ - '#Constant TESTS_BASE_URL not found.#'
+ - '#Constant TESTS_XDEBUG_ENABLED not found.#'
+ - '#Constant TESTS_XDEBUG_SESSION not found.#'
+ - '#Constant INTEGRATION_TESTS_DIR not found.#'
+ - '#Constant MAGENTO_MODULES_PATH not found.#'
+ - '#Constant TESTS_MODULES_PATH not found.#'
+ - '#Constant TESTS_INSTALLATION_DB_CONFIG_FILE not found.#'
+ - '#Constant T_[A-Z_]+ not found.#'
services:
diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php
index 93d5535d0e10e..83f292cfdf703 100644
--- a/lib/internal/Magento/Framework/App/Bootstrap.php
+++ b/lib/internal/Magento/Framework/App/Bootstrap.php
@@ -13,6 +13,7 @@
use Magento\Framework\Autoload\Populator;
use Magento\Framework\Config\File\ConfigFilePool;
use Magento\Framework\Filesystem\DriverPool;
+use Magento\Framework\HTTP\PhpEnvironment\Response;
use Psr\Log\LoggerInterface;
/**
@@ -386,7 +387,7 @@ private function initErrorHandler()
$handler = new ErrorHandler();
set_error_handler([$handler, 'handler']);
}
-
+
/**
* Getter for error code
*
@@ -428,9 +429,13 @@ public function isDeveloperMode()
*/
protected function terminate(\Throwable $e)
{
-
+ /** @var Response $response */
+ $response = $this->objectManager->get(Response::class);
+ $response->clearHeaders();
+ $response->setHttpResponseCode(500);
+ $response->setHeader('Content-Type', 'text/plain');
if ($this->isDeveloperMode()) {
- echo $e;
+ $response->setBody($e);
} else {
$message = "An error has happened during application run. See exception log for details.\n";
try {
@@ -441,8 +446,9 @@ protected function terminate(\Throwable $e)
} catch (\Exception $e) {
$message .= "Could not write error message to log. Please use developer mode to see the message.\n";
}
- echo $message;
+ $response->setBody($message);
}
+ $response->sendResponse();
exit(1);
}
// phpcs:enable
diff --git a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader.php b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader.php
deleted file mode 100644
index 3c5e9ca2539dc..0000000000000
--- a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader.php
+++ /dev/null
@@ -1,53 +0,0 @@
- 'name'
- ];
-
- /**
- * @param \Magento\Framework\Config\FileResolverInterface $fileResolver
- * @param \Magento\Framework\Communication\Config\Reader\XmlReader\Converter $converter
- * @param \Magento\Framework\Communication\Config\Reader\XmlReader\SchemaLocator $schemaLocator
- * @param \Magento\Framework\Config\ValidationStateInterface $validationState
- * @param string $fileName
- * @param array $idAttributes
- * @param string $domDocumentClass
- * @param string $defaultScope
- */
- public function __construct(
- \Magento\Framework\Config\FileResolverInterface $fileResolver,
- XmlReader\Converter $converter,
- XmlReader\SchemaLocator $schemaLocator,
- \Magento\Framework\Config\ValidationStateInterface $validationState,
- $fileName = 'communication.xml',
- $idAttributes = [],
- $domDocumentClass = \Magento\Framework\Config\Dom::class,
- $defaultScope = 'global'
- ) {
- parent::__construct(
- $fileResolver,
- $converter,
- $schemaLocator,
- $validationState,
- $fileName,
- $idAttributes,
- $domDocumentClass,
- $defaultScope
- );
- }
-}
diff --git a/lib/internal/Magento/Framework/Mview/Config/Converter.php b/lib/internal/Magento/Framework/Mview/Config/Converter.php
index 5c33ac150d00a..988cffa8b7ce2 100644
--- a/lib/internal/Magento/Framework/Mview/Config/Converter.php
+++ b/lib/internal/Magento/Framework/Mview/Config/Converter.php
@@ -5,10 +5,34 @@
*/
namespace Magento\Framework\Mview\Config;
+use Magento\Framework\Mview\View\AdditionalColumnsProcessor\DefaultProcessor;
+use Magento\Framework\Mview\View\ChangeLogBatchWalker;
use Magento\Framework\Mview\View\SubscriptionInterface;
class Converter implements \Magento\Framework\Config\ConverterInterface
{
+ /**
+ * @var string
+ */
+ private $defaultProcessor;
+
+ /**
+ * @var string
+ */
+ private $defaultIterator;
+
+ /**
+ * @param string $defaultProcessor
+ * @param string $defaultIterator
+ */
+ public function __construct(
+ string $defaultProcessor = DefaultProcessor::class,
+ string $defaultIterator = ChangeLogBatchWalker::class
+ ) {
+ $this->defaultProcessor = $defaultProcessor;
+ $this->defaultIterator = $defaultIterator;
+ }
+
/**
* Convert dom node tree to array
*
@@ -28,6 +52,7 @@ public function convert($source)
$data['view_id'] = $viewId;
$data['action_class'] = $this->getAttributeValue($viewNode, 'class');
$data['group'] = $this->getAttributeValue($viewNode, 'group');
+ $data['walker'] = $this->getAttributeValue($viewNode, 'walker') ?: $this->defaultIterator;
$data['subscriptions'] = [];
/** @var $childNode \DOMNode */
@@ -76,6 +101,7 @@ protected function convertChild(\DOMNode $childNode, $data)
$name = $this->getAttributeValue($subscription, 'name');
$column = $this->getAttributeValue($subscription, 'entity_column');
$subscriptionModel = $this->getAttributeValue($subscription, 'subscription_model');
+
if (!empty($subscriptionModel)
&& !in_array(
SubscriptionInterface::class,
@@ -89,11 +115,44 @@ class_implements(ltrim($subscriptionModel, '\\'))
$data['subscriptions'][$name] = [
'name' => $name,
'column' => $column,
- 'subscription_model' => $subscriptionModel
+ 'subscription_model' => $subscriptionModel,
+ 'additional_columns' => $this->getAdditionalColumns($subscription),
+ 'processor' => $this->getAttributeValue($subscription, 'processor')
+ ?: $this->defaultProcessor
];
}
break;
}
return $data;
}
+
+ /**
+ * Retrieve additional columns of subscription table
+ *
+ * @param \DOMNode $subscription
+ * @return array
+ */
+ private function getAdditionalColumns(\DOMNode $subscription): array
+ {
+ $additionalColumns = [];
+ foreach ($subscription->childNodes as $childNode) {
+ if ($childNode->nodeType != XML_ELEMENT_NODE || $childNode->nodeName != 'additionalColumns') {
+ continue;
+ }
+
+ foreach ($childNode->childNodes as $columnNode) {
+ if ($columnNode->nodeName !== 'column') {
+ continue;
+ }
+
+ $additionalColumns[$this->getAttributeValue($columnNode, 'name')] = [
+ 'name' => $this->getAttributeValue($columnNode, 'name'),
+ 'cl_name' => $this->getAttributeValue($columnNode, 'cl_name'),
+ 'constant' => $this->getAttributeValue($columnNode, 'constant'),
+ ];
+ }
+ }
+
+ return $additionalColumns;
+ }
}
diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php
index 0a4e8a3840749..16fc3a91924b8 100644
--- a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php
+++ b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php
@@ -11,6 +11,8 @@
use Magento\Framework\DB\Adapter\Pdo\Mysql;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\DB\Select;
+use Magento\Framework\Mview\Config;
+use Magento\Framework\Mview\View\AdditionalColumnsProcessor\ProcessorFactory;
use Magento\Framework\Mview\View\Changelog;
use Magento\Framework\Mview\View\ChangelogInterface;
use PHPUnit\Framework\MockObject\MockObject;
@@ -40,13 +42,33 @@ class ChangelogTest extends TestCase
*/
protected $resourceMock;
+ /**
+ * @var ProcessorFactory|MockObject
+ */
+ protected $processorFactory;
+
protected function setUp(): void
{
$this->connectionMock = $this->createMock(Mysql::class);
$this->resourceMock = $this->createMock(ResourceConnection::class);
$this->mockGetConnection($this->connectionMock);
+ $this->processorFactory = $this->createMock(ProcessorFactory::class);
+
+ $this->model = new Changelog($this->resourceMock, $this->getMviewConfigMock(), $this->processorFactory);
+ }
- $this->model = new Changelog($this->resourceMock);
+ /**
+ * @return Config|MockObject
+ */
+ private function getMviewConfigMock()
+ {
+ $mviewConfigMock = $this->createMock(Config::class);
+ $mviewConfigMock->expects($this->any())
+ ->method('getView')
+ ->willReturn([
+ 'subscriptions' => []
+ ]);
+ return $mviewConfigMock;
}
public function testInstanceOf()
@@ -54,7 +76,7 @@ public function testInstanceOf()
$resourceMock =
$this->createMock(ResourceConnection::class);
$resourceMock->expects($this->once())->method('getConnection')->willReturn(true);
- $model = new Changelog($resourceMock);
+ $model = new Changelog($resourceMock, $this->getMviewConfigMock(), $this->processorFactory);
$this->assertInstanceOf(ChangelogInterface::class, $model);
}
@@ -65,7 +87,7 @@ public function testCheckConnectionException()
$resourceMock =
$this->createMock(ResourceConnection::class);
$resourceMock->expects($this->once())->method('getConnection')->willReturn(null);
- $model = new Changelog($resourceMock);
+ $model = new Changelog($resourceMock, $this->getMviewConfigMock(), $this->processorFactory);
$model->setViewId('ViewIdTest');
$this->assertNull($model);
}
diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php
index 697071d0d3067..3f3c326beb5a5 100644
--- a/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php
+++ b/lib/internal/Magento/Framework/Mview/Test/Unit/View/SubscriptionTest.php
@@ -7,10 +7,13 @@
namespace Magento\Framework\Mview\Test\Unit\View;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\Pdo\Mysql;
use Magento\Framework\DB\Ddl\Trigger;
use Magento\Framework\DB\Ddl\TriggerFactory;
+use Magento\Framework\Mview\Config;
+use Magento\Framework\Mview\View\AdditionalColumnsProcessor\DefaultProcessor;
use Magento\Framework\Mview\View\ChangelogInterface;
use Magento\Framework\Mview\View\CollectionInterface;
use Magento\Framework\Mview\View\StateInterface;
@@ -46,8 +49,19 @@ class SubscriptionTest extends TestCase
/** @var string */
private $tableName;
+ /**
+ * @var Config|MockObject
+ */
+ private $mviewConfig;
+
+ /**
+ * @var DefaultProcessor|MockObject
+ */
+ private $defaultProcessor;
+
protected function setUp(): void
{
+ $this->tableName = 'test_table';
$this->connectionMock = $this->createMock(Mysql::class);
$this->resourceMock = $this->createMock(ResourceConnection::class);
@@ -55,10 +69,14 @@ protected function setUp(): void
->method('quoteIdentifier')
->willReturnArgument(0);
+ $this->defaultProcessor = $this->createMock(DefaultProcessor::class);
$this->resourceMock->expects($this->atLeastOnce())
->method('getConnection')
->willReturn($this->connectionMock);
-
+ ObjectManager::getInstance()->expects($this->any())
+ ->method('get')
+ ->with(DefaultProcessor::class)
+ ->willReturn($this->defaultProcessor);
$this->triggerFactoryMock = $this->createMock(TriggerFactory::class);
$this->viewCollectionMock = $this->getMockForAbstractClass(
CollectionInterface::class,
@@ -78,18 +96,33 @@ protected function setUp(): void
true,
[]
);
-
+ $this->viewMock->expects($this->any())
+ ->method('getId')
+ ->willReturn(1);
$this->resourceMock->expects($this->any())
->method('getTableName')
->willReturnArgument(0);
-
+ $mviewConfigMock = $this->createMock(Config::class);
+ $mviewConfigMock->expects($this->any())
+ ->method('getView')
+ ->willReturn([
+ 'subscriptions' => [
+ $this->tableName => [
+ 'processor' => DefaultProcessor::class
+ ]
+ ]
+ ]);
+ $this->mviewConfig = $mviewConfigMock;
$this->model = new Subscription(
$this->resourceMock,
$this->triggerFactoryMock,
$this->viewCollectionMock,
$this->viewMock,
$this->tableName,
- 'columnName'
+ 'columnName',
+ [],
+ [],
+ $mviewConfigMock
);
}
@@ -122,7 +155,7 @@ public function testCreate()
$triggerMock->expects($this->exactly(3))
->method('setName')
->with($triggerName)->willReturnSelf();
- $triggerMock->expects($this->exactly(3))
+ $triggerMock->expects($this->any())
->method('getName')
->willReturn('triggerName');
$triggerMock->expects($this->exactly(3))
@@ -167,7 +200,7 @@ public function testCreate()
true,
[]
);
- $changelogMock->expects($this->exactly(3))
+ $changelogMock->expects($this->any())
->method('getName')
->willReturn('test_view_cl');
$changelogMock->expects($this->exactly(3))
@@ -191,7 +224,7 @@ public function testCreate()
true,
[]
);
- $otherChangelogMock->expects($this->exactly(3))
+ $otherChangelogMock->expects($this->any())
->method('getName')
->willReturn('other_test_view_cl');
$otherChangelogMock->expects($this->exactly(3))
@@ -217,7 +250,7 @@ public function testCreate()
->method('getChangelog')
->willReturn($otherChangelogMock);
- $this->viewMock->expects($this->exactly(3))
+ $this->viewMock->expects($this->any())
->method('getId')
->willReturn('this_id');
$this->viewMock->expects($this->never())
@@ -235,7 +268,6 @@ public function testCreate()
$this->connectionMock->expects($this->exactly(3))
->method('createTrigger')
->with($triggerMock);
-
$this->model->create();
}
@@ -244,7 +276,7 @@ public function testRemove()
$triggerMock = $this->createMock(Trigger::class);
$triggerMock->expects($this->exactly(3))
->method('setName')->willReturnSelf();
- $triggerMock->expects($this->exactly(3))
+ $triggerMock->expects($this->any())
->method('getName')
->willReturn('triggerName');
$triggerMock->expects($this->exactly(3))
@@ -271,7 +303,7 @@ public function testRemove()
true,
[]
);
- $otherChangelogMock->expects($this->exactly(3))
+ $otherChangelogMock->expects($this->any())
->method('getName')
->willReturn('other_test_view_cl');
$otherChangelogMock->expects($this->exactly(3))
@@ -297,7 +329,7 @@ public function testRemove()
->method('getChangelog')
->willReturn($otherChangelogMock);
- $this->viewMock->expects($this->exactly(3))
+ $this->viewMock->expects($this->any())
->method('getId')
->willReturn('this_id');
$this->viewMock->expects($this->never())
@@ -343,12 +375,24 @@ public function testBuildStatementIgnoredColumnSubscriptionLevel(): void
]
]
];
+ $mviewConfigMock = $this->createMock(Config::class);
+ $mviewConfigMock->expects($this->any())
+ ->method('getView')
+ ->willReturn([
+ 'subscriptions' => [
+ $tableName => [
+ 'processor' => DefaultProcessor::class
+ ]
+ ]
+ ]);
- $this->connectionMock->expects($this->once())
+ $this->connectionMock->expects($this->any())
->method('isTableExists')
+ ->with('cataloginventory_stock_item')
->willReturn(true);
- $this->connectionMock->expects($this->once())
+ $this->connectionMock->expects($this->any())
->method('describeTable')
+ ->with($tableName)
->willReturn([
'item_id' => ['COLUMN_NAME' => 'item_id'],
'product_id' => ['COLUMN_NAME' => 'product_id'],
@@ -359,10 +403,14 @@ public function testBuildStatementIgnoredColumnSubscriptionLevel(): void
]);
$otherChangelogMock = $this->getMockForAbstractClass(ChangelogInterface::class);
- $otherChangelogMock->expects($this->once())
+ $otherChangelogMock->expects($this->any())
->method('getViewId')
->willReturn($viewId);
+ $otherChangelogMock->expects($this->once())
+ ->method('getColumnName')
+ ->willReturn('entity_id');
+
$model = new Subscription(
$this->resourceMock,
$this->triggerFactoryMock,
@@ -371,7 +419,8 @@ public function testBuildStatementIgnoredColumnSubscriptionLevel(): void
$tableName,
'columnName',
[],
- $ignoredData
+ $ignoredData,
+ $mviewConfigMock
);
$method = new \ReflectionMethod($model, 'buildStatement');
diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php
index 7d69ff43f158b..8d460ac99a46b 100644
--- a/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php
+++ b/lib/internal/Magento/Framework/Mview/Test/Unit/ViewTest.php
@@ -7,6 +7,7 @@
namespace Magento\Framework\Mview\Test\Unit;
+use Laminas\Log\Filter\Mock;
use Magento\Framework\Mview\ActionFactory;
use Magento\Framework\Mview\ActionInterface;
use Magento\Framework\Mview\ConfigInterface;
@@ -53,6 +54,11 @@ class ViewTest extends TestCase
*/
protected $subscriptionFactoryMock;
+ /**
+ * @var MockObject|View\ChangeLogBatchIteratorInterface
+ */
+ private $iteratorMock;
+
/**
* @inheritdoc
*/
@@ -67,6 +73,7 @@ protected function setUp(): void
true,
['getView']
);
+ $this->iteratorMock = $this->createMock(View\ChangeLogBatchIteratorInterface::class);
$this->actionFactoryMock = $this->createPartialMock(ActionFactory::class, ['get']);
$this->stateMock = $this->createPartialMock(
State::class,
@@ -97,7 +104,10 @@ protected function setUp(): void
$this->actionFactoryMock,
$this->stateMock,
$this->changelogMock,
- $this->subscriptionFactoryMock
+ $this->subscriptionFactoryMock,
+ [],
+ [],
+ $this->iteratorMock
);
}
@@ -334,7 +344,7 @@ public function testUpdate()
$currentVersionId
);
$this->changelogMock->expects(
- $this->once()
+ $this->any()
)->method(
'getList'
)->with(
@@ -345,6 +355,7 @@ public function testUpdate()
);
$actionMock = $this->getMockForAbstractClass(ActionInterface::class);
+ $this->iteratorMock->expects($this->once())->method('walk')->willReturn($listId);
$actionMock->expects($this->once())->method('execute')->with($listId)->willReturnSelf();
$this->actionFactoryMock->expects(
$this->once()
@@ -390,7 +401,7 @@ public function testUpdateEx(): void
->expects($this->once())
->method('getVersion')
->willReturn($currentVersionId);
-
+ $this->iteratorMock->expects($this->any())->method('walk')->willReturn($this->generateChangeLog(150, 1, 150));
$this->changelogMock->method('getList')
->willReturnMap(
[
@@ -401,7 +412,7 @@ public function testUpdateEx(): void
);
$actionMock = $this->getMockForAbstractClass(ActionInterface::class);
- $actionMock->expects($this->once())
+ $actionMock->expects($this->any())
->method('execute')
->with($this->generateChangeLog(150, 1, 150))
->willReturnSelf();
@@ -457,7 +468,7 @@ public function testUpdateWithException()
$this->stateMock->expects($this->atLeastOnce())
->method('getMode')
->willReturn(StateInterface::MODE_ENABLED);
- $this->stateMock->expects($this->exactly(2))
+ $this->stateMock->expects($this->any())
->method('getStatus')
->willReturn(StateInterface::STATUS_IDLE);
$this->stateMock->expects($this->exactly(2))
@@ -472,16 +483,9 @@ public function testUpdateWithException()
)->willReturn(
$currentVersionId
);
- $this->changelogMock->expects(
- $this->once()
- )->method(
- 'getList'
- )->with(
- $lastVersionId,
- $currentVersionId
- )->willReturn(
- $listId
- );
+ $this->iteratorMock->expects($this->any())
+ ->method('walk')
+ ->willReturn([2,3]);
$actionMock = $this->createPartialMock(ActionInterface::class, ['execute']);
$actionMock->expects($this->once())->method('execute')->with($listId)->willReturnCallback(
@@ -767,8 +771,11 @@ public function testGetUpdated()
protected function loadView()
{
$viewId = 'view_test';
+ $this->changelogMock->expects($this->any())
+ ->method('getViewId')
+ ->willReturn($viewId);
$this->configMock->expects(
- $this->once()
+ $this->any()
)->method(
'getView'
)->with(
@@ -788,7 +795,7 @@ protected function getViewData()
'view_id' => 'view_test',
'action_class' => 'Some\Class\Name',
'group' => 'some_group',
- 'subscriptions' => ['some_entity' => ['name' => 'some_entity', 'column' => 'entity_id']]
+ 'subscriptions' => ['some_entity' => ['name' => 'some_entity', 'column' => 'entity_id']],
];
}
}
diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_config.php b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_config.php
index a19f90546bac3..af488ecf5589e 100644
--- a/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_config.php
+++ b/lib/internal/Magento/Framework/Mview/Test/Unit/_files/mview_config.php
@@ -20,14 +20,19 @@
'some_entity' => [
'name' => 'some_entity',
'column' => 'entity_id',
- 'subscription_model' => null
+ 'subscription_model' => null,
+ 'additional_columns' => [],
+ 'processor' => \Magento\Framework\Mview\View\AdditionalColumnsProcessor\DefaultProcessor::class
],
'some_product_relation' => [
'name' => 'some_product_relation',
'column' => 'product_id',
- 'subscription_model' => null
+ 'subscription_model' => null,
+ 'additional_columns' => [],
+ 'processor' => \Magento\Framework\Mview\View\AdditionalColumnsProcessor\DefaultProcessor::class
],
],
+ 'walker' => \Magento\Framework\Mview\View\ChangeLogBatchWalker::class
],
]
];
diff --git a/lib/internal/Magento/Framework/Mview/View.php b/lib/internal/Magento/Framework/Mview/View.php
index dade475a20482..420702c434103 100644
--- a/lib/internal/Magento/Framework/Mview/View.php
+++ b/lib/internal/Magento/Framework/Mview/View.php
@@ -9,7 +9,10 @@
namespace Magento\Framework\Mview;
use InvalidArgumentException;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
+use Magento\Framework\Mview\View\ChangeLogBatchWalkerFactory;
+use Magento\Framework\Mview\View\ChangeLogBatchWalkerInterface;
use Magento\Framework\Mview\View\ChangelogTableNotExistsException;
use Magento\Framework\Mview\View\SubscriptionFactory;
use Exception;
@@ -27,11 +30,6 @@ class View extends DataObject implements ViewInterface
*/
const DEFAULT_BATCH_SIZE = 1000;
- /**
- * Max versions to load from database at a time
- */
- private static $maxVersionQueryBatch = 100000;
-
/**
* @var string
*/
@@ -67,6 +65,11 @@ class View extends DataObject implements ViewInterface
*/
private $changelogBatchSize;
+ /**
+ * @var ChangeLogBatchWalkerFactory
+ */
+ private $changeLogBatchWalkerFactory;
+
/**
* @param ConfigInterface $config
* @param ActionFactory $actionFactory
@@ -75,6 +78,7 @@ class View extends DataObject implements ViewInterface
* @param SubscriptionFactory $subscriptionFactory
* @param array $data
* @param array $changelogBatchSize
+ * @param ChangeLogBatchWalkerFactory $changeLogBatchWalkerFactory
*/
public function __construct(
ConfigInterface $config,
@@ -83,7 +87,8 @@ public function __construct(
View\ChangelogInterface $changelog,
SubscriptionFactory $subscriptionFactory,
array $data = [],
- array $changelogBatchSize = []
+ array $changelogBatchSize = [],
+ ChangeLogBatchWalkerFactory $changeLogBatchWalkerFactory = null
) {
$this->config = $config;
$this->actionFactory = $actionFactory;
@@ -92,6 +97,8 @@ public function __construct(
$this->subscriptionFactory = $subscriptionFactory;
$this->changelogBatchSize = $changelogBatchSize;
parent::__construct($data);
+ $this->changeLogBatchWalkerFactory = $changeLogBatchWalkerFactory ?:
+ ObjectManager::getInstance()->get(ChangeLogBatchWalkerFactory::class);
}
/**
@@ -297,40 +304,28 @@ private function executeAction(ActionInterface $action, int $lastVersionId, int
$vsFrom = $lastVersionId;
while ($vsFrom < $currentVersionId) {
- $ids = $this->getBatchOfIds($vsFrom, $currentVersionId);
- // We run the actual indexer in batches.
- // Chunked AFTER loading to avoid duplicates in separate chunks.
- $chunks = array_chunk($ids, $batchSize);
- foreach ($chunks as $ids) {
- $action->execute($ids);
+ $walker = $this->getWalker();
+ $ids = $walker->walk($this->getChangelog(), $vsFrom, $currentVersionId, $batchSize);
+
+ if (empty($ids)) {
+ break;
}
+ $vsFrom += $batchSize;
+ $action->execute($ids);
}
}
/**
- * Get batch of entity ids
+ * Create and validate walker class for changelog
*
- * @param int $lastVersionId
- * @param int $currentVersionId
- * @return array
+ * @return ChangeLogBatchWalkerInterface|mixed
+ * @throws Exception
*/
- private function getBatchOfIds(int &$lastVersionId, int $currentVersionId): array
+ private function getWalker(): ChangeLogBatchWalkerInterface
{
- $ids = [];
- $versionBatchSize = self::$maxVersionQueryBatch;
- $idsBatchSize = self::$maxVersionQueryBatch;
- for ($vsFrom = $lastVersionId; $vsFrom < $currentVersionId; $vsFrom += $versionBatchSize) {
- // Don't go past the current version for atomicity.
- $versionTo = min($currentVersionId, $vsFrom + $versionBatchSize);
- /** To avoid duplicate ids need to flip and merge the array */
- $ids += array_flip($this->getChangelog()->getList($vsFrom, $versionTo));
- $lastVersionId = $versionTo;
- if (count($ids) >= $idsBatchSize) {
- break;
- }
- }
-
- return array_keys($ids);
+ $config = $this->config->getView($this->changelog->getViewId());
+ $walkerClass = $config['walker'];
+ return $this->changeLogBatchWalkerFactory->create($walkerClass);
}
/**
diff --git a/lib/internal/Magento/Framework/Mview/View/AdditionalColumnProcessorInterface.php b/lib/internal/Magento/Framework/Mview/View/AdditionalColumnProcessorInterface.php
new file mode 100644
index 0000000000000..c89fbce074a13
--- /dev/null
+++ b/lib/internal/Magento/Framework/Mview/View/AdditionalColumnProcessorInterface.php
@@ -0,0 +1,38 @@
+resourceConnection = $resourceConnection;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getTriggerColumns(string $eventPrefix, array $additionalColumns): array
+ {
+ $resource = $this->resourceConnection->getConnection();
+ $triggersColumns = [
+ 'column_names' => [],
+ 'column_values' => []
+ ];
+
+ foreach ($additionalColumns as $additionalColumn) {
+ $triggersColumns['column_names'][$additionalColumn['name']] = $resource->quoteIdentifier(
+ $additionalColumn['cl_name']
+ );
+
+ $triggersColumns['column_values'][$additionalColumn['name']] = isset($additionalColumn['constant']) ?
+ $resource->quote($additionalColumn['constant']) :
+ $eventPrefix . $resource->quoteIdentifier($additionalColumn['name']);
+ }
+
+ return $triggersColumns;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPreStatements(): string
+ {
+ return '';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function processColumnForCLTable(Table $table, string $columnName): void
+ {
+ $table->addColumn(
+ $columnName,
+ \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
+ 255,
+ ['unsigned' => true, 'nullable' => true, 'default' => null],
+ $columnName
+ );
+ }
+}
diff --git a/lib/internal/Magento/Framework/Mview/View/AdditionalColumnsProcessor/ProcessorFactory.php b/lib/internal/Magento/Framework/Mview/View/AdditionalColumnsProcessor/ProcessorFactory.php
new file mode 100644
index 0000000000000..5907cefbffd50
--- /dev/null
+++ b/lib/internal/Magento/Framework/Mview/View/AdditionalColumnsProcessor/ProcessorFactory.php
@@ -0,0 +1,38 @@
+objectManager = $objectManager;
+ }
+
+ /**
+ * Instantiate additional columns processor
+ *
+ * @param string $processorClassName
+ * @return AdditionalColumnProcessorInterface
+ */
+ public function create(string $processorClassName): AdditionalColumnProcessorInterface
+ {
+ return $this->objectManager->create($processorClassName);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalker.php b/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalker.php
new file mode 100644
index 0000000000000..7a767e656c3ca
--- /dev/null
+++ b/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalker.php
@@ -0,0 +1,59 @@
+resourceConnection = $resourceConnection;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function walk(ChangelogInterface $changelog, int $fromVersionId, int $toVersion, int $batchSize)
+ {
+ $connection = $this->resourceConnection->getConnection();
+ $changelogTableName = $this->resourceConnection->getTableName($changelog->getName());
+
+ if (!$connection->isTableExists($changelogTableName)) {
+ throw new ChangelogTableNotExistsException(new Phrase("Table %1 does not exist", [$changelogTableName]));
+ }
+
+ $select = $connection->select()->distinct(true)
+ ->where(
+ 'version_id > ?',
+ $fromVersionId
+ )
+ ->where(
+ 'version_id <= ?',
+ $toVersion
+ )
+ ->group([$changelog->getColumnName()])
+ ->limit($batchSize);
+
+ $select->from($changelogTableName, [$changelog->getColumnName()]);
+ return $connection->fetchCol($select);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalkerFactory.php b/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalkerFactory.php
new file mode 100644
index 0000000000000..98d814775f62b
--- /dev/null
+++ b/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalkerFactory.php
@@ -0,0 +1,37 @@
+objectManager = $objectManager;
+ }
+
+ /**
+ * Instantiate BatchWalker interface
+ *
+ * @param string $batchWalkerClassName
+ * @return ChangeLogBatchWalkerInterface
+ */
+ public function create(string $batchWalkerClassName): ChangeLogBatchWalkerInterface
+ {
+ return $this->objectManager->create($batchWalkerClassName);
+ }
+}
diff --git a/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalkerInterface.php b/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalkerInterface.php
new file mode 100644
index 0000000000000..d9079c550403c
--- /dev/null
+++ b/lib/internal/Magento/Framework/Mview/View/ChangeLogBatchWalkerInterface.php
@@ -0,0 +1,30 @@
+connection = $resource->getConnection();
$this->resource = $resource;
$this->checkConnection();
+ $this->mviewConfig = $mviewConfig;
+ $this->additionalColumnsProcessorFactory = $additionalColumnsProcessorFactory;
}
/**
@@ -83,7 +109,7 @@ public function create()
$table = $this->connection->newTable(
$changelogTableName
)->addColumn(
- 'version_id',
+ self::VERSION_ID_COLUMN_NAME,
\Magento\Framework\DB\Ddl\Table::TYPE_INTEGER,
null,
['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true],
@@ -95,10 +121,46 @@ public function create()
['unsigned' => true, 'nullable' => false, 'default' => '0'],
'Entity ID'
);
+
+ foreach ($this->initAdditionalColumnData() as $columnData) {
+ /** @var AdditionalColumnProcessorInterface $processor */
+ $processorClass = $columnData['processor'];
+ $processor = $this->additionalColumnsProcessorFactory->create($processorClass);
+ $processor->processColumnForCLTable($table, $columnData['cl_name']);
+ }
+
$this->connection->createTable($table);
}
}
+ /**
+ * Retrieve additional column data
+ *
+ * @return array
+ * @throws \Exception
+ */
+ private function initAdditionalColumnData(): array
+ {
+ $config = $this->mviewConfig->getView($this->getViewId());
+ $additionalColumns = [];
+
+ if (!$config) {
+ return $additionalColumns;
+ }
+
+ foreach ($config['subscriptions'] as $subscription) {
+ if (isset($subscription['additional_columns'])) {
+ foreach ($subscription['additional_columns'] as $additionalColumn) {
+ //We are gatherig unique change log column names in order to create them later
+ $additionalColumns[$additionalColumn['cl_name']] = $additionalColumn;
+ $additionalColumns[$additionalColumn['cl_name']]['processor'] = $subscription['processor'];
+ }
+ }
+ }
+
+ return $additionalColumns;
+ }
+
/**
* Drop changelog table
*
@@ -139,7 +201,7 @@ public function clear($versionId)
*
* @param int $fromVersionId
* @param int $toVersionId
- * @return int[]
+ * @return array
* @throws ChangelogTableNotExistsException
*/
public function getList($fromVersionId, $toVersionId)
diff --git a/lib/internal/Magento/Framework/Mview/View/Subscription.php b/lib/internal/Magento/Framework/Mview/View/Subscription.php
index 510a4579d160b..17e4dede4cbf8 100644
--- a/lib/internal/Magento/Framework/Mview/View/Subscription.php
+++ b/lib/internal/Magento/Framework/Mview/View/Subscription.php
@@ -6,8 +6,10 @@
namespace Magento\Framework\Mview\View;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Ddl\Trigger;
+use Magento\Framework\Mview\Config;
use Magento\Framework\Mview\View\StateInterface;
/**
@@ -72,6 +74,10 @@ class Subscription implements SubscriptionInterface
* @var Resource
*/
protected $resource;
+ /**
+ * @var Config
+ */
+ private $mviewConfig;
/**
* @param ResourceConnection $resource
@@ -82,6 +88,7 @@ class Subscription implements SubscriptionInterface
* @param string $columnName
* @param array $ignoredUpdateColumns
* @param array $ignoredUpdateColumnsBySubscription
+ * @param Config|null $mviewConfig
*/
public function __construct(
ResourceConnection $resource,
@@ -91,7 +98,8 @@ public function __construct(
$tableName,
$columnName,
$ignoredUpdateColumns = [],
- $ignoredUpdateColumnsBySubscription = []
+ $ignoredUpdateColumnsBySubscription = [],
+ Config $mviewConfig = null
) {
$this->connection = $resource->getConnection();
$this->triggerFactory = $triggerFactory;
@@ -102,6 +110,7 @@ public function __construct(
$this->resource = $resource;
$this->ignoredUpdateColumns = $ignoredUpdateColumns;
$this->ignoredUpdateColumnsBySubscription = $ignoredUpdateColumnsBySubscription;
+ $this->mviewConfig = $mviewConfig ?? ObjectManager::getInstance()->get(Config::class);
}
/**
@@ -196,6 +205,38 @@ protected function getLinkedViews()
return $this->linkedViews;
}
+ /**
+ * Prepare columns for trigger statement. Should be protected in order to serve new approach
+ *
+ * @param ChangelogInterface $changelog
+ * @param string $event
+ * @return array
+ * @throws \Exception
+ */
+ protected function prepareColumns(ChangelogInterface $changelog, string $event): array
+ {
+ $prefix = $event === Trigger::EVENT_DELETE ? 'OLD.' : 'NEW.';
+ $subscriptionData = $this->mviewConfig->getView($changelog->getViewId())['subscriptions'][$this->getTableName()];
+ $columns = [
+ 'column_names' => [
+ 'entity_id' => $this->connection->quoteIdentifier($changelog->getColumnName())
+ ],
+ 'column_values' => [
+ 'entity_id' => $this->getEntityColumn($prefix)
+ ]
+ ];
+
+ if (!empty($subscriptionData['additional_columns'])) {
+ $processor = $this->getProcessor();
+ $columns = array_replace_recursive(
+ $columns,
+ $processor->getTriggerColumns($prefix, $subscriptionData['additional_columns'])
+ );
+ }
+
+ return $columns;
+ }
+
/**
* Build trigger statement for INSERT, UPDATE, DELETE events
*
@@ -205,13 +246,10 @@ protected function getLinkedViews()
*/
protected function buildStatement($event, $changelog)
{
+ $trigger = "%sINSERT IGNORE INTO %s (%s) VALUES (%s);";
switch ($event) {
- case Trigger::EVENT_INSERT:
- $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);";
- break;
case Trigger::EVENT_UPDATE:
$tableName = $this->resource->getTableName($this->getTableName());
- $trigger = "INSERT IGNORE INTO %s (%s) VALUES (NEW.%s);";
if ($this->connection->isTableExists($tableName) &&
$describe = $this->connection->describeTable($tableName)
) {
@@ -240,20 +278,47 @@ protected function buildStatement($event, $changelog)
}
}
break;
- case Trigger::EVENT_DELETE:
- $trigger = "INSERT IGNORE INTO %s (%s) VALUES (OLD.%s);";
- break;
- default:
- return '';
}
+ $columns = $this->prepareColumns($changelog, $event);
return sprintf(
$trigger,
+ $this->getProcessor()->getPreStatements(),
$this->connection->quoteIdentifier($this->resource->getTableName($changelog->getName())),
- $this->connection->quoteIdentifier($changelog->getColumnName()),
- $this->connection->quoteIdentifier($this->getColumnName())
+ implode(", " , $columns['column_names']),
+ implode(", ", $columns['column_values'])
);
}
+ /**
+ * Instantiate and retrieve additional columns processor
+ *
+ * @return AdditionalColumnProcessorInterface
+ * @throws \Exception
+ */
+ private function getProcessor(): AdditionalColumnProcessorInterface
+ {
+ $subscriptionData = $this->mviewConfig->getView($this->getView()->getId())['subscriptions'];
+ $processorClass = $subscriptionData[$this->getTableName()]['processor'];
+ $processor = ObjectManager::getInstance()->get($processorClass);
+
+ if (!$processor instanceof AdditionalColumnProcessorInterface) {
+ throw new \Exception(
+ 'Processor should implements ' . AdditionalColumnProcessorInterface::class
+ );
+ }
+
+ return $processor;
+ }
+
+ /**
+ * @param string $prefix
+ * @return string
+ */
+ public function getEntityColumn(string $prefix): string
+ {
+ return $prefix . $this->connection->quoteIdentifier($this->getColumnName());
+ }
+
/**
* Build an "after" event for the given table and event
*
diff --git a/lib/internal/Magento/Framework/Mview/etc/mview.xsd b/lib/internal/Magento/Framework/Mview/etc/mview.xsd
index dfff4964f6587..04754fa499249 100644
--- a/lib/internal/Magento/Framework/Mview/etc/mview.xsd
+++ b/lib/internal/Magento/Framework/Mview/etc/mview.xsd
@@ -46,6 +46,7 @@
+
@@ -76,9 +77,25 @@
Table declaration.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/internal/Magento/Framework/View/Helper/SecureHtmlRenderer.php b/lib/internal/Magento/Framework/View/Helper/SecureHtmlRenderer.php
index ae8ab3f15bc96..052c51441d451 100644
--- a/lib/internal/Magento/Framework/View/Helper/SecureHtmlRenderer.php
+++ b/lib/internal/Magento/Framework/View/Helper/SecureHtmlRenderer.php
@@ -8,7 +8,6 @@
namespace Magento\Framework\View\Helper;
-use Magento\Framework\Api\SimpleDataObjectConverter;
use Magento\Framework\Math\Random;
use Magento\Framework\View\Helper\SecureHtmlRender\EventHandlerData;
use Magento\Framework\View\Helper\SecureHtmlRender\HtmlRenderer;
@@ -105,23 +104,27 @@ public function renderEventListenerAsTag(
}
$random = $this->random->getRandomString(10);
- $listenerFunction = 'eventListener' .$random;
- $elementName = 'listenedElement' .$random;
+ $listenerFunction = 'eventListener' . $random;
+ $elementName = 'listenedElement' . $random;
$script = <<