= $block->escapeHtml($_item->getName()) ?>
- getItemOptions()) : ?>
+ getItemOptions()): ?>
-
+
- = $block->escapeHtml($_option['label']) ?>
- getPrintStatus()) : ?>
+ getPrintStatus()): ?>
getFormatedOptionValue($_option) ?>
- >
- = $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?>
-
+ = $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?>
+
- = $block->escapeHtml($_option['label']) ?>
@@ -27,43 +27,46 @@ $_item = $block->getItem();
-
- - = $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?>
+
+
+ - = $block->escapeHtml($optionValue) ?>
getProductAdditionalInformationBlock(); ?>
-
+
= $addtInfoBlock->setItem($_item)->toHtml() ?>
= $block->escapeHtml($_item->getDescription()) ?>
|
- = /* @noEscape */ $block->prepareSku($block->getSku()) ?> |
+
+ = /* @noEscape */ $block->prepareSku($block->getSku()) ?>
+ |
= $block->getItemPriceHtml() ?>
|
- getItem()->getQtyOrdered() > 0) : ?>
+ getItem()->getQtyOrdered() > 0): ?>
-
= $block->escapeHtml(__('Ordered')) ?>
= (float) $block->getItem()->getQtyOrdered() ?>
- getItem()->getQtyShipped() > 0) : ?>
+ getItem()->getQtyShipped() > 0): ?>
-
= $block->escapeHtml(__('Shipped')) ?>
= (float) $block->getItem()->getQtyShipped() ?>
- getItem()->getQtyCanceled() > 0) : ?>
+ getItem()->getQtyCanceled() > 0): ?>
-
= $block->escapeHtml(__('Canceled')) ?>
= (float) $block->getItem()->getQtyCanceled() ?>
- getItem()->getQtyRefunded() > 0) : ?>
+ getItem()->getQtyRefunded() > 0): ?>
-
= $block->escapeHtml(__('Refunded')) ?>
= (float) $block->getItem()->getQtyRefunded() ?>
diff --git a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml
index 26fe74b0fc454..6c7567a8cd14b 100644
--- a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml
+++ b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml
@@ -9,15 +9,15 @@
= $block->escapeHtml($_item->getName()) ?>
- getItemOptions()) : ?>
+ getItemOptions()): ?>
-
+
- = $block->escapeHtml($_option['label']) ?>
- getPrintStatus()) : ?>
+ getPrintStatus()): ?>
getFormatedOptionValue($_option) ?>
- >
- = $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?>
-
+ = $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?>
+
- = $block->escapeHtml($_option['label']) ?>
@@ -26,18 +26,21 @@
-
- - = $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?>
+
+
+ - = $block->escapeHtml($optionValue) ?>
getProductAdditionalInformationBlock(); ?>
-
+
= $addInfoBlock->setItem($_item->getOrderItem())->toHtml() ?>
= $block->escapeHtml($_item->getDescription()) ?>
|
- = /* @noEscape */ $block->prepareSku($block->getSku()) ?> |
+
+ = /* @noEscape */ $block->prepareSku($block->getSku()) ?>
+ |
= (int) $_item->getQty() ?> |
diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php
index 53459f2c3e52f..d1440a2b547a4 100644
--- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php
+++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php
@@ -15,13 +15,14 @@
use Magento\Framework\View\Result\Layout;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\App\Action\HttpGetActionInterface;
+use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Export Coupons to csv file
*
* Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsCsv
*/
-class ExportCouponsCsv extends Quote implements HttpGetActionInterface
+class ExportCouponsCsv extends Quote implements HttpGetActionInterface, HttpPostActionInterface
{
/**
* Export coupon codes as CSV file
diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php
index fa3d4455410c4..401d8aea1aded 100644
--- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php
+++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php
@@ -15,13 +15,14 @@
use Magento\Framework\View\Result\Layout;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\App\Action\HttpGetActionInterface;
+use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Export coupons to xml file
*
* Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsXml
*/
-class ExportCouponsXml extends Quote implements HttpGetActionInterface
+class ExportCouponsXml extends Quote implements HttpGetActionInterface, HttpPostActionInterface
{
/**
* Export coupon codes as excel xml file
diff --git a/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml b/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml
index 90b591a7bb1b1..c8ad0efdd4b4d 100644
--- a/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml
+++ b/app/code/Magento/SalesRule/Test/Mftf/Section/DiscountSection.xml
@@ -14,6 +14,7 @@
+
diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml
index f32442ca5bc98..f956d036d7080 100644
--- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml
+++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml
@@ -39,8 +39,7 @@
-
-
+
@@ -93,8 +92,7 @@
-
-
+
diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForBundleProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForBundleProductTest.xml
new file mode 100644
index 0000000000000..101c72b78078a
--- /dev/null
+++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForBundleProductTest.xml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5.00
+
+
+ 3.00
+
+
+ 7.00
+
+
+ 18.00
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AssertStorefrontVerifySearchButtonIsDisabledActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AssertStorefrontVerifySearchButtonIsDisabledActionGroup.xml
new file mode 100644
index 0000000000000..57d39e35d539e
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AssertStorefrontVerifySearchButtonIsDisabledActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+ Verify search button has disabled attribute
+
+
+
+
+
+ $grabSearchButtonDisabledAttribute
+ true
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/AssertStorefrontVerifySearchButtonIsEnabledActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/AssertStorefrontVerifySearchButtonIsEnabledActionGroup.xml
new file mode 100644
index 0000000000000..2e1f8d4b68d36
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/AssertStorefrontVerifySearchButtonIsEnabledActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+ Verify search button does not disabled attribute
+
+
+
+
+
+ $grabSearchButtonAttribute
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/ActionGroup/StoreFrontFillSearchActionGroup.xml b/app/code/Magento/Search/Test/Mftf/ActionGroup/StoreFrontFillSearchActionGroup.xml
new file mode 100644
index 0000000000000..f90297df02c1f
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/ActionGroup/StoreFrontFillSearchActionGroup.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml
index f5bb414f59197..88e459178edbc 100644
--- a/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml
+++ b/app/code/Magento/Search/Test/Mftf/Test/AdminMassDeleteSearchTermEntityTest.xml
@@ -34,8 +34,7 @@
-
-
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchButtonDisabledTillMinimumSearchLengthHitTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchButtonDisabledTillMinimumSearchLengthHitTest.xml
new file mode 100644
index 0000000000000..742807d2c24e2
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchButtonDisabledTillMinimumSearchLengthHitTest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchButtonEnabledAfterMinimumSearchLengthHitTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchButtonEnabledAfterMinimumSearchLengthHitTest.xml
new file mode 100644
index 0000000000000..172fae919623c
--- /dev/null
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchButtonEnabledAfterMinimumSearchLengthHitTest.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml
index 22fcbfc2920ff..8c468cce91829 100644
--- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml
@@ -35,8 +35,7 @@
-
-
+
@@ -60,8 +59,7 @@
-
-
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml
index 0b02b49433dda..fb1f35730fd80 100644
--- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductNameTest.xml
@@ -38,8 +38,7 @@
-
-
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml
index d88bb023c60b2..1558f9aa5342b 100644
--- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductShortDescriptionTest.xml
@@ -39,8 +39,7 @@
-
-
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml
index 4c586d18fd3cf..19c12843c23a2 100644
--- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductSkuTest.xml
@@ -39,8 +39,7 @@
-
-
+
diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml
index c5cbf1e0709c6..4f8cd9da856ca 100644
--- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml
+++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchTermEntityRedirectTest.xml
@@ -32,7 +32,7 @@
-
+
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 b4493c5f38089..9b4c814f73d73 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
@@ -232,8 +232,10 @@ define([
break;
case $.ui.keyCode.ENTER:
- this.searchForm.trigger('submit');
- e.preventDefault();
+ if (this.element.val().length >= parseInt(this.options.minSearchLength, 10)) {
+ this.searchForm.trigger('submit');
+ e.preventDefault();
+ }
break;
case $.ui.keyCode.DOWN:
@@ -294,9 +296,10 @@ define([
dropdown = $(''),
value = this.element.val();
- this.submitBtn.disabled = isEmpty(value);
+ this.submitBtn.disabled = true;
if (value.length >= parseInt(this.options.minSearchLength, 10)) {
+ this.submitBtn.disabled = false;
$.getJSON(this.options.url, {
q: value
}, $.proxy(function (data) {
diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml
index 9d501e4b34ef7..b1fb2aad54272 100644
--- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml
+++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreatePartialShipmentEntityTest.xml
@@ -51,8 +51,7 @@
-
-
+
diff --git a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml
index a900a73fc36bc..5d46ef0a76263 100644
--- a/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml
+++ b/app/code/Magento/Shipping/Test/Mftf/Test/AdminCreateShipmentEntityTest.xml
@@ -51,8 +51,7 @@
-
-
+
diff --git a/app/code/Magento/Sitemap/Model/Observer.php b/app/code/Magento/Sitemap/Model/Observer.php
index ce74d738c4bc3..4333c71c7497f 100644
--- a/app/code/Magento/Sitemap/Model/Observer.php
+++ b/app/code/Magento/Sitemap/Model/Observer.php
@@ -3,11 +3,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sitemap\Model;
-use Magento\Sitemap\Model\EmailNotification as SitemapEmail;
+use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Sitemap\Model\EmailNotification as SitemapEmail;
use Magento\Sitemap\Model\ResourceModel\Sitemap\CollectionFactory;
+use Magento\Store\Model\App\Emulation;
use Magento\Store\Model\ScopeInterface;
/**
@@ -61,20 +65,28 @@ class Observer
*/
private $emailNotification;
+ /**
+ * @var Emulation
+ */
+ private $appEmulation;
+
/**
* Observer constructor.
* @param ScopeConfigInterface $scopeConfig
* @param CollectionFactory $collectionFactory
* @param EmailNotification $emailNotification
+ * @param Emulation $appEmulation
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
CollectionFactory $collectionFactory,
- SitemapEmail $emailNotification
+ SitemapEmail $emailNotification,
+ Emulation $appEmulation
) {
$this->scopeConfig = $scopeConfig;
$this->collectionFactory = $collectionFactory;
$this->emailNotification = $emailNotification;
+ $this->appEmulation = $appEmulation;
}
/**
@@ -105,9 +117,16 @@ public function scheduledGenerateSitemaps()
foreach ($collection as $sitemap) {
/* @var $sitemap \Magento\Sitemap\Model\Sitemap */
try {
+ $this->appEmulation->startEnvironmentEmulation(
+ $sitemap->getStoreId(),
+ Area::AREA_FRONTEND,
+ true
+ );
$sitemap->generateXml();
} catch (\Exception $e) {
$errors[] = $e->getMessage();
+ } finally {
+ $this->appEmulation->stopEnvironmentEmulation();
}
}
if ($errors && $recipient) {
diff --git a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
index 23ebe4f85f79e..70520862faee1 100644
--- a/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
+++ b/app/code/Magento/Sitemap/Test/Unit/Model/ObserverTest.php
@@ -142,4 +142,39 @@ public function testScheduledGenerateSitemapsSendsExceptionEmail()
$this->observer->scheduledGenerateSitemaps();
}
+
+ /**
+ * Test if cron scheduled XML sitemap generation will start and stop the store environment emulation
+ *
+ * @throws \Exception
+ */
+ public function testCronGenerateSitemapEnvironmentEmulation()
+ {
+ $storeId = 1;
+
+ $this->scopeConfigMock->expects($this->once())->method('isSetFlag')->willReturn(true);
+
+ $this->collectionFactoryMock->expects($this->once())
+ ->method('create')
+ ->willReturn($this->sitemapCollectionMock);
+
+ $this->sitemapCollectionMock->expects($this->any())
+ ->method('getIterator')
+ ->willReturn(new \ArrayIterator([$this->sitemapMock]));
+
+ $this->sitemapMock->expects($this->at(0))
+ ->method('getStoreId')
+ ->willReturn($storeId);
+
+ $this->sitemapMock->expects($this->once())
+ ->method('generateXml');
+
+ $this->appEmulationMock->expects($this->once())
+ ->method('startEnvironmentEmulation');
+
+ $this->appEmulationMock->expects($this->once())
+ ->method('stopEnvironmentEmulation');
+
+ $this->observer->scheduledGenerateSitemaps();
+ }
}
diff --git a/app/code/Magento/Store/Block/Switcher.php b/app/code/Magento/Store/Block/Switcher.php
index f15349f11066d..a924805fcba90 100644
--- a/app/code/Magento/Store/Block/Switcher.php
+++ b/app/code/Magento/Store/Block/Switcher.php
@@ -170,9 +170,15 @@ public function getGroups()
if ($store) {
$group->setHomeUrl($store->getHomeUrl());
+ $group->setSortOrder($store->getSortOrder());
$groups[] = $group;
}
}
+
+ usort($groups, static function ($itemA, $itemB) {
+ return (int)$itemA->getSortOrder() <=> (int)$itemB->getSortOrder();
+ });
+
$this->setData('groups', $groups);
}
return $this->getData('groups');
@@ -193,7 +199,12 @@ public function getStores()
$stores = [];
} else {
$stores = $rawStores[$groupId];
+
+ uasort($stores, static function ($itemA, $itemB) {
+ return (int)$itemA->getSortOrder() <=> (int)$itemB->getSortOrder();
+ });
}
+
$this->setData('stores', $stores);
}
return $this->getData('stores');
diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCheckStoreViewOptionsActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCheckStoreViewOptionsActionGroup.xml
new file mode 100644
index 0000000000000..ba96633a621c2
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCheckStoreViewOptionsActionGroup.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Goes to the Catalog->Product filters and check store view options at the Store View dropdown
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewFillSortOrderActionGroup.xml b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewFillSortOrderActionGroup.xml
new file mode 100644
index 0000000000000..1b9b147209c66
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/ActionGroup/AdminCreateStoreViewFillSortOrderActionGroup.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ Fill 'Sort Order' field
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml
index bdb1842cf2959..39664ae10a07d 100644
--- a/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml
+++ b/app/code/Magento/Store/Test/Mftf/Data/StoreData.xml
@@ -206,4 +206,23 @@
store
add
+
+
+ sameNameStoreView
+ storeViewCode
+ 1
+ null
+ add
+ store
+ customStoreGroup
+
+
+ sameNameStoreView
+ storeViewCode
+ 1
+ null
+ add
+ store
+ customStoreGroup
+
diff --git a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml
index e56836c491276..cd7f180d0bb0e 100644
--- a/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml
+++ b/app/code/Magento/Store/Test/Mftf/Section/AdminStoresGridSection/AdminStoresGridSection.xml
@@ -22,5 +22,6 @@
+
diff --git a/app/code/Magento/Store/Test/Mftf/Test/AdminCreateDuplicateNameStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateDuplicateNameStoreViewTest.xml
new file mode 100644
index 0000000000000..ec81424b1acfa
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/Test/AdminCreateDuplicateNameStoreViewTest.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Store/Test/Mftf/Test/StorefrontCheckSortOrderStoreViewTest.xml b/app/code/Magento/Store/Test/Mftf/Test/StorefrontCheckSortOrderStoreViewTest.xml
new file mode 100644
index 0000000000000..442ee99e12793
--- /dev/null
+++ b/app/code/Magento/Store/Test/Mftf/Test/StorefrontCheckSortOrderStoreViewTest.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{SecondStoreGroupUnique.name}}
+ $grabSwatchFirstOption
+
+
+ {{customStoreGroup.name}}
+ $grabSwatchSecondOption
+
+
+
diff --git a/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php b/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php
index 9106da8ffb177..60c69834f6aa6 100644
--- a/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php
+++ b/app/code/Magento/Store/Test/Unit/Block/SwitcherTest.php
@@ -7,91 +7,159 @@
namespace Magento\Store\Test\Unit\Block;
+use Magento\Directory\Helper\Data;
+use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Data\Helper\PostHelper;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Framework\UrlInterface;
use Magento\Framework\View\Element\Template\Context;
use Magento\Store\Api\Data\StoreInterface;
use Magento\Store\Block\Switcher;
+use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
+use Magento\Store\Model\Website;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+/**
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class SwitcherTest extends TestCase
{
- /** @var Switcher */
- protected $switcher;
-
- /** @var Context|MockObject */
- protected $context;
+ /**
+ * @var Switcher
+ */
+ private $switcher;
- /** @var PostHelper|MockObject */
- protected $corePostDataHelper;
+ /**
+ * @var PostHelper|MockObject
+ */
+ private $corePostDataHelperMock;
- /** @var StoreManagerInterface|MockObject */
- protected $storeManager;
+ /**
+ * @var StoreManagerInterface|MockObject
+ */
+ private $storeManagerMock;
- /** @var UrlInterface|MockObject */
- protected $urlBuilder;
+ /**
+ * @var UrlInterface|MockObject
+ */
+ private $urlBuilderMock;
- /** @var StoreInterface|MockObject */
- private $store;
+ /**
+ * @var ScopeConfigInterface|MockObject
+ */
+ private $scopeConfigMock;
/**
* @return void
*/
protected function setUp(): void
{
- $this->storeManager = $this->getMockBuilder(StoreManagerInterface::class)
- ->getMock();
- $this->urlBuilder = $this->getMockForAbstractClass(UrlInterface::class);
- $this->context = $this->createMock(Context::class);
- $this->context->expects($this->any())->method('getStoreManager')->willReturn($this->storeManager);
- $this->context->expects($this->any())->method('getUrlBuilder')->willReturn($this->urlBuilder);
- $this->corePostDataHelper = $this->createMock(PostHelper::class);
- $this->store = $this->getMockBuilder(StoreInterface::class)
- ->disableOriginalConstructor()
- ->getMockForAbstractClass();
+ $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class)->getMock();
+ $this->urlBuilderMock = $this->createMock(UrlInterface::class);
+ $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class);
+ $contextMock = $this->createMock(Context::class);
+ $contextMock->method('getStoreManager')->willReturn($this->storeManagerMock);
+ $contextMock->method('getUrlBuilder')->willReturn($this->urlBuilderMock);
+ $contextMock->method('getScopeConfig')->willReturn($this->scopeConfigMock);
+ $this->corePostDataHelperMock = $this->createMock(PostHelper::class);
$this->switcher = (new ObjectManager($this))->getObject(
Switcher::class,
[
- 'context' => $this->context,
- 'postDataHelper' => $this->corePostDataHelper,
+ 'context' => $contextMock,
+ 'postDataHelper' => $this->corePostDataHelperMock,
]
);
}
+ public function testGetStoresSortOrder()
+ {
+ $groupId = 1;
+ $storesSortOrder = [
+ 1 => 2,
+ 2 => 4,
+ 3 => 1,
+ 4 => 3
+ ];
+
+ $currentStoreMock = $this->getMockBuilder(Store::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $currentStoreMock->method('getGroupId')->willReturn($groupId);
+ $currentStoreMock->method('isUseStoreInUrl')->willReturn(false);
+ $this->storeManagerMock->method('getStore')
+ ->willReturn($currentStoreMock);
+
+ $currentWebsiteMock = $this->getMockBuilder(Website::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->storeManagerMock->method('getWebsite')
+ ->willReturn($currentWebsiteMock);
+
+ $stores = [];
+ foreach ($storesSortOrder as $storeId => $sortOrder) {
+ $storeMock = $this->getMockBuilder(Store::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getId', 'getGroupId', 'getSortOrder', 'isActive', 'getUrl'])
+ ->getMock();
+ $storeMock->method('getId')->willReturn($storeId);
+ $storeMock->method('getGroupId')->willReturn($groupId);
+ $storeMock->method('getSortOrder')->willReturn($sortOrder);
+ $storeMock->method('isActive')->willReturn(true);
+ $storeMock->method('getUrl')->willReturn('https://example.org');
+ $stores[] = $storeMock;
+ }
+
+ $scopeConfigMap = array_map(static function ($item) {
+ return [
+ Data::XML_PATH_DEFAULT_LOCALE,
+ ScopeInterface::SCOPE_STORE,
+ $item,
+ 'en_US'
+ ];
+ }, $stores);
+ $this->scopeConfigMock->method('getValue')
+ ->willReturnMap($scopeConfigMap);
+
+ $currentWebsiteMock->method('getStores')
+ ->willReturn($stores);
+
+ $this->assertEquals([3, 1, 4, 2], array_keys($this->switcher->getStores()));
+ }
+
/**
* @return void
*/
public function testGetTargetStorePostData()
{
- $store = $this->getMockBuilder(Store::class)
+ $storeMock = $this->getMockBuilder(Store::class)
->disableOriginalConstructor()
->getMock();
- $store->expects($this->any())
- ->method('getCode')
+ $oldStoreMock = $this->getMockBuilder(StoreInterface::class)
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ $storeMock->method('getCode')
->willReturn('new-store');
$storeSwitchUrl = 'http://domain.com/stores/store/redirect';
- $store->expects($this->atLeastOnce())
+ $storeMock->expects($this->atLeastOnce())
->method('getCurrentUrl')
->with(false)
->willReturn($storeSwitchUrl);
- $this->storeManager->expects($this->once())
+ $this->storeManagerMock->expects($this->once())
->method('getStore')
- ->willReturn($this->store);
- $this->store->expects($this->once())
+ ->willReturn($oldStoreMock);
+ $oldStoreMock->expects($this->once())
->method('getCode')
->willReturn('old-store');
- $this->urlBuilder->expects($this->once())
+ $this->urlBuilderMock->expects($this->once())
->method('getUrl')
->willReturn($storeSwitchUrl);
- $this->corePostDataHelper->expects($this->any())
- ->method('getPostData')
+ $this->corePostDataHelperMock->method('getPostData')
->with($storeSwitchUrl, ['___store' => 'new-store', 'uenc' => null, '___from_store' => 'old-store']);
- $this->switcher->getTargetStorePostData($store);
+ $this->switcher->getTargetStorePostData($storeMock);
}
/**
@@ -104,7 +172,7 @@ public function testIsStoreInUrl($isUseStoreInUrl)
$storeMock->expects($this->once())->method('isUseStoreInUrl')->willReturn($isUseStoreInUrl);
- $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeMock);
+ $this->storeManagerMock->method('getStore')->willReturn($storeMock);
$this->assertEquals($this->switcher->isStoreInUrl(), $isUseStoreInUrl);
// check value is cached
$this->assertEquals($this->switcher->isStoreInUrl(), $isUseStoreInUrl);
@@ -114,7 +182,7 @@ public function testIsStoreInUrl($isUseStoreInUrl)
* @see self::testIsStoreInUrlDataProvider()
* @return array
*/
- public function isStoreInUrlDataProvider()
+ public function isStoreInUrlDataProvider(): array
{
return [[true], [false]];
}
diff --git a/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php b/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php
index 907eb74e20fa2..f8aa09cb20a61 100644
--- a/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php
+++ b/app/code/Magento/Store/Ui/Component/Listing/Column/Store/Options.php
@@ -10,7 +10,7 @@
use Magento\Store\Model\System\Store as SystemStore;
/**
- * Class Options
+ * Ui stores options
*/
class Options implements OptionSourceInterface
{
@@ -93,37 +93,38 @@ protected function sanitizeName($name)
*
* @return void
*/
- protected function generateCurrentOptions()
+ protected function generateCurrentOptions(): void
{
$websiteCollection = $this->systemStore->getWebsiteCollection();
$groupCollection = $this->systemStore->getGroupCollection();
$storeCollection = $this->systemStore->getStoreCollection();
- /** @var \Magento\Store\Model\Website $website */
+
foreach ($websiteCollection as $website) {
$groups = [];
- /** @var \Magento\Store\Model\Group $group */
foreach ($groupCollection as $group) {
- if ($group->getWebsiteId() == $website->getId()) {
+ if ($group->getWebsiteId() === $website->getId()) {
$stores = [];
- /** @var \Magento\Store\Model\Store $store */
foreach ($storeCollection as $store) {
- if ($store->getGroupId() == $group->getId()) {
- $name = $this->sanitizeName($store->getName());
- $stores[$name]['label'] = str_repeat(' ', 8) . $name;
- $stores[$name]['value'] = $store->getId();
+ if ($store->getGroupId() === $group->getId()) {
+ $stores[] = [
+ 'label' => str_repeat(' ', 8) . $this->sanitizeName($store->getName()),
+ 'value' => $store->getId(),
+ ];
}
}
if (!empty($stores)) {
- $name = $this->sanitizeName($group->getName());
- $groups[$name]['label'] = str_repeat(' ', 4) . $name;
- $groups[$name]['value'] = array_values($stores);
+ $groups[] = [
+ 'label' => str_repeat(' ', 4) . $this->sanitizeName($group->getName()),
+ 'value' => array_values($stores),
+ ];
}
}
}
if (!empty($groups)) {
- $name = $this->sanitizeName($website->getName());
- $this->currentOptions[$name]['label'] = $name;
- $this->currentOptions[$name]['value'] = array_values($groups);
+ $this->currentOptions[] = [
+ 'label' => $this->sanitizeName($website->getName()),
+ 'value' => array_values($groups),
+ ];
}
}
}
diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml
index d56572afd8847..07ce30b702f91 100644
--- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml
+++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminSetUpWatermarkForSwatchImageTest.xml
@@ -38,8 +38,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Pricing/Render/Adjustment.php b/app/code/Magento/Tax/Pricing/Render/Adjustment.php
index 8613e62f2983e..0e5c619790a97 100644
--- a/app/code/Magento/Tax/Pricing/Render/Adjustment.php
+++ b/app/code/Magento/Tax/Pricing/Render/Adjustment.php
@@ -38,6 +38,8 @@ public function __construct(
}
/**
+ * Apply the right HTML output to the adjustment
+ *
* @return string
*/
protected function apply()
@@ -173,4 +175,16 @@ public function displayPriceExcludingTax()
{
return $this->taxHelper->displayPriceExcludingTax();
}
+
+ /**
+ * Obtain a value for data-price-type attribute
+ *
+ * @return string
+ */
+ public function getDataPriceType(): string
+ {
+ return $this->amountRender->getPriceType() === 'finalPrice'
+ ? 'basePrice'
+ : 'base' . ucfirst($this->amountRender->getPriceType());
+ }
}
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminClickAddTaxRuleButtonActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminClickAddTaxRuleButtonActionGroup.xml
new file mode 100644
index 0000000000000..ea5c4cb74d19e
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminClickAddTaxRuleButtonActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Click button for creating new tax rule.
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml
new file mode 100644
index 0000000000000..1aab6ea2c4eec
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+ Delete Tax Rate.
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml
new file mode 100644
index 0000000000000..2b110e969b113
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Filter Tax Rates by tax rate code.
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml
index 07968c281c68b..5e7ce53a3a3fc 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateDefaultsTaxRuleTest.xml
@@ -30,8 +30,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml
index be7185a5166a2..ceb04a9c42e66 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml
index c8e4defc40c9f..7497b950a8c0e 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml
@@ -23,12 +23,13 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -59,7 +60,7 @@
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml
index c6a5a6c69e788..da89ad3e9337c 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -58,7 +58,7 @@
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml
index ef9b66041893d..da30157d94182 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -60,7 +60,7 @@
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml
index 23c4ffd78a88d..93e0f6514e83b 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml
@@ -23,12 +23,12 @@
-
-
-
-
-
-
+
+
+
+
+
+
@@ -62,7 +62,7 @@
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml
index ba0834da7c0e7..f1a48af741cd6 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithCustomerAndProductTaxClassTest.xml
@@ -40,8 +40,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml
index ae37bc8a8930a..de7a0fb2d9144 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewAndExistingTaxRateAndCustomerAndProductTaxClassTest.xml
@@ -41,8 +41,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml
index 2a008991c2dc8..4798ec60ab898 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithNewTaxClassesAndTaxRateTest.xml
@@ -41,8 +41,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml
index de55453fcabc4..a08c878ba2063 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRuleWithZipRangeTest.xml
@@ -41,8 +41,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml
index 881e09e5e35f4..751989497d10e 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml
@@ -25,14 +25,13 @@
-
-
-
-
+
+
+
+
+
+
-
-
-
@@ -44,8 +43,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml
index b9c3baab8c0dd..d2f1b1aa44393 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestSimpleTest.xml
@@ -79,8 +79,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml
index 8fafbd9986c64..5441664d7c530 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutGuestVirtualTest.xml
@@ -79,8 +79,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml
index ae988cd43efd5..76f4dcd8e161e 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInSimpleTest.xml
@@ -94,8 +94,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml
index 6552d31a8a523..c98765976f36f 100644
--- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml
+++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCheckoutTest/StorefrontTaxQuoteCheckoutLoggedInVirtualTest.xml
@@ -94,8 +94,7 @@
-
-
+
diff --git a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml
index e87d1c9eb96aa..685893151bc5a 100644
--- a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml
+++ b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml
@@ -6,12 +6,13 @@
?>
+
-displayBothPrices()) : ?>
- displayBothPrices()): ?>
+
= /* @noEscape */ $block->getDisplayAmountExclTax() ?>
diff --git a/app/code/Magento/Theme/Model/Indexer/Design/Config/Plugin/Store.php b/app/code/Magento/Theme/Model/Indexer/Design/Config/Plugin/Store.php
index ca4a238fd1ff4..18ce1be2e68d1 100644
--- a/app/code/Magento/Theme/Model/Indexer/Design/Config/Plugin/Store.php
+++ b/app/code/Magento/Theme/Model/Indexer/Design/Config/Plugin/Store.php
@@ -6,7 +6,7 @@
namespace Magento\Theme\Model\Indexer\Design\Config\Plugin;
use Magento\Framework\Indexer\IndexerRegistry;
-use Magento\Store\Model\Store as StoreStore;
+use Magento\Store\Model\Store as StoreModel;
use Magento\Theme\Model\Data\Design\Config;
class Store
@@ -19,42 +19,41 @@ class Store
/**
* @param IndexerRegistry $indexerRegistry
*/
- public function __construct(
- IndexerRegistry $indexerRegistry
- ) {
+ public function __construct(IndexerRegistry $indexerRegistry)
+ {
$this->indexerRegistry = $indexerRegistry;
}
/**
* Invalidate design config grid indexer on store creation
*
- * @param StoreStore $subject
- * @param \Closure $proceed
- * @return StoreStore
+ * @param StoreModel $subject
+ * @param StoreModel $result
+ * @return StoreModel
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function aroundSave(StoreStore $subject, \Closure $proceed)
+ public function afterSave(StoreModel $subject, StoreModel $result)
{
- $isObjectNew = $subject->getId() == 0;
- $result = $proceed();
- if ($isObjectNew) {
+ if ($result->isObjectNew()) {
$this->indexerRegistry->get(Config::DESIGN_CONFIG_GRID_INDEXER_ID)->invalidate();
}
+
return $result;
}
/**
* Invalidate design config grid indexer on store removal
*
- * @param StoreStore $subject
- * @param StoreStore $result
- * @return StoreStore
+ * @param StoreModel $subject
+ * @param StoreModel $result
+ * @return StoreModel
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
- public function afterDelete(StoreStore $subject, $result)
+ public function afterDelete(StoreModel $subject, $result)
{
$this->indexerRegistry->get(Config::DESIGN_CONFIG_GRID_INDEXER_ID)->invalidate();
+
return $result;
}
}
diff --git a/app/code/Magento/Theme/Test/Mftf/Test/StoreFrontCheckNotificationMessageContainerTest.xml b/app/code/Magento/Theme/Test/Mftf/Test/StoreFrontCheckNotificationMessageContainerTest.xml
new file mode 100644
index 0000000000000..c60385b768bf3
--- /dev/null
+++ b/app/code/Magento/Theme/Test/Mftf/Test/StoreFrontCheckNotificationMessageContainerTest.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Theme/Test/Unit/Model/Indexer/Design/Config/Plugin/StoreTest.php b/app/code/Magento/Theme/Test/Unit/Model/Indexer/Design/Config/Plugin/StoreTest.php
index b61246cc7583f..1d48c0fe04e7a 100644
--- a/app/code/Magento/Theme/Test/Unit/Model/Indexer/Design/Config/Plugin/StoreTest.php
+++ b/app/code/Magento/Theme/Test/Unit/Model/Indexer/Design/Config/Plugin/StoreTest.php
@@ -9,6 +9,7 @@
use Magento\Framework\Indexer\IndexerInterface;
use Magento\Framework\Indexer\IndexerRegistry;
+use Magento\Store\Model\Store as StoreModel;
use Magento\Theme\Model\Data\Design\Config;
use Magento\Theme\Model\Indexer\Design\Config\Plugin\Store;
use PHPUnit\Framework\MockObject\MockObject;
@@ -17,10 +18,10 @@
class StoreTest extends TestCase
{
/** @var Store */
- protected $model;
+ private $model;
/** @var IndexerRegistry|MockObject */
- protected $indexerRegistryMock;
+ private $indexerRegistryMock;
protected function setUp(): void
{
@@ -31,21 +32,15 @@ protected function setUp(): void
$this->model = new Store($this->indexerRegistryMock);
}
- public function testAroundSave()
+ public function testAfterSave(): void
{
- $subjectId = 0;
-
- /** @var \Magento\Store\Model\Store|MockObject $subjectMock */
- $subjectMock = $this->getMockBuilder(\Magento\Store\Model\Store::class)
+ /** @var StoreModel|MockObject $subjectMock */
+ $subjectMock = $this->getMockBuilder(StoreModel::class)
->disableOriginalConstructor()
->getMock();
$subjectMock->expects($this->once())
- ->method('getId')
- ->willReturn($subjectId);
-
- $closureMock = function () use ($subjectMock) {
- return $subjectMock;
- };
+ ->method('isObjectNew')
+ ->willReturn(true);
/** @var IndexerInterface|MockObject $indexerMock */
$indexerMock = $this->getMockBuilder(IndexerInterface::class)
@@ -58,35 +53,29 @@ public function testAroundSave()
->with(Config::DESIGN_CONFIG_GRID_INDEXER_ID)
->willReturn($indexerMock);
- $this->assertEquals($subjectMock, $this->model->aroundSave($subjectMock, $closureMock));
+ $this->assertSame($subjectMock, $this->model->afterSave($subjectMock, $subjectMock));
}
- public function testAroundSaveWithExistentSubject()
+ public function testAfterSaveWithExistentSubject(): void
{
- $subjectId = 1;
-
- /** @var \Magento\Store\Model\Store|MockObject $subjectMock */
- $subjectMock = $this->getMockBuilder(\Magento\Store\Model\Store::class)
+ /** @var StoreModel|MockObject $subjectMock */
+ $subjectMock = $this->getMockBuilder(StoreModel::class)
->disableOriginalConstructor()
->getMock();
$subjectMock->expects($this->once())
- ->method('getId')
- ->willReturn($subjectId);
-
- $closureMock = function () use ($subjectMock) {
- return $subjectMock;
- };
+ ->method('isObjectNew')
+ ->willReturn(false);
$this->indexerRegistryMock->expects($this->never())
->method('get');
- $this->assertEquals($subjectMock, $this->model->aroundSave($subjectMock, $closureMock));
+ $this->assertSame($subjectMock, $this->model->afterSave($subjectMock, $subjectMock));
}
- public function testAfterDelete()
+ public function testAfterDelete(): void
{
- /** @var \Magento\Store\Model\Store|MockObject $subjectMock */
- $subjectMock = $this->getMockBuilder(\Magento\Store\Model\Store::class)
+ /** @var StoreModel|MockObject $subjectMock */
+ $subjectMock = $this->getMockBuilder(StoreModel::class)
->disableOriginalConstructor()
->getMock();
@@ -101,6 +90,6 @@ public function testAfterDelete()
->with(Config::DESIGN_CONFIG_GRID_INDEXER_ID)
->willReturn($indexerMock);
- $this->assertEquals($subjectMock, $this->model->afterDelete($subjectMock, $subjectMock));
+ $this->assertSame($subjectMock, $this->model->afterDelete($subjectMock, $subjectMock));
}
}
diff --git a/app/code/Magento/Theme/view/frontend/templates/messages.phtml b/app/code/Magento/Theme/view/frontend/templates/messages.phtml
index 85e752635fb3a..f863da70e8987 100644
--- a/app/code/Magento/Theme/view/frontend/templates/messages.phtml
+++ b/app/code/Magento/Theme/view/frontend/templates/messages.phtml
@@ -6,7 +6,7 @@
?>
-
+
+
-
-
+
@@ -62,8 +61,7 @@
-
-
+
diff --git a/app/code/Magento/Translation/view/frontend/requirejs-config.js b/app/code/Magento/Translation/view/frontend/requirejs-config.js
index b4b3ce0f8c554..9a99d49eddbcf 100644
--- a/app/code/Magento/Translation/view/frontend/requirejs-config.js
+++ b/app/code/Magento/Translation/view/frontend/requirejs-config.js
@@ -10,8 +10,5 @@ var config = {
addClass: 'Magento_Translation/js/add-class',
'Magento_Translation/add-class': 'Magento_Translation/js/add-class'
}
- },
- deps: [
- 'mage/translate-inline'
- ]
+ }
};
diff --git a/app/code/Magento/Ui/Component/Form/Element/DataType/Media/OpenDialogUrl.php b/app/code/Magento/Ui/Component/Form/Element/DataType/Media/OpenDialogUrl.php
index 27370cbfbd68c..9961fc41fc70d 100644
--- a/app/code/Magento/Ui/Component/Form/Element/DataType/Media/OpenDialogUrl.php
+++ b/app/code/Magento/Ui/Component/Form/Element/DataType/Media/OpenDialogUrl.php
@@ -8,10 +8,8 @@
namespace Magento\Ui\Component\Form\Element\DataType\Media;
-use Magento\Framework\DataObject;
-
/**
- * Basic configuration for OdenDialogUrl
+ * Basic configuration for OpenDialogUrl
*/
class OpenDialogUrl
{
@@ -23,11 +21,11 @@ class OpenDialogUrl
private $openDialogUrl;
/**
- * @param DataObject $url
+ * @param string $url
*/
- public function __construct(DataObject $url = null)
+ public function __construct(string $url = null)
{
- $this->openDialogUrl = $url;
+ $this->openDialogUrl = $url ?? self::DEFAULT_OPEN_DIALOG_URL;
}
/**
@@ -37,9 +35,6 @@ public function __construct(DataObject $url = null)
*/
public function get(): string
{
- if ($this->openDialogUrl) {
- return $this->openDialogUrl->getUrl();
- }
- return self::DEFAULT_OPEN_DIALOG_URL;
+ return $this->openDialogUrl;
}
}
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridBulkActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridBulkActionGroup.xml
new file mode 100644
index 0000000000000..9db9ea7becfc8
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridBulkActionGroup.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ Massive action for all rows on Admin Grid page.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridColumnShowActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridColumnShowActionGroup.xml
new file mode 100644
index 0000000000000..6440cc01bcafe
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridColumnShowActionGroup.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ Shows new column on Admin Grid page.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridSelectAllActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridSelectAllActionGroup.xml
new file mode 100644
index 0000000000000..bbfb7e46d89ec
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridSelectAllActionGroup.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ Click on select all option on the grid
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
index fcee31c0bd80c..c5b000259e265 100644
--- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
+++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml
@@ -9,7 +9,7 @@
-
+
diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml
new file mode 100644
index 0000000000000..c7236c33e7cc0
--- /dev/null
+++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
index 5f29c5982e094..0ac35df78e001 100644
--- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
+++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
@@ -1126,13 +1126,17 @@ define([
* Update whether value differs from default value
*/
setDifferedFromDefault: function () {
- var recordData = utils.copy(this.recordData());
+ var recordData;
- Array.isArray(recordData) && recordData.forEach(function (item) {
- delete item['record_id'];
- });
+ if (this.default) {
+ recordData = utils.copy(this.recordData());
+
+ Array.isArray(recordData) && recordData.forEach(function (item) {
+ delete item['record_id'];
+ });
- this.isDifferedFromDefault(!_.isEqual(recordData, this.default));
+ this.isDifferedFromDefault(!_.isEqual(recordData, this.default));
+ }
},
/**
diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js
index 9a34e57df86c7..65443fadf8007 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js
@@ -668,7 +668,7 @@ define([
* @returns {Object} Chainable
*/
toggleListVisible: function () {
- this.listVisible(!this.listVisible());
+ this.listVisible(!this.disabled() && !this.listVisible());
return this;
},
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js
index d675bd7a60ab5..7dcf0994ef56b 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js
@@ -2,6 +2,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+/* eslint-disable no-undef */
define([
'jquery',
'Magento_Ui/js/grid/columns/column',
@@ -32,7 +33,8 @@ define([
listens: {
'${ $.provider }:params.filters': 'hide',
'${ $.provider }:params.search': 'hide',
- '${ $.provider }:params.paging': 'hide'
+ '${ $.provider }:params.paging': 'hide',
+ '${ $.provider }:data.items': 'updateDisplayedRecord'
},
exports: {
height: '${ $.parentName }.thumbnail_url:previewHeight'
@@ -48,6 +50,25 @@ define([
this._super();
$(document).on('keydown', this.handleKeyDown.bind(this));
+ this.lastOpenedImage.subscribe(function (newValue) {
+
+ if (newValue === false && _.isNull(this.visibleRecord())) {
+ return;
+ }
+
+ if (newValue === this.visibleRecord()) {
+ return;
+ }
+
+ if (newValue === false) {
+ this.hide();
+
+ return;
+ }
+
+ this.show(this.masonry().rows()[newValue]);
+ }.bind(this));
+
return this;
},
@@ -128,8 +149,6 @@ define([
* @param {Object} record
*/
show: function (record) {
- var img;
-
if (record._rowIndex === this.visibleRecord()) {
this.hide();
@@ -141,9 +160,21 @@ define([
this._selectRow(record.rowNumber || null);
this.visibleRecord(record._rowIndex);
- img = $(this.previewImageSelector + ' img');
+ this.lastOpenedImage(record._rowIndex);
+ this.updateImageData();
+ },
- if (img.get(0).complete) {
+ /**
+ * Update image data when image preview is opened
+ */
+ updateImageData: function () {
+ var img = $(this.previewImageSelector + ' img');
+
+ if (!img.get(0)) {
+ setTimeout(function () {
+ this.updateImageData();
+ }.bind(this), 100);
+ } else if (img.get(0).complete) {
this.updateHeight();
this.scrollToPreview();
} else {
@@ -152,8 +183,17 @@ define([
this.scrollToPreview();
}.bind(this));
}
+ },
- this.lastOpenedImage(record._rowIndex);
+ /**
+ * Update preview displayed record data from the new items data if the preview is expanded
+ *
+ * @param {Array} items
+ */
+ updateDisplayedRecord: function (items) {
+ if (!_.isNull(this.visibleRecord())) {
+ this.displayedRecord(items[this.visibleRecord()]);
+ }
},
/**
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
index ba0f4d25c25a4..828bbccee0478 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/multiselect.js
@@ -52,6 +52,7 @@ define([
listens: {
'${ $.provider }:params.filters': 'onFilter',
+ '${ $.provider }:params.search': 'onSearch',
selected: 'onSelectedChange',
rows: 'onRowsChange'
},
@@ -235,7 +236,7 @@ define([
* @returns {Multiselect} Chainable.
*/
togglePage: function () {
- return this.isPageSelected() ? this.deselectPage() : this.selectPage();
+ return this.isPageSelected() && !this.excluded().length ? this.deselectPage() : this.selectPage();
},
/**
@@ -496,6 +497,13 @@ define([
if (!this.preserveSelectionsOnFilter) {
this.deselectAll();
}
+ },
+
+ /**
+ * Is invoked when search is applied or removed
+ */
+ onSearch: function () {
+ this.onFilter();
}
});
});
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/elements/ui-select.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/elements/ui-select.js
new file mode 100644
index 0000000000000..a913f3fa4a042
--- /dev/null
+++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/elements/ui-select.js
@@ -0,0 +1,88 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+ 'Magento_Ui/js/form/element/ui-select',
+ 'jquery',
+ 'underscore'
+], function (Select, $, _) {
+ 'use strict';
+
+ return Select.extend({
+ defaults: {
+ bookmarkProvider: 'ns = ${ $.ns }, index = bookmarks',
+ filterChipsProvider: 'componentType = filters, ns = ${ $.ns }',
+ validationUrl: false,
+ loadedOption: [],
+ validationLoading: true,
+ imports: {
+ activeIndex: '${ $.bookmarkProvider }:activeIndex'
+ },
+ modules: {
+ filterChips: '${ $.filterChipsProvider }'
+ },
+ listens: {
+ activeIndex: 'validateInitialValue'
+ }
+
+ },
+
+ /**
+ * Initializes UiSelect component.
+ *
+ * @returns {UiSelect} Chainable.
+ */
+ initialize: function () {
+ this._super();
+
+ this.validateInitialValue();
+
+ return this;
+ },
+
+ /**
+ * Validate initial value actually exists
+ */
+ validateInitialValue: function () {
+ if (_.isEmpty(this.value())) {
+ this.validationLoading(false);
+
+ return;
+ }
+
+ $.ajax({
+ url: this.validationUrl,
+ type: 'GET',
+ dataType: 'json',
+ context: this,
+ data: {
+ ids: this.value()
+ },
+
+ /** @param {Object} response */
+ success: function (response) {
+ if (!_.isEmpty(response)) {
+ this.options([]);
+ this.success({
+ options: response
+ });
+ }
+ this.filterChips().updateActive();
+ },
+
+ /** set empty array if error occurs */
+ error: function () {
+ this.options([]);
+ },
+
+ /** stop loader */
+ complete: function () {
+ this.validationLoading(false);
+ this.setCaption();
+ }
+ });
+ }
+ });
+});
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
index fe33389eabad4..848ad60219a2b 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js
@@ -200,6 +200,7 @@ define([
* @returns {Filters} Chainable.
*/
apply: function () {
+ $('body').notification('clear');
this.set('applied', removeEmpty(this.filters));
return this;
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js b/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js
index 1f870e9e819a1..3c5e72d4d66ed 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js
@@ -5,17 +5,20 @@
define([
'uiComponent',
- 'underscore'
-], function (Component, _) {
+ 'underscore',
+ 'jquery'
+], function (Component, _, $) {
'use strict';
return Component.extend({
defaults: {
listingNamespace: null,
+ bookmarkProvider: 'componentType = bookmark, ns = ${ $.listingNamespace }',
filterProvider: 'componentType = filters, ns = ${ $.listingNamespace }',
filterKey: 'filters',
searchString: location.search,
modules: {
+ bookmarks: '${ $.bookmarkProvider }',
filterComponent: '${ $.filterProvider }'
}
},
@@ -36,7 +39,9 @@ define([
* Apply filter
*/
apply: function () {
- var urlFilter = this.getFilterParam(this.searchString);
+ var urlFilter = this.getFilterParam(this.searchString),
+ applied,
+ filters;
if (_.isUndefined(this.filterComponent())) {
setTimeout(function () {
@@ -46,9 +51,20 @@ define([
return;
}
+ if (!_.isUndefined(this.bookmarks())) {
+ if (!_.size(this.bookmarks().getViewData(this.bookmarks().defaultIndex))) {
+ setTimeout(function () {
+ this.apply();
+ }.bind(this), 500);
+
+ return;
+ }
+ }
+
if (Object.keys(urlFilter).length) {
- this.filterComponent().setData(urlFilter, false);
- this.filterComponent().apply();
+ applied = this.filterComponent().get('applied');
+ filters = $.extend({}, applied, urlFilter);
+ this.filterComponent().set('applied', filters);
}
},
diff --git a/app/code/Magento/Ui/view/frontend/web/template/messages.html b/app/code/Magento/Ui/view/frontend/web/template/messages.html
index 0a8f672765b3c..c094d9d58bb75 100644
--- a/app/code/Magento/Ui/view/frontend/web/template/messages.html
+++ b/app/code/Magento/Ui/view/frontend/web/template/messages.html
@@ -6,12 +6,12 @@
-->
-
+
-
+
diff --git a/app/code/Magento/User/i18n/en_US.csv b/app/code/Magento/User/i18n/en_US.csv
index 064b6428387fe..cd550015401d0 100644
--- a/app/code/Magento/User/i18n/en_US.csv
+++ b/app/code/Magento/User/i18n/en_US.csv
@@ -106,8 +106,8 @@ username,username
Custom,Custom
All,All
Resources,Resources
-"Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?"
-"Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?"
+"Warning! This action will remove this user from already assigned role. Are you sure?","Warning! This action will remove this user from already assigned role. Are you sure?"
+"Warning! This action will remove those users from already assigned roles. Are you sure?","Warning! This action will remove those users from already assigned roles. Are you sure?"
"Password Reset Confirmation for %name","Password Reset Confirmation for %name"
"%name,","%name,"
"There was recently a request to change the password for your account.","There was recently a request to change the password for your account."
diff --git a/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml b/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml
index 2042479832898..b0107a53593d3 100644
--- a/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml
+++ b/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml
@@ -51,8 +51,8 @@ if (is_object($myBlock) && $myBlock->getJsObjectName()):
if (checked) {
confirm({
- content: "{$myBlock->escapeJs(__('Warning!\r\nThis action will remove this user from already ' .
- 'assigned role\r\nAre you sure?'))}",
+ content: "{$myBlock->escapeJs(__('Warning! This action will remove this user from already ' .
+ 'assigned role. Are you sure?'))}",
actions: {
confirm: function () {
checkbox[0].checked = false;
@@ -102,7 +102,7 @@ if (is_object($myBlock) && $myBlock->getJsObjectName()):
allCheckbox.checked = true;
confirm({
content: "{$myBlock->escapeJs(
- __('Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?')
+ __('Warning! This action will remove those users from already assigned roles. Are you sure?')
)}",
actions: {
confirm: function () {
diff --git a/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml b/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml
index 71a866f945693..7455c26334c02 100644
--- a/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml
+++ b/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml
@@ -45,7 +45,7 @@ if (is_object($myBlock) && $myBlock->getJsObjectName()):
var checked = isInput ? checkbox[0].checked : !checkbox[0].checked;
if (checked && warning && radioBoxes.size() > 0) {
if ( !confirm("{$myBlock->escapeJs(
- __('Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?')
+ __('Warning! This action will remove this user from already assigned role. Are you sure?')
)}") ) {
checkbox[0].checked = false;
for(i in radioBoxes) {
diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php
index 195c3f397ff18..b05b70cfcbc71 100644
--- a/app/code/Magento/Widget/Model/Widget.php
+++ b/app/code/Magento/Widget/Model/Widget.php
@@ -5,6 +5,16 @@
*/
namespace Magento\Widget\Model;
+use Magento\Framework\App\Cache\Type\Config;
+use Magento\Framework\DataObject;
+use Magento\Framework\Escaper;
+use Magento\Framework\Math\Random;
+use Magento\Framework\View\Asset\Repository;
+use Magento\Framework\View\Asset\Source;
+use Magento\Framework\View\FileSystem;
+use Magento\Widget\Helper\Conditions;
+use Magento\Widget\Model\Config\Data;
+
/**
* Widget model for different purposes
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -15,32 +25,32 @@
class Widget
{
/**
- * @var \Magento\Widget\Model\Config\Data
+ * @var Data
*/
protected $dataStorage;
/**
- * @var \Magento\Framework\App\Cache\Type\Config
+ * @var Config
*/
protected $configCacheType;
/**
- * @var \Magento\Framework\View\Asset\Repository
+ * @var Repository
*/
protected $assetRepo;
/**
- * @var \Magento\Framework\View\Asset\Source
+ * @var Source
*/
protected $assetSource;
/**
- * @var \Magento\Framework\View\FileSystem
+ * @var FileSystem
*/
protected $viewFileSystem;
/**
- * @var \Magento\Framework\Escaper
+ * @var Escaper
*/
protected $escaper;
@@ -50,30 +60,35 @@ class Widget
protected $widgetsArray = [];
/**
- * @var \Magento\Widget\Helper\Conditions
+ * @var Conditions
*/
protected $conditionsHelper;
/**
- * @var \Magento\Framework\Math\Random
+ * @var Random
*/
private $mathRandom;
/**
- * @param \Magento\Framework\Escaper $escaper
- * @param \Magento\Widget\Model\Config\Data $dataStorage
- * @param \Magento\Framework\View\Asset\Repository $assetRepo
- * @param \Magento\Framework\View\Asset\Source $assetSource
- * @param \Magento\Framework\View\FileSystem $viewFileSystem
- * @param \Magento\Widget\Helper\Conditions $conditionsHelper
+ * @var string[]
+ */
+ private $reservedChars = ['}', '{'];
+
+ /**
+ * @param Escaper $escaper
+ * @param Data $dataStorage
+ * @param Repository $assetRepo
+ * @param Source $assetSource
+ * @param FileSystem $viewFileSystem
+ * @param Conditions $conditionsHelper
*/
public function __construct(
- \Magento\Framework\Escaper $escaper,
- \Magento\Widget\Model\Config\Data $dataStorage,
- \Magento\Framework\View\Asset\Repository $assetRepo,
- \Magento\Framework\View\Asset\Source $assetSource,
- \Magento\Framework\View\FileSystem $viewFileSystem,
- \Magento\Widget\Helper\Conditions $conditionsHelper
+ Escaper $escaper,
+ Data $dataStorage,
+ Repository $assetRepo,
+ Source $assetSource,
+ FileSystem $viewFileSystem,
+ Conditions $conditionsHelper
) {
$this->escaper = $escaper;
$this->dataStorage = $dataStorage;
@@ -110,14 +125,11 @@ public function getWidgetByClassType($type)
$widgets = $this->getWidgets();
/** @var array $widget */
foreach ($widgets as $widget) {
- if (isset($widget['@'])) {
- if (isset($widget['@']['type'])) {
- if ($type === $widget['@']['type']) {
- return $widget;
- }
- }
+ if (isset($widget['@']['type']) && $type === $widget['@']['type']) {
+ return $widget;
}
}
+
return null;
}
@@ -131,6 +143,7 @@ public function getWidgetByClassType($type)
*/
public function getConfigAsXml($type)
{
+ // phpstan:ignore
return $this->getXmlElementByType($type);
}
@@ -296,42 +309,70 @@ public function getWidgetsArray($filters = [])
*/
public function getWidgetDeclaration($type, $params = [], $asIs = true)
{
- $directive = '{{widget type="' . $type . '"';
$widget = $this->getConfigAsObject($type);
+ $params = array_filter($params, function ($value) {
+ return $value !== null && $value !== '';
+ });
+
+ $directiveParams = '';
foreach ($params as $name => $value) {
// Retrieve default option value if pre-configured
- if ($name == 'conditions') {
- $name = 'conditions_encoded';
- $value = $this->conditionsHelper->encode($value);
- } elseif (is_array($value)) {
- $value = implode(',', $value);
- } elseif (trim($value) == '') {
- $parameters = $widget->getParameters();
- if (isset($parameters[$name]) && is_object($parameters[$name])) {
- $value = $parameters[$name]->getValue();
- }
- }
- if (isset($value)) {
- $directive .= sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false));
- }
+ $directiveParams .= $this->getDirectiveParam($widget, $name, $value);
}
- $directive .= $this->getWidgetPageVarName($params);
-
- $directive .= '}}';
+ $directive = sprintf('{{widget type="%s"%s%s}}', $type, $directiveParams, $this->getWidgetPageVarName($params));
if ($asIs) {
return $directive;
}
- $html = sprintf(
+ return sprintf(
' ',
$this->idEncode($directive),
$this->getPlaceholderImageUrl($type),
$this->escaper->escapeUrl($directive)
);
- return $html;
+ }
+
+ /**
+ * Returns directive param with prepared value
+ *
+ * @param DataObject $widget
+ * @param string $name
+ * @param string|array $value
+ * @return string
+ */
+ private function getDirectiveParam(DataObject $widget, string $name, $value): string
+ {
+ if ($name === 'conditions') {
+ $name = 'conditions_encoded';
+ $value = $this->conditionsHelper->encode($value);
+ } elseif (is_array($value)) {
+ $value = implode(',', $value);
+ } elseif (trim($value) === '') {
+ $parameters = $widget->getParameters();
+ if (isset($parameters[$name]) && is_object($parameters[$name])) {
+ $value = $parameters[$name]->getValue();
+ }
+ } else {
+ $value = $this->getPreparedValue($value);
+ }
+
+ return sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false));
+ }
+
+ /**
+ * Returns encoded value if it contains reserved chars
+ *
+ * @param string $value
+ * @return string
+ */
+ private function getPreparedValue(string $value): string
+ {
+ $pattern = sprintf('/%s/', implode('|', $this->reservedChars));
+
+ return preg_match($pattern, $value) ? rawurlencode($value) : $value;
}
/**
diff --git a/app/code/Magento/Wishlist/Controller/Shared/Allcart.php b/app/code/Magento/Wishlist/Controller/Shared/Allcart.php
index 6300b14dcf515..89413eff8323f 100644
--- a/app/code/Magento/Wishlist/Controller/Shared/Allcart.php
+++ b/app/code/Magento/Wishlist/Controller/Shared/Allcart.php
@@ -3,13 +3,24 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
+declare(strict_types=1);
+
namespace Magento\Wishlist\Controller\Shared;
+use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
-use Magento\Wishlist\Model\ItemCarrier;
+use Magento\Framework\App\Action\HttpGetActionInterface;
+use Magento\Framework\App\Action\HttpPostActionInterface;
+use Magento\Framework\Controller\Result\Forward;
+use Magento\Framework\Controller\Result\Redirect;
use Magento\Framework\Controller\ResultFactory;
+use Magento\Wishlist\Model\ItemCarrier;
-class Allcart extends \Magento\Framework\App\Action\Action
+/**
+ * Wishlist Allcart Controller
+ */
+class Allcart extends Action implements HttpGetActionInterface, HttpPostActionInterface
{
/**
* @var WishlistProvider
@@ -17,7 +28,7 @@ class Allcart extends \Magento\Framework\App\Action\Action
protected $wishlistProvider;
/**
- * @var \Magento\Wishlist\Model\ItemCarrier
+ * @var ItemCarrier
*/
protected $itemCarrier;
@@ -39,21 +50,22 @@ public function __construct(
/**
* Add all items from wishlist to shopping cart
*
- * @return \Magento\Framework\Controller\ResultInterface
+ * {@inheritDoc}
*/
public function execute()
{
$wishlist = $this->wishlistProvider->getWishlist();
if (!$wishlist) {
- /** @var \Magento\Framework\Controller\Result\Forward $resultForward */
+ /** @var Forward $resultForward */
$resultForward = $this->resultFactory->create(ResultFactory::TYPE_FORWARD);
$resultForward->forward('noroute');
return $resultForward;
}
$redirectUrl = $this->itemCarrier->moveAllToCart($wishlist, $this->getRequest()->getParam('qty'));
- /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */
+ /** @var Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
$resultRedirect->setUrl($redirectUrl);
+
return $resultRedirect;
}
}
diff --git a/app/code/Magento/Wishlist/Controller/Shared/Cart.php b/app/code/Magento/Wishlist/Controller/Shared/Cart.php
index c0a394ce9d762..939cbe3a2c46f 100644
--- a/app/code/Magento/Wishlist/Controller/Shared/Cart.php
+++ b/app/code/Magento/Wishlist/Controller/Shared/Cart.php
@@ -13,7 +13,7 @@
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context as ActionContext;
use Magento\Framework\App\Action\HttpPostActionInterface;
-use Magento\Framework\Controller\Result\Redirect;
+use Magento\Framework\Controller\Result\Redirect as ResultRedirect;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Escaper;
use Magento\Framework\Exception\LocalizedException;
@@ -124,9 +124,11 @@ public function execute()
} catch (\Exception $e) {
$this->messageManager->addExceptionMessage($e, __('We can\'t add the item to the cart right now.'));
}
- /** @var Redirect $resultRedirect */
+
+ /** @var ResultRedirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
$resultRedirect->setUrl($redirectUrl);
+
return $resultRedirect;
}
}
diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php
index 5d9b1911bc292..ce1a1cdc2942d 100644
--- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php
+++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php
@@ -7,7 +7,7 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer;
-use Magento\CatalogInventory\Model\Stock;
+use Magento\CatalogInventory\Model\ResourceModel\StockStatusFilterInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Sales\Model\ConfigInterface;
@@ -162,6 +162,17 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
* @var CollectionBuilderInterface
*/
private $productCollectionBuilder;
+ /**
+ * @var StockStatusFilterInterface
+ */
+ private $stockStatusFilter;
+
+ /**
+ * Whether product table is joined in select
+ *
+ * @var bool
+ */
+ private $isProductTableJoined = false;
/**
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
@@ -181,10 +192,11 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
* @param \Magento\Catalog\Model\Entity\AttributeFactory $catalogAttrFactory
* @param \Magento\Wishlist\Model\ResourceModel\Item $resource
* @param \Magento\Framework\App\State $appState
- * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
+ * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
* @param TableMaintainer|null $tableMaintainer
* @param ConfigInterface|null $salesConfig
* @param CollectionBuilderInterface|null $productCollectionBuilder
+ * @param StockStatusFilterInterface|null $stockStatusFilter
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -208,7 +220,8 @@ public function __construct(
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
TableMaintainer $tableMaintainer = null,
ConfigInterface $salesConfig = null,
- ?CollectionBuilderInterface $productCollectionBuilder = null
+ ?CollectionBuilderInterface $productCollectionBuilder = null,
+ ?StockStatusFilterInterface $stockStatusFilter = null
) {
$this->stockConfiguration = $stockConfiguration;
$this->_adminhtmlSales = $adminhtmlSales;
@@ -227,6 +240,8 @@ public function __construct(
$this->salesConfig = $salesConfig ?: ObjectManager::getInstance()->get(ConfigInterface::class);
$this->productCollectionBuilder = $productCollectionBuilder
?: ObjectManager::getInstance()->get(CollectionBuilderInterface::class);
+ $this->stockStatusFilter = $stockStatusFilter
+ ?: ObjectManager::getInstance()->get(StockStatusFilterInterface::class);
}
/**
@@ -368,15 +383,8 @@ protected function _renderFiltersBefore()
$connection = $this->getConnection();
if ($this->_productInStock && !$this->stockConfiguration->isShowOutOfStock()) {
- $inStockConditions = [
- "stockItem.product_id = {$mainTableName}.product_id",
- $connection->quoteInto('stockItem.stock_status = ?', Stock::STOCK_IN_STOCK),
- ];
- $this->getSelect()->join(
- ['stockItem' => $this->getTable('cataloginventory_stock_status')],
- join(' AND ', $inStockConditions),
- []
- );
+ $this->joinProductTable();
+ $this->stockStatusFilter->execute($this->getSelect(), 'product_entity', 'stockItem');
}
if ($this->_productVisible) {
@@ -398,7 +406,11 @@ protected function _renderFiltersBefore()
$availableProductTypes = $this->salesConfig->getAvailableProductTypes();
$this->getSelect()->join(
['cat_prod' => $this->getTable('catalog_product_entity')],
- $this->getConnection()->quoteInto('cat_prod.type_id IN (?)', $availableProductTypes),
+ $this->getConnection()
+ ->quoteInto(
+ "cat_prod.type_id IN (?) AND {$mainTableName}.product_id = cat_prod.entity_id",
+ $availableProductTypes
+ ),
[]
);
}
@@ -583,12 +595,9 @@ protected function _joinProductNameTable()
$entityMetadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
$linkField = $entityMetadata->getLinkField();
+ $this->joinProductTable();
$this->getSelect()->join(
- ['product_entity' => $this->getTable('catalog_product_entity')],
- 'product_entity.entity_id = main_table.product_id',
- []
- )->join(
['product_name_table' => $attribute->getBackendTable()],
'product_name_table.' . $linkField . ' = product_entity.' . $linkField .
' AND product_name_table.store_id = ' .
@@ -673,4 +682,21 @@ protected function _afterLoadData()
return $this;
}
+
+ /**
+ * Join product table to select if not already joined
+ *
+ * @return void
+ */
+ private function joinProductTable(): void
+ {
+ if (!$this->isProductTableJoined) {
+ $this->getSelect()->join(
+ ['product_entity' => $this->getTable('catalog_product_entity')],
+ 'product_entity.entity_id = main_table.product_id',
+ []
+ );
+ $this->isProductTableJoined = true;
+ }
+ }
}
diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php
index 437b3c757f9cf..f544dd374d734 100644
--- a/app/code/Magento/Wishlist/Model/Wishlist.php
+++ b/app/code/Magento/Wishlist/Model/Wishlist.php
@@ -12,9 +12,8 @@
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ProductFactory;
-use Magento\CatalogInventory\Api\Data\StockItemInterface;
+use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
-use Magento\CatalogInventory\Model\Configuration;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\DataObject;
@@ -27,7 +26,6 @@
use Magento\Framework\Registry;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\Stdlib\DateTime;
-use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\Store;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Wishlist\Helper\Data;
@@ -150,14 +148,9 @@ class Wishlist extends AbstractModel implements IdentityInterface
private $serializer;
/**
- * @var ScopeConfigInterface
+ * @var StockConfigurationInterface
*/
- private $scopeConfig;
-
- /**
- * @var StockRegistryInterface|null
- */
- private $stockRegistry;
+ private $stockConfiguration;
/**
* Constructor
@@ -181,8 +174,9 @@ class Wishlist extends AbstractModel implements IdentityInterface
* @param Json|null $serializer
* @param StockRegistryInterface|null $stockRegistry
* @param ScopeConfigInterface|null $scopeConfig
- *
+ * @param StockConfigurationInterface|null $stockConfiguration
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
Context $context,
@@ -203,7 +197,8 @@ public function __construct(
array $data = [],
Json $serializer = null,
StockRegistryInterface $stockRegistry = null,
- ScopeConfigInterface $scopeConfig = null
+ ScopeConfigInterface $scopeConfig = null,
+ ?StockConfigurationInterface $stockConfiguration = null
) {
$this->_useCurrentWebsite = $useCurrentWebsite;
$this->_catalogProduct = $catalogProduct;
@@ -218,8 +213,8 @@ public function __construct(
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
$this->productRepository = $productRepository;
- $this->scopeConfig = $scopeConfig ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class);
- $this->stockRegistry = $stockRegistry ?: ObjectManager::getInstance()->get(StockRegistryInterface::class);
+ $this->stockConfiguration = $stockConfiguration
+ ?: ObjectManager::getInstance()->get(StockConfigurationInterface::class);
}
/**
@@ -467,7 +462,7 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false
throw new LocalizedException(__('Cannot specify product.'));
}
- if ($this->isInStock($productId)) {
+ if (!$this->stockConfiguration->isShowOutOfStock($storeId) && !$product->getIsSalable()) {
throw new LocalizedException(__('Cannot add product without stock to wishlist.'));
}
@@ -672,25 +667,6 @@ public function isSalable()
return false;
}
- /**
- * Retrieve if product has stock or config is set for showing out of stock products
- *
- * @param int $productId
- *
- * @return bool
- */
- private function isInStock($productId)
- {
- /** @var StockItemInterface $stockItem */
- $stockItem = $this->stockRegistry->getStockItem($productId);
- $showOutOfStock = $this->scopeConfig->isSetFlag(
- Configuration::XML_PATH_SHOW_OUT_OF_STOCK,
- ScopeInterface::SCOPE_STORE
- );
- $isInStock = $stockItem ? $stockItem->getIsInStock() : false;
- return !$isInStock && !$showOutOfStock;
- }
-
/**
* Check customer is owner this wishlist
*
diff --git a/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php b/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php
index aef3cbf571ff6..622f072e8d668 100644
--- a/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php
+++ b/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php
@@ -24,7 +24,7 @@ class WishlistItemFactory
public function create(array $data): WishlistItem
{
return new WishlistItem(
- $data['quantity'],
+ $data['quantity'] ?? 0,
$data['sku'] ?? null,
$data['parent_sku'] ?? null,
isset($data['wishlist_item_id']) ? (int) $data['wishlist_item_id'] : null,
diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminDeleteCustomerWishListItemTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminDeleteCustomerWishListItemTest.xml
index 28a17d30aea2b..a2219d5145f17 100644
--- a/app/code/Magento/Wishlist/Test/Mftf/Test/AdminDeleteCustomerWishListItemTest.xml
+++ b/app/code/Magento/Wishlist/Test/Mftf/Test/AdminDeleteCustomerWishListItemTest.xml
@@ -23,7 +23,6 @@
-
diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php
index eea3346e8e81b..d9339af8144f4 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/AllcartTest.php
@@ -3,6 +3,7 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
declare(strict_types=1);
namespace Magento\Wishlist\Test\Unit\Controller\Shared;
@@ -20,83 +21,60 @@
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+/**
+ * Test for \Magento\Wishlist\Controller\Shared\Allcart.
+ */
class AllcartTest extends TestCase
{
/**
* @var Allcart
*/
- protected $allcartController;
-
- /**
- * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
- */
- protected $objectManagerHelper;
-
- /**
- * @var Context
- */
- protected $context;
+ private $allcartController;
/**
* @var WishlistProvider|MockObject
*/
- protected $wishlistProviderMock;
+ private $wishlistProviderMock;
/**
* @var ItemCarrier|MockObject
*/
- protected $itemCarrierMock;
+ private $itemCarrierMock;
/**
* @var Wishlist|MockObject
*/
- protected $wishlistMock;
+ private $wishlistMock;
/**
* @var Http|MockObject
*/
- protected $requestMock;
-
- /**
- * @var ResultFactory|MockObject
- */
- protected $resultFactoryMock;
+ private $requestMock;
/**
* @var Redirect|MockObject
*/
- protected $resultRedirectMock;
+ private $resultRedirectMock;
/**
* @var Forward|MockObject
*/
- protected $resultForwardMock;
+ private $resultForwardMock;
+ /**
+ * @inheritDoc
+ */
protected function setUp(): void
{
- $this->wishlistProviderMock = $this->getMockBuilder(WishlistProvider::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->itemCarrierMock = $this->getMockBuilder(ItemCarrier::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->wishlistMock = $this->getMockBuilder(Wishlist::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->requestMock = $this->getMockBuilder(Http::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->resultFactoryMock = $this->getMockBuilder(ResultFactory::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->resultRedirectMock = $this->getMockBuilder(Redirect::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->resultForwardMock = $this->getMockBuilder(Forward::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->resultFactoryMock->expects($this->any())
+ $this->wishlistProviderMock = $this->createMock(WishlistProvider::class);
+ $this->itemCarrierMock = $this->createMock(ItemCarrier::class);
+ $this->wishlistMock = $this->createMock(Wishlist::class);
+ $this->requestMock = $this->createMock(Http::class);
+ $resultFactoryMock = $this->createMock(ResultFactory::class);
+ $this->resultRedirectMock = $this->createMock(Redirect::class);
+ $this->resultForwardMock = $this->createMock(Forward::class);
+
+ $resultFactoryMock->expects($this->any())
->method('create')
->willReturnMap(
[
@@ -105,18 +83,18 @@ protected function setUp(): void
]
);
- $this->objectManagerHelper = new ObjectManagerHelper($this);
- $this->context = $this->objectManagerHelper->getObject(
+ $objectManagerHelper = new ObjectManagerHelper($this);
+ $context = $objectManagerHelper->getObject(
Context::class,
[
'request' => $this->requestMock,
- 'resultFactory' => $this->resultFactoryMock
+ 'resultFactory' => $resultFactoryMock
]
);
- $this->allcartController = $this->objectManagerHelper->getObject(
+ $this->allcartController = $objectManagerHelper->getObject(
Allcart::class,
[
- 'context' => $this->context,
+ 'context' => $context,
'wishlistProvider' => $this->wishlistProviderMock,
'itemCarrier' => $this->itemCarrierMock
]
diff --git a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php
index 923b33ef4748b..e6a127457a6c6 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Controller/Shared/CartTest.php
@@ -8,6 +8,7 @@
namespace Magento\Wishlist\Test\Unit\Controller\Shared;
use Magento\Catalog\Model\Product;
+use Magento\Catalog\Model\Product\Exception;
use Magento\Checkout\Helper\Cart as CartHelper;
use Magento\Checkout\Model\Cart;
use Magento\Framework\App\Action\Context as ActionContext;
@@ -29,156 +30,146 @@
use PHPUnit\Framework\TestCase;
/**
+ * Test for \Magento\Wishlist\Controller\Shared\Cart.
+ *
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CartTest extends TestCase
{
- /** @var SharedCart|MockObject */
- protected $model;
-
- /** @var RequestInterface|MockObject */
- protected $request;
-
- /** @var ManagerInterface|MockObject */
- protected $messageManager;
-
- /** @var ActionContext|MockObject */
- protected $context;
-
- /** @var Cart|MockObject */
- protected $cart;
+ /**
+ * @var SharedCart|MockObject
+ */
+ private $model;
- /** @var CartHelper|MockObject */
- protected $cartHelper;
+ /**
+ * @var RequestInterface|MockObject
+ */
+ private $request;
- /** @var Quote|MockObject */
- protected $quote;
+ /**
+ * @var ManagerInterface|MockObject
+ */
+ private $messageManager;
- /** @var OptionCollection|MockObject */
- protected $optionCollection;
+ /**
+ * @var Cart|MockObject
+ */
+ private $cart;
- /** @var OptionFactory|MockObject */
- protected $optionFactory;
+ /**
+ * @var CartHelper|MockObject
+ */
+ private $cartHelper;
- /** @var Option|MockObject */
- protected $option;
+ /**
+ * @var Quote|MockObject
+ */
+ private $quote;
- /** @var ItemFactory|MockObject */
- protected $itemFactory;
+ /**
+ * @var OptionCollection|MockObject
+ */
+ private $optionCollection;
- /** @var Item|MockObject */
- protected $item;
+ /**
+ * @var Option|MockObject
+ */
+ private $option;
- /** @var Escaper|MockObject */
- protected $escaper;
+ /**
+ * @var Item|MockObject
+ */
+ private $item;
- /** @var RedirectInterface|MockObject */
- protected $redirect;
+ /**
+ * @var Escaper|MockObject
+ */
+ private $escaper;
- /** @var ResultFactory|MockObject */
- protected $resultFactory;
+ /**
+ * @var RedirectInterface|MockObject
+ */
+ private $redirect;
- /** @var Redirect|MockObject */
- protected $resultRedirect;
+ /**
+ * @var Redirect|MockObject
+ */
+ private $resultRedirect;
- /** @var Product|MockObject */
- protected $product;
+ /**
+ * @var Product|MockObject
+ */
+ private $product;
+ /**
+ * @inheritDoc
+ */
protected function setUp(): void
{
- $this->request = $this->getMockBuilder(RequestInterface::class)
- ->getMockForAbstractClass();
-
- $this->redirect = $this->getMockBuilder(RedirectInterface::class)
- ->getMockForAbstractClass();
-
- $this->messageManager = $this->getMockBuilder(ManagerInterface::class)
- ->getMockForAbstractClass();
-
- $this->resultRedirect = $this->getMockBuilder(Redirect::class)
- ->disableOriginalConstructor()
- ->getMock();
+ $this->request = $this->getMockForAbstractClass(RequestInterface::class);
+ $this->redirect = $this->getMockForAbstractClass(RedirectInterface::class);
+ $this->messageManager = $this->getMockForAbstractClass(ManagerInterface::class);
+ $this->resultRedirect = $this->createMock(Redirect::class);
- $this->resultFactory = $this->getMockBuilder(ResultFactory::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->resultFactory->expects($this->once())
+ $resultFactory = $this->createMock(ResultFactory::class);
+ $resultFactory->expects($this->once())
->method('create')
->with(ResultFactory::TYPE_REDIRECT)
->willReturn($this->resultRedirect);
- $this->context = $this->getMockBuilder(\Magento\Framework\App\Action\Context::class)
+ /** @var ActionContext|MockObject $context */
+ $context = $this->getMockBuilder(ActionContext::class)
->disableOriginalConstructor()
->getMock();
- $this->context->expects($this->any())
+ $context->expects($this->any())
->method('getRequest')
->willReturn($this->request);
- $this->context->expects($this->any())
+ $context->expects($this->any())
->method('getRedirect')
->willReturn($this->redirect);
- $this->context->expects($this->any())
+ $context->expects($this->any())
->method('getMessageManager')
->willReturn($this->messageManager);
- $this->context->expects($this->any())
+ $context->expects($this->any())
->method('getResultFactory')
- ->willReturn($this->resultFactory);
-
- $this->cart = $this->getMockBuilder(\Magento\Checkout\Model\Cart::class)
- ->disableOriginalConstructor()
- ->getMock();
+ ->willReturn($resultFactory);
- $this->cartHelper = $this->getMockBuilder(\Magento\Checkout\Helper\Cart::class)
- ->disableOriginalConstructor()
- ->getMock();
+ $this->cart = $this->createMock(Cart::class);
+ $this->cartHelper = $this->createMock(CartHelper::class);
$this->quote = $this->getMockBuilder(Quote::class)
->disableOriginalConstructor()
- ->setMethods(['getHasError'])
+ ->addMethods(['getHasError'])
->getMock();
- $this->optionCollection = $this->getMockBuilder(
- \Magento\Wishlist\Model\ResourceModel\Item\Option\Collection::class
- )->disableOriginalConstructor()
- ->getMock();
+ $this->optionCollection = $this->createMock(OptionCollection::class);
$this->option = $this->getMockBuilder(Option::class)
->disableOriginalConstructor()
->getMock();
- $this->optionFactory = $this->getMockBuilder(OptionFactory::class)
- ->disableOriginalConstructor()
- ->setMethods(['create'])
- ->getMock();
- $this->optionFactory->expects($this->once())
+ /** @var OptionFactory|MockObject $optionFactory */
+ $optionFactory = $this->createMock(OptionFactory::class);
+ $optionFactory->expects($this->once())
->method('create')
->willReturn($this->option);
- $this->item = $this->getMockBuilder(Item::class)
- ->disableOriginalConstructor()
- ->getMock();
+ $this->item = $this->createMock(Item::class);
- $this->itemFactory = $this->getMockBuilder(ItemFactory::class)
- ->disableOriginalConstructor()
- ->setMethods(['create'])
- ->getMock();
- $this->itemFactory->expects($this->once())
+ $itemFactory = $this->createMock(ItemFactory::class);
+ $itemFactory->expects($this->once())
->method('create')
->willReturn($this->item);
- $this->escaper = $this->getMockBuilder(Escaper::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->product = $this->getMockBuilder(Product::class)
- ->disableOriginalConstructor()
- ->getMock();
+ $this->escaper = $this->createMock(Escaper::class);
+ $this->product = $this->createMock(Product::class);
$this->model = new SharedCart(
- $this->context,
+ $context,
$this->cart,
- $this->optionFactory,
- $this->itemFactory,
+ $optionFactory,
+ $itemFactory,
$this->cartHelper,
$this->escaper
);
@@ -358,7 +349,7 @@ public function testExecuteProductException()
$this->option->expects($this->once())
->method('getCollection')
- ->willThrowException(new \Magento\Catalog\Model\Product\Exception(__('LocalizedException')));
+ ->willThrowException(new Exception(__('LocalizedException')));
$this->resultRedirect->expects($this->once())
->method('setUrl')
diff --git a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
index e09491813877b..369f77e527287 100644
--- a/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
+++ b/app/code/Magento/Wishlist/Test/Unit/Model/WishlistTest.php
@@ -13,6 +13,7 @@
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Type\AbstractType;
use Magento\Catalog\Model\ProductFactory;
+use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\CatalogInventory\Model\Stock\Item as StockItem;
use Magento\CatalogInventory\Model\Stock\StockItemRepository;
@@ -132,6 +133,10 @@ class WishlistTest extends TestCase
* @var StockRegistryInterface|MockObject
*/
private $stockRegistry;
+ /**
+ * @var StockConfigurationInterface|MockObject
+ */
+ private $stockConfiguration;
protected function setUp(): void
{
@@ -194,6 +199,8 @@ protected function setUp(): void
->method('getEventDispatcher')
->willReturn($this->eventDispatcher);
+ $this->stockConfiguration = $this->createMock(StockConfigurationInterface::class);
+
$this->wishlist = new Wishlist(
$context,
$this->registry,
@@ -213,7 +220,8 @@ protected function setUp(): void
[],
$this->serializer,
$this->stockRegistry,
- $this->scopeConfig
+ $this->scopeConfig,
+ $this->stockConfiguration
);
}
@@ -300,6 +308,7 @@ public function testUpdateItem($itemId, $buyRequest, $param): void
$newProduct->expects($this->once())
->method('getTypeInstance')
->willReturn($instanceType);
+ $newProduct->expects($this->any())->method('getIsSalable')->willReturn(true);
$item = $this->getMockBuilder(Item::class)
->disableOriginalConstructor()
@@ -388,8 +397,19 @@ public function updateItemDataProvider(): array
];
}
- public function testAddNewItem()
+ /**
+ * @param bool $getIsSalable
+ * @param bool $isShowOutOfStock
+ * @param string $throwException
+ *
+ * @dataProvider addNewItemDataProvider
+ */
+ public function testAddNewItem(bool $getIsSalable, bool $isShowOutOfStock, string $throwException): void
{
+ if ($throwException) {
+ $this->expectExceptionMessage($throwException);
+ }
+ $this->stockConfiguration->method('isShowOutOfStock')->willReturn($isShowOutOfStock);
$productId = 1;
$storeId = 1;
$buyRequest = json_encode(
@@ -407,34 +427,31 @@ public function testAddNewItem()
$instanceType = $this->getMockBuilder(AbstractType::class)
->disableOriginalConstructor()
->getMock();
- $instanceType->expects($this->once())
- ->method('processConfiguration')
+ $instanceType->method('processConfiguration')
->willReturn('product');
$productMock = $this->getMockBuilder(Product::class)
->disableOriginalConstructor()
- ->setMethods(['getId', 'hasWishlistStoreId', 'getStoreId', 'getTypeInstance'])
+ ->setMethods(['getId', 'hasWishlistStoreId', 'getStoreId', 'getTypeInstance', 'getIsSalable'])
->getMock();
- $productMock->expects($this->once())
- ->method('getId')
+ $productMock->method('getId')
->willReturn($productId);
- $productMock->expects($this->once())
- ->method('hasWishlistStoreId')
+ $productMock->method('hasWishlistStoreId')
->willReturn(false);
- $productMock->expects($this->once())
- ->method('getStoreId')
+ $productMock->method('getStoreId')
->willReturn($storeId);
- $productMock->expects($this->once())
- ->method('getTypeInstance')
+ $productMock->method('getTypeInstance')
->willReturn($instanceType);
+ $productMock->expects($this->any())
+ ->method('getIsSalable')
+ ->willReturn($getIsSalable);
$this->productRepository->expects($this->once())
->method('getById')
->with($productId, false, $storeId)
->willReturn($productMock);
- $this->serializer->expects($this->once())
- ->method('unserialize')
+ $this->serializer->method('unserialize')
->willReturnCallback(
function ($value) {
return json_decode($value, true);
@@ -453,4 +470,17 @@ function ($value) {
$this->assertEquals($result, $this->wishlist->addNewItem($productMock, $buyRequest));
}
+
+ /**
+ * @return array[]
+ */
+ public function addNewItemDataProvider(): array
+ {
+ return [
+ [false, false, 'Cannot add product without stock to wishlist'],
+ [false, true, ''],
+ [true, false, ''],
+ [true, true, ''],
+ ];
+ }
}
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php
index 3489585cd17d7..840c4638614c4 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/AddProductsToWishlist.php
@@ -83,7 +83,7 @@ public function resolve(
array $args = null
) {
if (!$this->wishlistConfig->isEnabled()) {
- throw new GraphQlInputException(__('The wishlist is not currently available.'));
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
}
$customerId = $context->getUserId();
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php
index cad574ef56ed2..b73afe27883dd 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlistResolver.php
@@ -54,7 +54,7 @@ public function resolve(
array $args = null
) {
if (!$this->wishlistConfig->isEnabled()) {
- throw new GraphQlInputException(__('The wishlist is not currently available.'));
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
}
if (false === $context->getExtensionAttributes()->getIsCustomer()) {
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlists.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlists.php
new file mode 100644
index 0000000000000..ad0c73691720a
--- /dev/null
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/CustomerWishlists.php
@@ -0,0 +1,102 @@
+wishlistDataMapper = $wishlistDataMapper;
+ $this->wishlistConfig = $wishlistConfig;
+ $this->wishlistCollectionFactory = $wishlistCollectionFactory;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!$this->wishlistConfig->isEnabled()) {
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
+ }
+
+ $customerId = $context->getUserId();
+
+ if (null === $customerId || 0 === $customerId) {
+ throw new GraphQlAuthorizationException(
+ __('The current user cannot perform operations on wishlist')
+ );
+ }
+
+ $currentPage = $args['currentPage'] ?? 1;
+ $pageSize = $args['pageSize'] ?? 20;
+
+ /** @var WishlistCollection $collection */
+ $collection = $this->wishlistCollectionFactory->create();
+ $collection->filterByCustomerId($customerId);
+
+ if ($currentPage > 0) {
+ $collection->setCurPage($currentPage);
+ }
+
+ if ($pageSize > 0) {
+ $collection->setPageSize($pageSize);
+ }
+
+ $wishlists = [];
+
+ /** @var Wishlist $wishList */
+ foreach ($collection->getItems() as $wishList) {
+ array_push($wishlists, $this->wishlistDataMapper->map($wishList));
+ }
+
+ return $wishlists;
+ }
+}
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php
index 65c8498fc89ad..31dd33ff2cd79 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/ProductResolver.php
@@ -7,12 +7,12 @@
namespace Magento\WishlistGraphQl\Model\Resolver;
+use Magento\Catalog\Model\Product;
use Magento\CatalogGraphQl\Model\ProductDataProvider;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
-use Magento\Wishlist\Model\Item;
/**
* Fetches the Product data according to the GraphQL schema
@@ -45,9 +45,9 @@ public function resolve(
if (!isset($value['model'])) {
throw new LocalizedException(__('Missing key "model" in Wishlist Item value data'));
}
- /** @var Item $wishlistItem */
- $wishlistItem = $value['model'];
+ /** @var Product $product */
+ $product = $value['model'];
- return $this->productDataProvider->getProductDataById((int)$wishlistItem->getProductId());
+ return $this->productDataProvider->getProductDataById((int) $product->getId());
}
}
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php
index a59c5ccdb0f70..66a6c7b86ea37 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/RemoveProductsFromWishlist.php
@@ -83,7 +83,7 @@ public function resolve(
array $args = null
) {
if (!$this->wishlistConfig->isEnabled()) {
- throw new GraphQlInputException(__('The wishlist is not currently available.'));
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
}
$customerId = $context->getUserId();
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/Type/WishlistItemType.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/Type/WishlistItemType.php
new file mode 100644
index 0000000000000..ae4a6ed2b6a64
--- /dev/null
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/Type/WishlistItemType.php
@@ -0,0 +1,59 @@
+supportedTypes = $supportedTypes;
+ }
+
+ /**
+ * Resolving wishlist item type
+ *
+ * @param array $data
+ *
+ * @return string
+ *
+ * @throws LocalizedException
+ */
+ public function resolveType(array $data): string
+ {
+ if (!$data['model'] instanceof ProductInterface) {
+ throw new LocalizedException(__('"model" should be a "%instance" instance', [
+ 'instance' => ProductInterface::class
+ ]));
+ }
+
+ $productTypeId = $data['model']->getTypeId();
+
+ if (!isset($this->supportedTypes[$productTypeId])) {
+ throw new LocalizedException(
+ __('Product "%product_type" type is not supported', ['product_type' => $productTypeId])
+ );
+ }
+
+ return $this->supportedTypes[$productTypeId];
+ }
+}
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php
index c6ede66fc2b1b..47a408d55555b 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/UpdateProductsInWishlist.php
@@ -83,7 +83,7 @@ public function resolve(
array $args = null
) {
if (!$this->wishlistConfig->isEnabled()) {
- throw new GraphQlInputException(__('The wishlist is not currently available.'));
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
}
$customerId = $context->getUserId();
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistById.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistById.php
new file mode 100644
index 0000000000000..1ddf91637fe90
--- /dev/null
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistById.php
@@ -0,0 +1,115 @@
+wishlistResource = $wishlistResource;
+ $this->wishlistFactory = $wishlistFactory;
+ $this->wishlistDataMapper = $wishlistDataMapper;
+ $this->wishlistConfig = $wishlistConfig;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!$this->wishlistConfig->isEnabled()) {
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
+ }
+
+ $customerId = $context->getUserId();
+
+ if (null === $customerId || 0 === $customerId) {
+ throw new GraphQlAuthorizationException(
+ __('The current user cannot perform operations on wishlist')
+ );
+ }
+
+ $wishlist = $this->getWishlist((int) $args['id'], $customerId);
+
+ if (null === $wishlist->getId() || (int) $wishlist->getCustomerId() !== $customerId) {
+ return [];
+ }
+
+ return $this->wishlistDataMapper->map($wishlist);
+ }
+
+ /**
+ * Get wishlist
+ *
+ * @param int $wishlistId
+ * @param int $customerId
+ *
+ * @return Wishlist
+ */
+ private function getWishlist(int $wishlistId, int $customerId): Wishlist
+ {
+ $wishlist = $this->wishlistFactory->create();
+
+ if ($wishlistId > 0) {
+ $this->wishlistResource->load($wishlist, $wishlistId);
+ } else {
+ $this->wishlistResource->load($wishlist, $customerId, 'customer_id');
+ }
+
+ return $wishlist;
+ }
+}
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItems.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItems.php
new file mode 100644
index 0000000000000..77ff483a60bd2
--- /dev/null
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItems.php
@@ -0,0 +1,98 @@
+wishlistItemCollectionFactory = $wishlistItemCollectionFactory;
+ $this->storeManager = $storeManager;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!isset($value['model'])) {
+ throw new LocalizedException(__('Missing key "model" in Wishlist value data'));
+ }
+ /** @var Wishlist $wishlist */
+ $wishlist = $value['model'];
+
+ $wishlistItems = $this->getWishListItems($wishlist);
+
+ $data = [];
+ foreach ($wishlistItems as $wishlistItem) {
+ $data[] = [
+ 'id' => $wishlistItem->getId(),
+ 'quantity' => $wishlistItem->getData('qty'),
+ 'description' => $wishlistItem->getDescription(),
+ 'added_at' => $wishlistItem->getAddedAt(),
+ 'model' => $wishlistItem->getProduct(),
+ 'itemModel' => $wishlistItem,
+ ];
+ }
+ return $data;
+ }
+
+ /**
+ * Get wishlist items
+ *
+ * @param Wishlist $wishlist
+ * @return Item[]
+ */
+ private function getWishListItems(Wishlist $wishlist): array
+ {
+ /** @var WishlistItemCollection $wishlistItemCollection */
+ $wishlistItemCollection = $this->wishlistItemCollectionFactory->create();
+ $wishlistItemCollection
+ ->addWishlistFilter($wishlist)
+ ->addStoreFilter(array_map(function (StoreInterface $store) {
+ return $store->getId();
+ }, $this->storeManager->getStores()))
+ ->setVisibilityFilter();
+ return $wishlistItemCollection->getItems();
+ }
+}
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php
index dfbbf6543f66f..36a03da2b79a9 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistItemsResolver.php
@@ -70,7 +70,7 @@ public function resolve(
'qty' => $wishlistItem->getData('qty'),
'description' => $wishlistItem->getDescription(),
'added_at' => $wishlistItem->getAddedAt(),
- 'model' => $wishlistItem,
+ 'model' => $wishlistItem->getProduct(),
];
}
return $data;
diff --git a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php
index 09c0a8a935a6c..f31b403a514fb 100644
--- a/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php
+++ b/app/code/Magento/WishlistGraphQl/Model/Resolver/WishlistResolver.php
@@ -63,7 +63,7 @@ public function resolve(
array $args = null
) {
if (!$this->wishlistConfig->isEnabled()) {
- throw new GraphQlInputException(__('The wishlist is not currently available.'));
+ throw new GraphQlInputException(__('The wishlist configuration is currently disabled.'));
}
$customerId = $context->getUserId();
diff --git a/app/code/Magento/WishlistGraphQl/composer.json b/app/code/Magento/WishlistGraphQl/composer.json
index 7a3fca599a4b3..58bc738bd24d6 100644
--- a/app/code/Magento/WishlistGraphQl/composer.json
+++ b/app/code/Magento/WishlistGraphQl/composer.json
@@ -5,6 +5,7 @@
"require": {
"php": "~7.3.0||~7.4.0",
"magento/framework": "*",
+ "magento/module-catalog": "*",
"magento/module-catalog-graph-ql": "*",
"magento/module-wishlist": "*",
"magento/module-store": "*"
diff --git a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls
index 430e77cc45e96..69bc45462d4c8 100644
--- a/app/code/Magento/WishlistGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/WishlistGraphQl/etc/schema.graphqls
@@ -6,7 +6,12 @@ type Query {
}
type Customer {
- wishlist: Wishlist! @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\CustomerWishlistResolver") @doc(description: "Contains the contents of a customer's wish lists") @cache(cacheable: false)
+ wishlists(
+ pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."),
+ currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1.")
+ ): [Wishlist!]! @doc(description: "An array of wishlists. In Magento Open Source, customers are limited to one wish list. The number of wish lists is configurable for Magento Commerce") @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\CustomerWishlists")
+ wishlist: Wishlist! @deprecated(reason: "Use `Customer.wishlists` or `Customer.wishlist_v2`") @resolver(class:"\\Magento\\WishlistGraphQl\\Model\\Resolver\\CustomerWishlistResolver") @doc(description: "Contains a customer's wish lists") @cache(cacheable: false)
+ wishlist_v2(id: ID!): Wishlist @doc(description: "Retrieve the specified wish list") @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistById")
}
type WishlistOutput @doc(description: "Deprecated: `Wishlist` type should be used instead") {
@@ -19,12 +24,22 @@ type WishlistOutput @doc(description: "Deprecated: `Wishlist` type should be use
type Wishlist {
id: ID @doc(description: "Wishlist unique identifier")
- items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @doc(description: "An array of items in the customer's wish list"),
- items_count: Int @doc(description: "The number of items in the wish list"),
- sharing_code: String @doc(description: "An encrypted code that Magento uses to link to the wish list"),
+ items: [WishlistItem] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItemsResolver") @deprecated(reason: "Use field `items_v2` from type `Wishlist` instead")
+ items_v2: [WishlistItemInterface] @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\WishlistItems") @doc(description: "An array of items in the customer's wish list")
+ items_count: Int @doc(description: "The number of items in the wish list")
+ sharing_code: String @doc(description: "An encrypted code that Magento uses to link to the wish list")
updated_at: String @doc(description: "The time of the last modification to the wish list")
}
+interface WishlistItemInterface @typeResolver(class: "Magento\\WishlistGraphQl\\Model\\Resolver\\Type\\WishlistItemType") {
+ id: ID! @doc(description: "The ID of the wish list item")
+ quantity: Float! @doc(description: "The quantity of this wish list item")
+ description: String @doc(description: "The description of the item")
+ added_at: String! @doc(description: "The date and time the item was added to the wish list")
+ product: ProductInterface @doc(description: "Product details of the wish list item") @resolver(class: "\\Magento\\WishlistGraphQl\\Model\\Resolver\\ProductResolver")
+ customizable_options: [SelectedCustomizableOption] @doc(description: "Custom options selected for the wish list item")
+}
+
type WishlistItem {
id: Int @doc(description: "The wish list item ID")
qty: Float @doc(description: "The quantity of this wish list item"),
diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less
index fa158589feb96..654236e143a29 100644
--- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less
+++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less
@@ -18,15 +18,10 @@
// _____________________________________________
.currency-addon {
+ .lib-vendor-prefix-display(inline-flex);
border: 1px solid rgb(173,173,173);
- position: relative;
- display: -webkit-inline-flex;
- display: -ms-inline-flexbox;
- display: inline-flex;
- -webkit-flex-direction: row;
- -ms-flex-direction: row;
- flex-direction: row;
flex-flow: row nowrap;
+ position: relative;
width: 100%;
.admin__control-text {
diff --git a/app/design/adminhtml/Magento/backend/web/css/source/components/_image-uploader.less b/app/design/adminhtml/Magento/backend/web/css/source/components/_image-uploader.less
index f187697281252..a9172d5164c38 100644
--- a/app/design/adminhtml/Magento/backend/web/css/source/components/_image-uploader.less
+++ b/app/design/adminhtml/Magento/backend/web/css/source/components/_image-uploader.less
@@ -9,8 +9,8 @@
.image-uploader {
.image-upload-requirements {
- margin-top: 8px;
font-size: .9em;
+ margin-top: 8px;
}
.image-placeholder {
@@ -19,12 +19,12 @@
}
.image-uploader-spinner {
- width: 50%;
- height: 50%;
background-size: auto;
+ height: 50%;
margin: 0;
- transform: translate(50%, 50%);
position: absolute;
+ transform: translate(50%, 50%);
+ width: 50%;
}
.image-uploader-preview {
@@ -33,7 +33,10 @@
.image-uploader-preview-link,
.image-uploader-preview-link .preview-image {
+ display: block;
height: inherit;
+ margin-left: auto;
+ margin-right: auto;
}
}
diff --git a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_checkout-agreements.less b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_checkout-agreements.less
index ff4f07a983940..b8be2b33a4475 100644
--- a/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_checkout-agreements.less
+++ b/app/design/frontend/Magento/blank/Magento_Checkout/web/css/source/module/checkout/_checkout-agreements.less
@@ -13,6 +13,30 @@
margin-bottom: @indent__base;
}
+ .checkout-agreement.field {
+ .lib-vendor-prefix-display();
+
+ &.required {
+ label:after {
+ content: none;
+ }
+
+ .action-show {
+ &:after {
+ content: '*';
+ .lib-typography(
+ @_font-size: @form-field-label-asterisk__font-size,
+ @_color: @form-field-label-asterisk__color,
+ @_font-family: @form-field-label-asterisk__font-family,
+ @_font-weight: @form-field-label-asterisk__font-weight,
+ @_line-height: @form-field-label-asterisk__line-height,
+ @_font-style: @form-field-label-asterisk__font-style
+ );
+ }
+ }
+ }
+ }
+
.action-show {
&:extend(.abs-action-button-as-link all);
vertical-align: baseline;
diff --git a/app/design/frontend/Magento/blank/web/css/source/_extends.less b/app/design/frontend/Magento/blank/web/css/source/_extends.less
index 5bdaa4c3c35a3..690b89f42b419 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_extends.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_extends.less
@@ -1110,7 +1110,7 @@
.abs-shopping-cart-items {
.action {
&.continue {
- border-radius: 3px;
+ border-radius: @button__border-radius;
font-weight: @font-weight__bold;
.lib-link-as-button();
.lib-button(
diff --git a/app/design/frontend/Magento/blank/web/css/source/_icons.less b/app/design/frontend/Magento/blank/web/css/source/_icons.less
index 7d1ceaca73c72..5cd7795aa506c 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_icons.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_icons.less
@@ -8,6 +8,7 @@
@family-name: @icons__font-name,
@font-path: @icons__font-path,
@font-weight: normal,
- @font-style: normal
+ @font-style: normal,
+ @font-display: block
);
}
diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less
index fad906a089400..f9cca1ca16a18 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less
@@ -28,10 +28,10 @@
.nav-toggle {
.lib-icon-font(
- @icon-menu,
- @_icon-font-size: 28px,
- @_icon-font-color: @header-icons-color,
- @_icon-font-color-hover: @header-icons-color-hover
+ @icon-menu,
+ @_icon-font-size: 28px,
+ @_icon-font-color: @header-icons-color,
+ @_icon-font-color-hover: @header-icons-color-hover
);
.lib-icon-text-hide();
cursor: pointer;
@@ -54,13 +54,13 @@
.parent {
.level-top {
- position: relative;
.lib-icon-font(
- @_icon-font-content: @icon-down,
- @_icon-font-size: 42px,
- @_icon-font-position: after,
- @_icon-font-display: block
+ @_icon-font-content: @icon-down,
+ @_icon-font-size: 42px,
+ @_icon-font-position: after,
+ @_icon-font-display: block
);
+ position: relative;
&:after {
position: absolute;
@@ -70,8 +70,8 @@
&.ui-state-active {
.lib-icon-font-symbol(
- @_icon-font-content: @icon-up,
- @_icon-font-position: after
+ @_icon-font-content: @icon-up,
+ @_icon-font-position: after
);
}
}
@@ -82,12 +82,10 @@
-webkit-overflow-scrolling: touch;
.lib-css(transition, left .3s, 1);
height: 100%;
- left: -80%;
left: calc(~'-1 * (100% - @{active-nav-indent})');
overflow: auto;
position: fixed;
top: 0;
- width: 80%;
width: calc(~'100% - @{active-nav-indent}');
.switcher {
@@ -109,13 +107,13 @@
.switcher-trigger {
strong {
- position: relative;
.lib-icon-font(
- @_icon-font-content: @icon-down,
- @_icon-font-size: 42px,
- @_icon-font-position: after,
- @_icon-font-display: block
+ @_icon-font-content: @icon-down,
+ @_icon-font-size: 42px,
+ @_icon-font-position: after,
+ @_icon-font-display: block
);
+ position: relative;
&:after {
position: absolute;
@@ -126,16 +124,18 @@
&.active strong {
.lib-icon-font-symbol(
- @_icon-font-content: @icon-up,
- @_icon-font-position: after
+ @_icon-font-content: @icon-up,
+ @_icon-font-position: after
);
}
}
+
.switcher-dropdown {
.lib-list-reset-styles();
display: none;
padding: @indent__s 0;
}
+
.switcher-options {
&.active {
.switcher-dropdown {
@@ -143,6 +143,7 @@
}
}
}
+
.header.links {
.lib-list-reset-styles();
border-bottom: 1px solid @color-gray82;
@@ -200,13 +201,11 @@
.nav-open {
.page-wrapper {
- left: 80%;
left: calc(~'100% - @{active-nav-indent}');
}
.nav-sections {
@_shadow: 0 0 5px 0 rgba(50, 50, 50, .75);
-
.lib-css(box-shadow, @_shadow, 1);
left: 0;
z-index: 99;
@@ -293,10 +292,6 @@
display: none;
}
- .nav-sections-item-content {
- display: block !important;
- }
-
.nav-sections-item-content > * {
display: none;
}
diff --git a/app/design/frontend/Magento/blank/web/css/source/_typography.less b/app/design/frontend/Magento/blank/web/css/source/_typography.less
index 6807c0f692af8..02ccd90d4655d 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_typography.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_typography.less
@@ -9,7 +9,7 @@
& when (@media-common = true) {
.lib-font-face(
- @family-name: @font-family-name__base,
+ @family-name: 'Open Sans',
@font-path: '@{baseDir}fonts/opensans/light/opensans-300',
@font-weight: 300,
@font-style: normal,
@@ -17,7 +17,7 @@
);
.lib-font-face(
- @family-name: @font-family-name__base,
+ @family-name: 'Open Sans',
@font-path: '@{baseDir}fonts/opensans/regular/opensans-400',
@font-weight: 400,
@font-style: normal,
@@ -25,7 +25,7 @@
);
.lib-font-face(
- @family-name: @font-family-name__base,
+ @family-name: 'Open Sans',
@font-path: '@{baseDir}fonts/opensans/semibold/opensans-600',
@font-weight: 600,
@font-style: normal,
@@ -33,7 +33,7 @@
);
.lib-font-face(
- @family-name: @font-family-name__base,
+ @family-name: 'Open Sans',
@font-path: '@{baseDir}fonts/opensans/bold/opensans-700',
@font-weight: 700,
@font-style: normal,
diff --git a/app/etc/di.xml b/app/etc/di.xml
index fed2e336046f9..585c88f68ff6f 100644
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -185,6 +185,7 @@
+
diff --git a/composer.json b/composer.json
index 25be12b5bb72f..57fbfaaa35c2b 100644
--- a/composer.json
+++ b/composer.json
@@ -215,12 +215,15 @@
"magento/module-media-content-synchronization-api": "*",
"magento/module-media-content-synchronization-catalog": "*",
"magento/module-media-content-synchronization-cms": "*",
+ "magento/module-media-gallery-synchronization-metadata": "*",
"magento/module-media-gallery-metadata": "*",
"magento/module-media-gallery-metadata-api": "*",
"magento/module-media-gallery-catalog-ui": "*",
"magento/module-media-gallery-cms-ui": "*",
"magento/module-media-gallery-catalog-integration": "*",
"magento/module-media-gallery-catalog": "*",
+ "magento/module-media-gallery-renditions": "*",
+ "magento/module-media-gallery-renditions-api": "*",
"magento/module-media-storage": "*",
"magento/module-message-queue": "*",
"magento/module-msrp": "*",
diff --git a/composer.lock b/composer.lock
index c2eed9d87cc00..8a5d82536cee4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "0b51badfd1978bb34febd90226af9e27",
+ "content-hash": "a03edc1c8ee05f82886eebd6ed288df8",
"packages": [
{
"name": "colinmollenhour/cache-backend-file",
@@ -206,6 +206,16 @@
"ssl",
"tls"
],
+ "funding": [
+ {
+ "url": "https://packagist.com",
+ "type": "custom"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+ "type": "tidelift"
+ }
+ ],
"time": "2020-04-08T08:27:21+00:00"
},
{
@@ -1346,12 +1356,6 @@
"BSD-3-Clause"
],
"description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.",
- "funding": [
- {
- "url": "https://funding.communitybridge.org/projects/laminas-project",
- "type": "community_bridge"
- }
- ],
"time": "2020-05-20T13:45:39+00:00"
},
{
@@ -3319,12 +3323,6 @@
"laminas",
"zf"
],
- "funding": [
- {
- "url": "https://funding.communitybridge.org/projects/laminas-project",
- "type": "community_bridge"
- }
- ],
"time": "2020-05-20T16:45:56+00:00"
},
{
@@ -3564,16 +3562,6 @@
"logging",
"psr-3"
],
- "funding": [
- {
- "url": "https://github.com/Seldaek",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
- "type": "tidelift"
- }
- ],
"time": "2020-05-22T07:31:27+00:00"
},
{
@@ -4366,16 +4354,6 @@
"parser",
"validator"
],
- "funding": [
- {
- "url": "https://github.com/Seldaek",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
- "type": "tidelift"
- }
- ],
"time": "2020-04-30T19:05:18+00:00"
},
{
@@ -7337,6 +7315,555 @@
],
"time": "2020-06-27T23:57:46+00:00"
},
+ {
+ "name": "hoa/consistency",
+ "version": "1.17.05.02",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Consistency.git",
+ "reference": "fd7d0adc82410507f332516faf655b6ed22e4c2f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Consistency/zipball/fd7d0adc82410507f332516faf655b6ed22e4c2f",
+ "reference": "fd7d0adc82410507f332516faf655b6ed22e4c2f",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/exception": "~1.0",
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "hoa/stream": "~1.0",
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Consistency\\": "."
+ },
+ "files": [
+ "Prelude.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Consistency library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "autoloader",
+ "callable",
+ "consistency",
+ "entity",
+ "flex",
+ "keyword",
+ "library"
+ ],
+ "time": "2017-05-02T12:18:12+00:00"
+ },
+ {
+ "name": "hoa/console",
+ "version": "3.17.05.02",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Console.git",
+ "reference": "e231fd3ea70e6d773576ae78de0bdc1daf331a66"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Console/zipball/e231fd3ea70e6d773576ae78de0bdc1daf331a66",
+ "reference": "e231fd3ea70e6d773576ae78de0bdc1daf331a66",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0",
+ "hoa/exception": "~1.0",
+ "hoa/file": "~1.0",
+ "hoa/protocol": "~1.0",
+ "hoa/stream": "~1.0",
+ "hoa/ustring": "~4.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "suggest": {
+ "ext-pcntl": "To enable hoa://Event/Console/Window:resize.",
+ "hoa/dispatcher": "To use the console kit.",
+ "hoa/router": "To use the console kit."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Console\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Console library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "autocompletion",
+ "chrome",
+ "cli",
+ "console",
+ "cursor",
+ "getoption",
+ "library",
+ "option",
+ "parser",
+ "processus",
+ "readline",
+ "terminfo",
+ "tput",
+ "window"
+ ],
+ "time": "2017-05-02T12:26:19+00:00"
+ },
+ {
+ "name": "hoa/event",
+ "version": "1.17.01.13",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Event.git",
+ "reference": "6c0060dced212ffa3af0e34bb46624f990b29c54"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Event/zipball/6c0060dced212ffa3af0e34bb46624f990b29c54",
+ "reference": "6c0060dced212ffa3af0e34bb46624f990b29c54",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Event\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Event library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "event",
+ "library",
+ "listener",
+ "observer"
+ ],
+ "time": "2017-01-13T15:30:50+00:00"
+ },
+ {
+ "name": "hoa/exception",
+ "version": "1.17.01.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Exception.git",
+ "reference": "091727d46420a3d7468ef0595651488bfc3a458f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Exception/zipball/091727d46420a3d7468ef0595651488bfc3a458f",
+ "reference": "091727d46420a3d7468ef0595651488bfc3a458f",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Exception\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Exception library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "exception",
+ "library"
+ ],
+ "time": "2017-01-16T07:53:27+00:00"
+ },
+ {
+ "name": "hoa/file",
+ "version": "1.17.07.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/File.git",
+ "reference": "35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/File/zipball/35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca",
+ "reference": "35cb979b779bc54918d2f9a4e02ed6c7a1fa67ca",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0",
+ "hoa/exception": "~1.0",
+ "hoa/iterator": "~2.0",
+ "hoa/stream": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\File\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\File library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "Socket",
+ "directory",
+ "file",
+ "finder",
+ "library",
+ "link",
+ "temporary"
+ ],
+ "time": "2017-07-11T07:42:15+00:00"
+ },
+ {
+ "name": "hoa/iterator",
+ "version": "2.17.01.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Iterator.git",
+ "reference": "d1120ba09cb4ccd049c86d10058ab94af245f0cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Iterator/zipball/d1120ba09cb4ccd049c86d10058ab94af245f0cc",
+ "reference": "d1120ba09cb4ccd049c86d10058ab94af245f0cc",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Iterator\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Iterator library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "iterator",
+ "library"
+ ],
+ "time": "2017-01-10T10:34:47+00:00"
+ },
+ {
+ "name": "hoa/protocol",
+ "version": "1.17.01.14",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Protocol.git",
+ "reference": "5c2cf972151c45f373230da170ea015deecf19e2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Protocol/zipball/5c2cf972151c45f373230da170ea015deecf19e2",
+ "reference": "5c2cf972151c45f373230da170ea015deecf19e2",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Protocol\\": "."
+ },
+ "files": [
+ "Wrapper.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Protocol library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "library",
+ "protocol",
+ "resource",
+ "stream",
+ "wrapper"
+ ],
+ "time": "2017-01-14T12:26:10+00:00"
+ },
+ {
+ "name": "hoa/stream",
+ "version": "1.17.02.21",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Stream.git",
+ "reference": "3293cfffca2de10525df51436adf88a559151d82"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Stream/zipball/3293cfffca2de10525df51436adf88a559151d82",
+ "reference": "3293cfffca2de10525df51436adf88a559151d82",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/event": "~1.0",
+ "hoa/exception": "~1.0",
+ "hoa/protocol": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Stream\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Stream library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "Context",
+ "bucket",
+ "composite",
+ "filter",
+ "in",
+ "library",
+ "out",
+ "protocol",
+ "stream",
+ "wrapper"
+ ],
+ "time": "2017-02-21T16:01:06+00:00"
+ },
+ {
+ "name": "hoa/ustring",
+ "version": "4.17.01.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hoaproject/Ustring.git",
+ "reference": "e6326e2739178799b1fe3fdd92029f9517fa17a0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hoaproject/Ustring/zipball/e6326e2739178799b1fe3fdd92029f9517fa17a0",
+ "reference": "e6326e2739178799b1fe3fdd92029f9517fa17a0",
+ "shasum": ""
+ },
+ "require": {
+ "hoa/consistency": "~1.0",
+ "hoa/exception": "~1.0"
+ },
+ "require-dev": {
+ "hoa/test": "~2.0"
+ },
+ "suggest": {
+ "ext-iconv": "ext/iconv must be present (or a third implementation) to use Hoa\\Ustring::transcode().",
+ "ext-intl": "To get a better Hoa\\Ustring::toAscii() and Hoa\\Ustring::compareTo()."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Hoa\\Ustring\\": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ivan Enderlin",
+ "email": "ivan.enderlin@hoa-project.net"
+ },
+ {
+ "name": "Hoa community",
+ "homepage": "https://hoa-project.net/"
+ }
+ ],
+ "description": "The Hoa\\Ustring library.",
+ "homepage": "https://hoa-project.net/",
+ "keywords": [
+ "library",
+ "search",
+ "string",
+ "unicode"
+ ],
+ "time": "2017-01-16T07:08:25+00:00"
+ },
{
"name": "jms/metadata",
"version": "1.7.0",
@@ -7593,12 +8120,6 @@
"sftp",
"storage"
],
- "funding": [
- {
- "url": "https://offset.earth/frankdejonge",
- "type": "other"
- }
- ],
"time": "2020-05-18T15:13:39+00:00"
},
{
@@ -7709,16 +8230,16 @@
},
{
"name": "magento/magento2-functional-testing-framework",
- "version": "3.0.0",
+ "version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/magento/magento2-functional-testing-framework.git",
- "reference": "8d98efa7434a30ab9e82ef128c430ef8e3a50699"
+ "reference": "8a106ea029f222f4354854636861273c7577bee9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/8d98efa7434a30ab9e82ef128c430ef8e3a50699",
- "reference": "8d98efa7434a30ab9e82ef128c430ef8e3a50699",
+ "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/8a106ea029f222f4354854636861273c7577bee9",
+ "reference": "8a106ea029f222f4354854636861273c7577bee9",
"shasum": ""
},
"require": {
@@ -7736,6 +8257,7 @@
"ext-intl": "*",
"ext-json": "*",
"ext-openssl": "*",
+ "hoa/console": "~3.0",
"monolog/monolog": "^1.17",
"mustache/mustache": "~2.5",
"php": "^7.3",
@@ -7795,7 +8317,7 @@
"magento",
"testing"
],
- "time": "2020-07-09T21:26:19+00:00"
+ "time": "2020-08-19T19:57:27+00:00"
},
{
"name": "mikey179/vfsstream",
@@ -8817,20 +9339,6 @@
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
- "funding": [
- {
- "url": "https://github.com/ondrejmirtes",
- "type": "github"
- },
- {
- "url": "https://www.patreon.com/phpstan",
- "type": "patreon"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
- "type": "tidelift"
- }
- ],
"time": "2020-05-05T12:55:44+00:00"
},
{
@@ -9120,12 +9628,6 @@
"keywords": [
"timer"
],
- "funding": [
- {
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- }
- ],
"time": "2020-04-20T06:00:37+00:00"
},
{
@@ -9181,6 +9683,7 @@
"type": "github"
}
],
+ "abandoned": true,
"time": "2020-06-27T06:36:25+00:00"
},
{
@@ -9269,16 +9772,6 @@
"testing",
"xunit"
],
- "funding": [
- {
- "url": "https://phpunit.de/donate.html",
- "type": "custom"
- },
- {
- "url": "https://github.com/sebastianbergmann",
- "type": "github"
- }
- ],
"time": "2020-05-22T13:54:05+00:00"
},
{
@@ -9786,6 +10279,7 @@
],
"description": "FinderFacade is a convenience wrapper for Symfony's Finder component.",
"homepage": "https://github.com/sebastianbergmann/finder-facade",
+ "abandoned": true,
"time": "2020-02-08T06:07:58+00:00"
},
{
diff --git a/dev/tests/acceptance/staticRuleset.json b/dev/tests/acceptance/staticRuleset.json
index 74fe3469e353b..82cc9dfe74152 100644
--- a/dev/tests/acceptance/staticRuleset.json
+++ b/dev/tests/acceptance/staticRuleset.json
@@ -2,6 +2,7 @@
"tests": [
"actionGroupArguments",
"deprecatedEntityUsage",
- "annotations"
+ "annotations",
+ "pauseActionUsage"
]
}
diff --git a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/Item.php b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/Item.php
index 1731a974aaed3..71ff93875f2c1 100644
--- a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/Item.php
+++ b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/Item.php
@@ -11,6 +11,9 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
+/**
+ * Resolver for Item
+ */
class Item implements ResolverInterface
{
/**
diff --git a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/TestUnion.php b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/TestUnion.php
new file mode 100644
index 0000000000000..592b0caaa88a3
--- /dev/null
+++ b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/TestUnion.php
@@ -0,0 +1,34 @@
+ 'custom_name1_value',
+ 'custom_name2' => 'custom_name2_value',
+ ];
+ }
+}
diff --git a/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/UnionTypeResolver.php b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/UnionTypeResolver.php
new file mode 100644
index 0000000000000..40cbdadb8a948
--- /dev/null
+++ b/dev/tests/api-functional/_files/Magento/TestModuleGraphQlQuery/Model/Resolver/UnionTypeResolver.php
@@ -0,0 +1,27 @@
+reinitialize();
/** Apply method level fixtures if thy are available, apply class level fixtures otherwise */
- $this->_applyFixtures($this->_getFixtures($test, 'method') ?: $this->_getFixtures($test, 'class'));
+ $this->_applyFixtures(
+ $this->_getFixtures($test, 'method') ?: $this->_getFixtures($test, 'class'),
+ $test
+ );
}
/**
* Handler for 'endTest' event
+ *
+ * @param TestCase $test
*/
- public function endTest()
+ public function endTest(TestCase $test)
{
- $this->_revertFixtures();
+ $this->_revertFixtures($test);
$objectManager = Bootstrap::getObjectManager();
$objectManager->get(AttributeMetadataCache::class)->clean();
}
diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Helper/CompareArraysRecursively.php b/dev/tests/api-functional/framework/Magento/TestFramework/Helper/CompareArraysRecursively.php
new file mode 100644
index 0000000000000..d8a88c721c9fe
--- /dev/null
+++ b/dev/tests/api-functional/framework/Magento/TestFramework/Helper/CompareArraysRecursively.php
@@ -0,0 +1,70 @@
+ [
+ * 'items' => [
+ * [
+ * 'sku' => 'bundle-product',
+ * 'type_id' => 'bundle',
+ * 'items' => [
+ * [
+ * 'title' => 'Bundle Product Items',
+ * 'sku' => 'bundle-product',
+ * 'options' => [
+ * [
+ * 'price' => 2.75,
+ * 'label' => 'Simple Product',
+ * 'product' => [
+ * 'name' => 'Simple Product',
+ * 'sku' => 'simple',
+ * ]
+ * ]
+ * ]
+ * ]
+ * ];
+ * ```
+ *
+ * @param array $expected
+ * @param array $actual
+ * @return array
+ */
+ public function execute(array $expected, array $actual): array
+ {
+ $diffResult = [];
+
+ foreach ($expected as $key => $value) {
+ if (array_key_exists($key, $actual)) {
+ if (is_array($value)) {
+ $recursiveDiff = $this->execute($value, $actual[$key]);
+ if (!empty($recursiveDiff)) {
+ $diffResult[$key] = $recursiveDiff;
+ }
+ } else {
+ if (!in_array($value, $actual, true)) {
+ $diffResult[$key] = $value;
+ }
+ }
+ } else {
+ $diffResult[$key] = $value;
+ }
+ }
+
+ return $diffResult;
+ }
+}
diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php
index 5af6413840c27..2fe93c02e7adb 100644
--- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php
+++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php
@@ -213,7 +213,7 @@ private function processResponseHeaders(string $headers): array
$headerLines = preg_split('/((\r?\n)|(\r\n?))/', $headers);
foreach ($headerLines as $headerLine) {
- $headerParts = preg_split('/:/', $headerLine);
+ $headerParts = preg_split('/: /', $headerLine, 2);
if (count($headerParts) == 2) {
$headersArray[trim($headerParts[0])] = trim($headerParts[1]);
} elseif (preg_match('/HTTP\/[\.0-9]+/', $headerLine)) {
diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php
index 7f67c8c9ca8df..3de18a932f2cd 100644
--- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php
+++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQlAbstract.php
@@ -184,61 +184,4 @@ protected function assertResponseFields($actualResponse, $assertionMap)
);
}
}
-
- /**
- * Compare arrays recursively regardless of nesting.
- *
- * Can compare arrays that have both one level and n-level nesting.
- * ```
- * [
- * 'products' => [
- * 'items' => [
- * [
- * 'sku' => 'bundle-product',
- * 'type_id' => 'bundle',
- * 'items' => [
- * [
- * 'title' => 'Bundle Product Items',
- * 'sku' => 'bundle-product',
- * 'options' => [
- * [
- * 'price' => 2.75,
- * 'label' => 'Simple Product',
- * 'product' => [
- * 'name' => 'Simple Product',
- * 'sku' => 'simple',
- * ]
- * ]
- * ]
- * ]
- * ];
- * ```
- *
- * @param array $expected
- * @param array $actual
- * @return array
- */
- public function compareArraysRecursively(array $expected, array $actual): array
- {
- $diffResult = [];
-
- foreach ($expected as $key => $value) {
- if (array_key_exists($key, $actual)) {
- if (is_array($value)) {
- $recursiveDiff = $this->compareArraysRecursively($value, $actual[$key]);
- if (!empty($recursiveDiff)) {
- $diffResult[$key] = $recursiveDiff;
- }
- } else {
- if (!in_array($value, $actual, true)) {
- $diffResult[$key] = $value;
- }
- }
- } else {
- $diffResult[$key] = $value;
- }
- }
-
- return $diffResult;
- }
}
diff --git a/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php b/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php
index 7a4f472c69513..538c0b0ee5fac 100644
--- a/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php
@@ -225,6 +225,7 @@ public function testUpdateBundleAddSelection()
public function testUpdateBundleAddAndDeleteOption()
{
$bundleProduct = $this->createDynamicBundleProduct();
+ $linkedProductPrice = 20;
$bundleProductOptions = $this->getBundleProductOptions($bundleProduct);
@@ -238,7 +239,7 @@ public function testUpdateBundleAddAndDeleteOption()
[
'sku' => 'simple2',
'qty' => 2,
- "price" => 20,
+ "price" => $linkedProductPrice,
"price_type" => 1,
"is_default" => false,
],
@@ -256,6 +257,7 @@ public function testUpdateBundleAddAndDeleteOption()
$this->assertFalse(isset($bundleOptions[1]));
$this->assertEquals('simple2', $bundleOptions[0]['product_links'][0]['sku']);
$this->assertEquals(2, $bundleOptions[0]['product_links'][0]['qty']);
+ $this->assertEquals($linkedProductPrice, $bundleOptions[0]['product_links'][0]['price']);
}
/**
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryManagementTest.php
index bc3869df6a65b..1523bfe957901 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryManagementTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryManagementTest.php
@@ -9,6 +9,7 @@
use Magento\TestFramework\TestCase\WebapiAbstract;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\CompareArraysRecursively;
/**
* Tests CategoryManagement
@@ -19,6 +20,20 @@ class CategoryManagementTest extends WebapiAbstract
const SERVICE_NAME = 'catalogCategoryManagementV1';
+ /**
+ * @var CompareArraysRecursively
+ */
+ private $compareArraysRecursively;
+
+ /**
+ * @inheritDoc
+ */
+ protected function setUp(): void
+ {
+ $objectManager = Bootstrap::getObjectManager();
+ $this->compareArraysRecursively = $objectManager->create(CompareArraysRecursively::class);
+ }
+
/**
* Tests getTree operation
*
@@ -40,8 +55,8 @@ public function testTree($rootCategoryId, $depth, $expected)
]
];
$result = $this->_webApiCall($serviceInfo, $requestData);
- $expected = array_replace_recursive($result, $expected);
- $this->assertEquals($expected, $result);
+ $diff = $this->compareArraysRecursively->execute($expected, $result);
+ self::assertEquals([], $diff, "Actual categories response doesn't equal expected data");
}
/**
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
index 21b93645fd15a..461ab6c989104 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CategoryRepositoryTest.php
@@ -43,6 +43,11 @@ class CategoryRepositoryTest extends WebapiAbstract
*/
private $adminTokens;
+ /**
+ * @var string[]
+ */
+ private $createdCategories;
+
/**
* @inheritDoc
*/
@@ -132,8 +137,7 @@ public function testCreate()
sprintf('"%s" field value is invalid', $fieldName)
);
}
- // delete category to clean up auto-generated url rewrites
- $this->deleteCategory($result['id']);
+ $this->createdCategories = [$result['id']];
}
/**
@@ -214,8 +218,7 @@ public function testUpdate()
$this->assertFalse((bool)$category->getIsActive(), 'Category "is_active" must equal to false');
$this->assertEquals("Update Category Test", $category->getName());
$this->assertEquals("Update Category Description Test", $category->getDescription());
- // delete category to clean up auto-generated url rewrites
- $this->deleteCategory($categoryId);
+ $this->createdCategories = [$categoryId];
}
/**
@@ -243,8 +246,7 @@ public function testUpdateWithDefaultSortByAttribute()
$this->assertTrue((bool)$category->getIsActive(), 'Category "is_active" must equal to true');
$this->assertEquals("Update Category Test With default_sort_by Attribute", $category->getName());
$this->assertEquals("name", $category->getDefaultSortBy());
- // delete category to clean up auto-generated url rewrites
- $this->deleteCategory($categoryId);
+ $this->createdCategories = [$categoryId];
}
protected function getSimpleCategoryData($categoryData = [])
@@ -476,5 +478,23 @@ public function testSaveDesign(): void
}
//We don't have permissions to do that.
$this->assertEquals('Not allowed to edit the category\'s design attributes', $exceptionMessage);
+ $this->createdCategories = [$result['id']];
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @return void
+ */
+ protected function tearDown(): void
+ {
+ if (!empty($this->createdCategories)) {
+ // delete category to clean up auto-generated url rewrites
+ foreach ($this->createdCategories as $categoryId) {
+ $this->deleteCategory($categoryId);
+ }
+ }
+
+ parent::tearDown();
}
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductMultipleOptionsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductMultipleOptionsTest.php
index 3409b5e3af1af..77c4d5b84e72e 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductMultipleOptionsTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductMultipleOptionsTest.php
@@ -7,6 +7,8 @@
namespace Magento\GraphQl\Bundle;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\CompareArraysRecursively;
use Magento\TestFramework\TestCase\GraphQlAbstract;
/**
@@ -14,6 +16,20 @@
*/
class BundleProductMultipleOptionsTest extends GraphQlAbstract
{
+ /**
+ * @var CompareArraysRecursively
+ */
+ private $compareArraysRecursively;
+
+ /**
+ * @inheritDoc
+ */
+ protected function setUp(): void
+ {
+ $objectManager = Bootstrap::getObjectManager();
+ $this->compareArraysRecursively = $objectManager->create(CompareArraysRecursively::class);
+ }
+
/**
* @magentoApiDataFixture Magento/Bundle/_files/product_with_multiple_options.php
* @param array $bundleProductDataProvider
@@ -85,7 +101,7 @@ private function assertBundleProduct(array $response, array $bundleProductDataPr
$productItems = $response['products']['items'];
foreach ($bundleProductDataProvider as $key => $data) {
- $diff = $this->compareArraysRecursively($data, $productItems[$key]);
+ $diff = $this->compareArraysRecursively->execute($data, $productItems[$key]);
self::assertEquals([], $diff, "Actual response doesn't equal to expected data");
}
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php
index 25c808a549e80..b8f59b34fae0c 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CorsHeadersTest.php
@@ -76,7 +76,7 @@ public function testCorsHeadersWhenCorsIsEnabled(): void
$this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_HEADERS, 'Origin');
$this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOW_CREDENTIALS, '1');
$this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_METHODS, 'GET,POST');
- $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_ORIGINS, 'magento.local');
+ $this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_ALLOWED_ORIGINS, 'http://magento.local');
$this->resourceConfig->saveConfig(Configuration::XML_PATH_CORS_MAX_AGE, '86400');
$this->reinitConfig->reinit();
@@ -85,7 +85,7 @@ public function testCorsHeadersWhenCorsIsEnabled(): void
self::assertEquals('Origin', $headers['Access-Control-Allow-Headers']);
self::assertEquals('1', $headers['Access-Control-Allow-Credentials']);
self::assertEquals('GET,POST', $headers['Access-Control-Allow-Methods']);
- self::assertEquals('magento.local', $headers['Access-Control-Allow-Origin']);
+ self::assertEquals('http://magento.local', $headers['Access-Control-Allow-Origin']);
self::assertEquals('86400', $headers['Access-Control-Max-Age']);
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
index be183fe93815a..67012e75bf272 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/CreateEmptyCartTest.php
@@ -39,6 +39,11 @@ class CreateEmptyCartTest extends GraphQlAbstract
*/
private $quoteIdMaskFactory;
+ /**
+ * @var string
+ */
+ private $maskedQuoteId;
+
protected function setUp(): void
{
$objectManager = Bootstrap::getObjectManager();
@@ -61,6 +66,7 @@ public function testCreateEmptyCart()
self::assertNotNull($guestCart->getId());
self::assertNull($guestCart->getCustomer()->getId());
self::assertEquals('default', $guestCart->getStore()->getCode());
+ self::assertEquals('1', $guestCart->getCustomerIsGuest());
}
/**
@@ -81,6 +87,7 @@ public function testCreateEmptyCartWithNotDefaultStore()
self::assertNotNull($guestCart->getId());
self::assertNull($guestCart->getCustomer()->getId());
self::assertSame('fixture_second_store', $guestCart->getStore()->getCode());
+ self::assertEquals('1', $guestCart->getCustomerIsGuest());
}
/**
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php
index c2f94128ef8ec..cb210b180682c 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php
@@ -10,7 +10,7 @@
use Magento\TestFramework\TestCase\GraphQlAbstract;
/**
- * Get related products test
+ * Test coverage for get related products
*/
class GetRelatedProductsTest extends GraphQlAbstract
{
@@ -49,6 +49,40 @@ public function testQueryRelatedProducts()
self::assertRelatedProducts($relatedProducts);
}
+ /**
+ * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php
+ */
+ public function testQueryDisableRelatedProduct()
+ {
+ $productSku = 'simple_with_cross';
+
+ $query = <<graphQlQuery($query);
+
+ self::assertArrayHasKey('products', $response);
+ self::assertArrayHasKey('items', $response['products']);
+ self::assertCount(1, $response['products']['items']);
+ self::assertArrayHasKey(0, $response['products']['items']);
+ self::assertArrayHasKey('related_products', $response['products']['items'][0]);
+ $relatedProducts = $response['products']['items'][0]['related_products'];
+ self::assertCount(0, $relatedProducts);
+ }
+
/**
* @magentoApiDataFixture Magento/Catalog/_files/products_crosssell.php
*/
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php
index 2db06e383758f..0bbdf5a4c9803 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/TestModule/GraphQlQueryTest.php
@@ -10,7 +10,7 @@
use Magento\TestFramework\TestCase\GraphQlAbstract;
/**
- * Class GraphQlQueryTest
+ * Test for basic GraphQl features
*/
class GraphQlQueryTest extends GraphQlAbstract
{
@@ -100,4 +100,29 @@ public function testQueryViaGetRequestWithVariablesReturnsResults()
$this->assertArrayHasKey('testItem', $response);
}
+
+ public function testQueryTestUnionResults()
+ {
+ $query = <<graphQlQuery($query);
+
+ $this->assertArrayHasKey('testUnion', $response);
+ $testUnion = $response['testUnion'];
+ $this->assertArrayHasKey('custom_name1', $testUnion);
+ $this->assertEquals('custom_name1_value', $testUnion['custom_name1']);
+ $this->assertArrayNotHasKey('custom_name2', $testUnion);
+ }
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php
index a81ec701b22a8..b97cd379e4384 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddBundleProductToWishlistTest.php
@@ -16,6 +16,7 @@
use Magento\Integration\Api\CustomerTokenServiceInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
+use Magento\Ui\Component\Form\Element\Select;
use Magento\Wishlist\Model\Item;
use Magento\Wishlist\Model\WishlistFactory;
@@ -74,7 +75,7 @@ public function testAddBundleProductWithOptions(): void
$selection = $typeInstance->getSelectionsCollection([$option->getId()], $product)->getFirstItem();
$optionId = $option->getId();
$selectionId = $selection->getSelectionId();
- $bundleOptions = $this->generateBundleOptionIdV2((int) $optionId, (int) $selectionId, $optionQty);
+ $bundleOptions = $this->generateBundleOptionUid((int) $optionId, (int) $selectionId, $optionQty);
$query = $this->getQuery($sku, $qty, $bundleOptions);
$response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
@@ -88,9 +89,13 @@ public function testAddBundleProductWithOptions(): void
$this->assertEquals($wishlist->getItemsCount(), $response['items_count']);
$this->assertEquals($wishlist->getSharingCode(), $response['sharing_code']);
$this->assertEquals($wishlist->getUpdatedAt(), $response['updated_at']);
- $this->assertEquals($item->getData('qty'), $response['items'][0]['qty']);
- $this->assertEquals($item->getDescription(), $response['items'][0]['description']);
- $this->assertEquals($item->getAddedAt(), $response['items'][0]['added_at']);
+ $this->assertEquals($item->getData('qty'), $response['items_v2'][0]['quantity']);
+ $this->assertEquals($item->getDescription(), $response['items_v2'][0]['description']);
+ $this->assertEquals($item->getAddedAt(), $response['items_v2'][0]['added_at']);
+ $this->assertNotEmpty($response['items_v2'][0]['bundle_options']);
+ $bundleOptions = $response['items_v2'][0]['bundle_options'];
+ $this->assertEquals('Bundle Product Items', $bundleOptions[0]['label']);
+ $this->assertEquals(Select::NAME, $bundleOptions[0]['type']);
}
/**
@@ -149,11 +154,24 @@ private function getQuery(
sharing_code
items_count
updated_at
- items {
+ items_v2 {
id
description
- qty
+ quantity
added_at
+ ... on BundleWishlistItem {
+ bundle_options {
+ id
+ label
+ type
+ values {
+ id
+ label
+ quantity
+ price
+ }
+ }
+ }
}
}
}
@@ -169,7 +187,7 @@ private function getQuery(
*
* @return string
*/
- private function generateBundleOptionIdV2(int $optionId, int $selectionId, int $quantity): string
+ private function generateBundleOptionUid(int $optionId, int $selectionId, int $quantity): string
{
return base64_encode("bundle/$optionId/$selectionId/$quantity");
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php
index d8d44541f899d..cffc5eb6f93c1 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddConfigurableProductToWishlistTest.php
@@ -48,7 +48,7 @@ protected function setUp(): void
*
* @throws Exception
*/
- public function testAddDownloadableProductWithOptions(): void
+ public function testAddConfigurableProductWithOptions(): void
{
$product = $this->getConfigurableProductInfo();
$customerId = 1;
@@ -57,7 +57,7 @@ public function testAddDownloadableProductWithOptions(): void
$valueIndex = $product['configurable_options'][0]['values'][0]['value_index'];
$childSku = $product['variants'][0]['product']['sku'];
$parentSku = $product['sku'];
- $selectedConfigurableOptionsQuery = $this->generateSuperAttributesIdV2Query($attributeId, $valueIndex);
+ $selectedConfigurableOptionsQuery = $this->generateSuperAttributesUidQuery($attributeId, $valueIndex);
$query = $this->getQuery($parentSku, $childSku, $qty, $selectedConfigurableOptionsQuery);
@@ -66,16 +66,19 @@ public function testAddDownloadableProductWithOptions(): void
/** @var Item $wishlistItem */
$wishlistItem = $wishlist->getItemCollection()->getFirstItem();
- self::assertArrayHasKey('addProductsToWishlist', $response);
- self::assertArrayHasKey('wishlist', $response['addProductsToWishlist']);
+ $this->assertArrayHasKey('addProductsToWishlist', $response);
+ $this->assertArrayHasKey('wishlist', $response['addProductsToWishlist']);
$wishlistResponse = $response['addProductsToWishlist']['wishlist'];
- self::assertEquals($wishlist->getItemsCount(), $wishlistResponse['items_count']);
- self::assertEquals($wishlist->getSharingCode(), $wishlistResponse['sharing_code']);
- self::assertEquals($wishlist->getUpdatedAt(), $wishlistResponse['updated_at']);
- self::assertEquals($wishlistItem->getId(), $wishlistResponse['items'][0]['id']);
- self::assertEquals($wishlistItem->getData('qty'), $wishlistResponse['items'][0]['qty']);
- self::assertEquals($wishlistItem->getDescription(), $wishlistResponse['items'][0]['description']);
- self::assertEquals($wishlistItem->getAddedAt(), $wishlistResponse['items'][0]['added_at']);
+ $this->assertEquals($wishlist->getItemsCount(), $wishlistResponse['items_count']);
+ $this->assertEquals($wishlist->getSharingCode(), $wishlistResponse['sharing_code']);
+ $this->assertEquals($wishlist->getUpdatedAt(), $wishlistResponse['updated_at']);
+ $this->assertEquals($wishlistItem->getId(), $wishlistResponse['items_v2'][0]['id']);
+ $this->assertEquals($wishlistItem->getData('qty'), $wishlistResponse['items_v2'][0]['quantity']);
+ $this->assertEquals($wishlistItem->getDescription(), $wishlistResponse['items_v2'][0]['description']);
+ $this->assertEquals($wishlistItem->getAddedAt(), $wishlistResponse['items_v2'][0]['added_at']);
+ $this->assertNotEmpty($wishlistResponse['items_v2'][0]['configurable_options']);
+ $configurableOptions = $wishlistResponse['items_v2'][0]['configurable_options'];
+ $this->assertEquals('Test Configurable', $configurableOptions[0]['option_label']);
}
/**
@@ -135,11 +138,20 @@ private function getQuery(
sharing_code
items_count
updated_at
- items {
+ items_v2 {
id
description
- qty
+ quantity
added_at
+ ... on ConfigurableWishlistItem {
+ child_sku
+ configurable_options {
+ id
+ option_label
+ value_id
+ value_label
+ }
+ }
}
}
}
@@ -148,14 +160,14 @@ private function getQuery(
}
/**
- * Generates Id_v2 for super configurable product super attributes
+ * Generates uid for super configurable product super attributes
*
* @param int $attributeId
* @param int $valueIndex
*
* @return string
*/
- private function generateSuperAttributesIdV2Query(int $attributeId, int $valueIndex): string
+ private function generateSuperAttributesUidQuery(int $attributeId, int $valueIndex): string
{
return 'selected_options: ["' . base64_encode("configurable/$attributeId/$valueIndex") . '"]';
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php
index 489a960056f1b..0de45fb21b20b 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/AddDownloadableProductToWishlistTest.php
@@ -37,9 +37,9 @@ class AddDownloadableProductToWishlistTest extends GraphQlAbstract
private $wishlistFactory;
/**
- * @var GetCustomOptionsWithIDV2ForQueryBySku
+ * @var GetCustomOptionsWithUidForQueryBySku
*/
- private $getCustomOptionsWithIDV2ForQueryBySku;
+ private $getCustomOptionsWithUidForQueryBySku;
/**
* Set Up
@@ -49,69 +49,75 @@ protected function setUp(): void
$this->objectManager = Bootstrap::getObjectManager();
$this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class);
$this->wishlistFactory = $this->objectManager->get(WishlistFactory::class);
- $this->getCustomOptionsWithIDV2ForQueryBySku =
- $this->objectManager->get(GetCustomOptionsWithIDV2ForQueryBySku::class);
+ $this->getCustomOptionsWithUidForQueryBySku =
+ $this->objectManager->get(GetCustomOptionsWithUidForQueryBySku::class);
}
/**
- * @magentoConfigFixture default_store wishlist/general/active 1
+ * @magentoConfigFixture default_store wishlist/general/active 0
* @magentoApiDataFixture Magento/Customer/_files/customer.php
* @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable_with_custom_options.php
*/
- public function testAddDownloadableProductWithOptions(): void
+ public function testAddDownloadableProductOnDisabledWishlist(): void
{
- $customerId = 1;
- $sku = 'downloadable-product-with-purchased-separately-links';
$qty = 2;
+ $sku = 'downloadable-product-with-purchased-separately-links';
$links = $this->getProductsLinks($sku);
$linkId = key($links);
- $itemOptions = $this->getCustomOptionsWithIDV2ForQueryBySku->execute($sku);
+ $itemOptions = $this->getCustomOptionsWithUidForQueryBySku->execute($sku);
$itemOptions['selected_options'][] = $this->generateProductLinkSelectedOptions($linkId);
- $productOptionsQuery = preg_replace(
+ $productOptionsQuery = trim(preg_replace(
'/"([^"]+)"\s*:\s*/',
'$1:',
json_encode($itemOptions)
- );
- $query = $this->getQuery($qty, $sku, trim($productOptionsQuery, '{}'));
- $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
- $wishlist = $this->wishlistFactory->create();
- $wishlist->loadByCustomerId($customerId, true);
- /** @var Item $wishlistItem */
- $wishlistItem = $wishlist->getItemCollection()->getFirstItem();
-
- self::assertArrayHasKey('addProductsToWishlist', $response);
- self::assertArrayHasKey('wishlist', $response['addProductsToWishlist']);
- $wishlistResponse = $response['addProductsToWishlist']['wishlist'];
- self::assertEquals($wishlist->getItemsCount(), $wishlistResponse['items_count']);
- self::assertEquals($wishlist->getSharingCode(), $wishlistResponse['sharing_code']);
- self::assertEquals($wishlist->getUpdatedAt(), $wishlistResponse['updated_at']);
- self::assertEquals($wishlistItem->getId(), $wishlistResponse['items'][0]['id']);
- self::assertEquals($wishlistItem->getData('qty'), $wishlistResponse['items'][0]['qty']);
- self::assertEquals($wishlistItem->getDescription(), $wishlistResponse['items'][0]['description']);
- self::assertEquals($wishlistItem->getAddedAt(), $wishlistResponse['items'][0]['added_at']);
+ ), '{}');
+ $query = $this->getQuery($qty, $sku, $productOptionsQuery);
+ $this->expectExceptionMessage('The wishlist configuration is currently disabled.');
+ $this->graphQlMutation($query, [], '', $this->getHeaderMap());
}
/**
- * @magentoConfigFixture default_store wishlist/general/active 0
+ * @magentoConfigFixture default_store wishlist/general/active 1
* @magentoApiDataFixture Magento/Customer/_files/customer.php
* @magentoApiDataFixture Magento/Downloadable/_files/product_downloadable_with_custom_options.php
*/
- public function testAddDownloadableProductOnDisabledWishlist(): void
+ public function testAddDownloadableProductWithOptions(): void
{
- $qty = 2;
+ $customerId = 1;
$sku = 'downloadable-product-with-purchased-separately-links';
+ $qty = 2;
$links = $this->getProductsLinks($sku);
$linkId = key($links);
- $itemOptions = $this->getCustomOptionsWithIDV2ForQueryBySku->execute($sku);
+ $itemOptions = $this->getCustomOptionsWithUidForQueryBySku->execute($sku);
$itemOptions['selected_options'][] = $this->generateProductLinkSelectedOptions($linkId);
- $productOptionsQuery = trim(preg_replace(
+ $productOptionsQuery = preg_replace(
'/"([^"]+)"\s*:\s*/',
'$1:',
json_encode($itemOptions)
- ), '{}');
- $query = $this->getQuery($qty, $sku, $productOptionsQuery);
- $this->expectExceptionMessage('The wishlist is not currently available.');
- $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ );
+ $query = $this->getQuery($qty, $sku, trim($productOptionsQuery, '{}'));
+ $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
+ $wishlist = $this->wishlistFactory->create();
+ $wishlist->loadByCustomerId($customerId, true);
+ /** @var Item $wishlistItem */
+ $wishlistItem = $wishlist->getItemCollection()->getFirstItem();
+
+ $this->assertArrayHasKey('addProductsToWishlist', $response);
+ $this->assertArrayHasKey('wishlist', $response['addProductsToWishlist']);
+ $wishlistResponse = $response['addProductsToWishlist']['wishlist'];
+ $this->assertEquals($wishlist->getItemsCount(), $wishlistResponse['items_count']);
+ $this->assertEquals($wishlist->getSharingCode(), $wishlistResponse['sharing_code']);
+ $this->assertEquals($wishlist->getUpdatedAt(), $wishlistResponse['updated_at']);
+ $this->assertEquals($wishlistItem->getId(), $wishlistResponse['items_v2'][0]['id']);
+ $this->assertEquals($wishlistItem->getData('qty'), $wishlistResponse['items_v2'][0]['quantity']);
+ $this->assertEquals($wishlistItem->getDescription(), $wishlistResponse['items_v2'][0]['description']);
+ $this->assertEquals($wishlistItem->getAddedAt(), $wishlistResponse['items_v2'][0]['added_at']);
+ $this->assertNotEmpty($wishlistResponse['items_v2'][0]['links_v2']);
+ $wishlistItemLinks = $wishlistResponse['items_v2'][0]['links_v2'];
+ $this->assertEquals('Downloadable Product Link 1', $wishlistItemLinks[0]['title']);
+ $this->assertNotEmpty($wishlistResponse['items_v2'][0]['samples']);
+ $wishlistItemSamples = $wishlistResponse['items_v2'][0]['samples'];
+ $this->assertEquals('Downloadable Product Sample', $wishlistItemSamples[0]['title']);
}
/**
@@ -190,11 +196,23 @@ private function getQuery(
sharing_code
items_count
updated_at
- items {
+ items_v2 {
id
description
- qty
+ quantity
added_at
+ ... on DownloadableWishlistItem {
+ links_v2 {
+ id
+ title
+ sample_url
+ }
+ samples {
+ id
+ title
+ sample_url
+ }
+ }
}
}
}
@@ -203,7 +221,7 @@ private function getQuery(
}
/**
- * Generates Id_v2 for downloadable links
+ * Generates uid for downloadable links
*
* @param int $linkId
*
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php
index 0a8e1757a2ce2..04095c1679d2f 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/CustomerWishlistTest.php
@@ -131,7 +131,7 @@ public function testGuestCannotGetWishlist()
public function testCustomerCannotGetWishlistWhenDisabled()
{
$this->expectException(\Exception::class);
- $this->expectExceptionMessage('The wishlist is not currently available.');
+ $this->expectExceptionMessage('The wishlist configuration is currently disabled.');
$query =
<<customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
+ $this->wishlistCollectionFactory = Bootstrap::getObjectManager()->get(CollectionFactory::class);
+ }
+
+ /**
+ * Test fetching customer wishlist
+ *
+ * @magentoConfigFixture default_store wishlist/general/active 1
+ * @magentoApiDataFixture Magento/Wishlist/_files/wishlist.php
+ */
+ public function testCustomerWishlist(): void
+ {
+ $customerId = 1;
+ /** @var Wishlist $wishlist */
+ $collection = $this->wishlistCollectionFactory->create()->filterByCustomerId($customerId);
+ /** @var Item $wishlistItem */
+ $wishlistItem = $collection->getFirstItem();
+ $response = $this->graphQlQuery(
+ $this->getQuery(),
+ [],
+ '',
+ $this->getCustomerAuthHeaders('customer@example.com', 'password')
+ );
+ $this->assertArrayHasKey('wishlists', $response['customer']);
+ $wishlist = $response['customer']['wishlists'][0];
+ $this->assertEquals($wishlistItem->getItemsCount(), $wishlist['items_count']);
+ $this->assertEquals($wishlistItem->getSharingCode(), $wishlist['sharing_code']);
+ $this->assertEquals($wishlistItem->getUpdatedAt(), $wishlist['updated_at']);
+ $wishlistItemResponse = $wishlist['items_v2'][0];
+ $this->assertEquals('simple', $wishlistItemResponse['product']['sku']);
+ }
+
+ /**
+ * Testing fetching the wishlist when wishlist is disabled
+ *
+ * @magentoConfigFixture default_store wishlist/general/active 0
+ * @magentoApiDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testCustomerCannotGetWishlistWhenDisabled(): void
+ {
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('The wishlist configuration is currently disabled.');
+ $this->graphQlQuery(
+ $this->getQuery(),
+ [],
+ '',
+ $this->getCustomerAuthHeaders('customer@example.com', 'password')
+ );
+ }
+
+ /**
+ * Test wishlist fetching for a guest customer
+ *
+ * @magentoConfigFixture default_store wishlist/general/active 1
+ */
+ public function testGuestCannotGetWishlist(): void
+ {
+ $this->expectException(Exception::class);
+ $this->expectExceptionMessage('The current customer isn\'t authorized.');
+ $this->graphQlQuery($this->getQuery());
+ }
+
+ /**
+ * Returns GraphQl query string
+ *
+ * @return string
+ */
+ private function getQuery(): string
+ {
+ return <<customerTokenService->createCustomerAccessToken($email, $password);
+
+ return ['Authorization' => 'Bearer ' . $customerToken];
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php
index ebe99289b8934..13aaecbc7b733 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/DeleteProductsFromWishlistTest.php
@@ -42,17 +42,17 @@ public function testDeleteWishlistItemFromWishlist(): void
$wishlist = $this->getWishlist();
$wishlistId = $wishlist['customer']['wishlist']['id'];
$wishlist = $wishlist['customer']['wishlist'];
- $wishlistItems = $wishlist['items'];
- self::assertEquals(1, $wishlist['items_count']);
+ $wishlistItems = $wishlist['items_v2'];
+ $this->assertEquals(1, $wishlist['items_count']);
$query = $this->getQuery((int) $wishlistId, (int) $wishlistItems[0]['id']);
$response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
- self::assertArrayHasKey('removeProductsFromWishlist', $response);
- self::assertArrayHasKey('wishlist', $response['removeProductsFromWishlist']);
+ $this->assertArrayHasKey('removeProductsFromWishlist', $response);
+ $this->assertArrayHasKey('wishlist', $response['removeProductsFromWishlist']);
$wishlistResponse = $response['removeProductsFromWishlist']['wishlist'];
- self::assertEquals(0, $wishlistResponse['items_count']);
- self::assertEmpty($wishlistResponse['items']);
+ $this->assertEquals(0, $wishlistResponse['items_count']);
+ $this->assertEmpty($wishlistResponse['items_v2']);
}
/**
@@ -98,10 +98,10 @@ private function getQuery(
id
sharing_code
items_count
- items {
+ items_v2 {
id
description
- qty
+ quantity
}
}
}
@@ -134,9 +134,9 @@ private function getCustomerWishlistQuery(): string
wishlist {
id
items_count
- items {
+ items_v2 {
id
- qty
+ quantity
description
}
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithUidForQueryBySku.php
similarity index 94%
rename from dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php
rename to dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithUidForQueryBySku.php
index fcba7458f317a..4bd0c135f039a 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithIDV2ForQueryBySku.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/GetCustomOptionsWithUidForQueryBySku.php
@@ -10,9 +10,9 @@
use Magento\Catalog\Api\ProductCustomOptionRepositoryInterface;
/**
- * Generate an array with test values for customizable options with encoded id_v2 value
+ * Generate an array with test values for customizable options with encoded uid value
*/
-class GetCustomOptionsWithIDV2ForQueryBySku
+class GetCustomOptionsWithUidForQueryBySku
{
/**
* @var ProductCustomOptionRepositoryInterface
@@ -71,7 +71,7 @@ public function execute(string $sku): array
}
/**
- * Returns id_v2 of the selected custom option
+ * Returns uid of the selected custom option
*
* @param int $optionId
* @param int $optionValueId
@@ -84,7 +84,7 @@ private function encodeSelectedOption(int $optionId, int $optionValueId): string
}
/**
- * Returns id_v2 of the entered custom option
+ * Returns uid of the entered custom option
*
* @param int $optionId
*
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php
index 9a9cd424e54ca..08273e7936640 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Wishlist/UpdateProductsFromWishlistTest.php
@@ -43,18 +43,18 @@ public function testUpdateSimpleProductFromWishlist(): void
$qty = 5;
$description = 'New Description';
$wishlistId = $wishlist['customer']['wishlist']['id'];
- $wishlistItem = $wishlist['customer']['wishlist']['items'][0];
- self::assertNotEquals($description, $wishlistItem['description']);
- self::assertNotEquals($qty, $wishlistItem['qty']);
+ $wishlistItem = $wishlist['customer']['wishlist']['items_v2'][0];
+ $this->assertNotEquals($description, $wishlistItem['description']);
+ $this->assertNotEquals($qty, $wishlistItem['quantity']);
$query = $this->getQuery((int) $wishlistId, (int) $wishlistItem['id'], $qty, $description);
$response = $this->graphQlMutation($query, [], '', $this->getHeaderMap());
- self::assertArrayHasKey('updateProductsInWishlist', $response);
- self::assertArrayHasKey('wishlist', $response['updateProductsInWishlist']);
+ $this->assertArrayHasKey('updateProductsInWishlist', $response);
+ $this->assertArrayHasKey('wishlist', $response['updateProductsInWishlist']);
$wishlistResponse = $response['updateProductsInWishlist']['wishlist'];
- self::assertEquals($qty, $wishlistResponse['items'][0]['qty']);
- self::assertEquals($description, $wishlistResponse['items'][0]['description']);
+ $this->assertEquals($qty, $wishlistResponse['items_v2'][0]['quantity']);
+ $this->assertEquals($description, $wishlistResponse['items_v2'][0]['description']);
}
/**
@@ -110,10 +110,10 @@ private function getQuery(
id
sharing_code
items_count
- items {
+ items_v2 {
id
description
- qty
+ quantity
}
}
}
@@ -146,9 +146,9 @@ private function getCustomerWishlistQuery(): string
wishlist {
id
items_count
- items {
+ items_v2 {
id
- qty
+ quantity
description
}
}
diff --git a/dev/tests/integration/etc/install-config-mysql.php.dist b/dev/tests/integration/etc/install-config-mysql.php.dist
index 4766048c62375..1d4b3d1951e32 100644
--- a/dev/tests/integration/etc/install-config-mysql.php.dist
+++ b/dev/tests/integration/etc/install-config-mysql.php.dist
@@ -11,6 +11,9 @@ return [
'db-name' => 'magento_integration_tests',
'db-prefix' => '',
'backend-frontname' => 'backend',
+ 'search-engine' => 'elasticsearch7',
+ 'elasticsearch-host' => 'localhost',
+ 'elasticsearch-port' => 9200,
'admin-user' => \Magento\TestFramework\Bootstrap::ADMIN_NAME,
'admin-password' => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
'admin-email' => \Magento\TestFramework\Bootstrap::ADMIN_EMAIL,
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php
index 2f4b7bf79c1d6..9172d7cf857e5 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/AbstractDataFixture.php
@@ -7,8 +7,6 @@
namespace Magento\TestFramework\Annotation;
-use Magento\Framework\Component\ComponentRegistrarInterface;
-use Magento\Framework\Exception\LocalizedException;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
use PHPUnit\Framework\Exception;
use PHPUnit\Framework\TestCase;
@@ -106,10 +104,18 @@ protected function _applyOneFixture($fixture)
* Execute fixture scripts if any
*
* @param array $fixtures
+ * @param TestCase $test
* @return void
*/
- protected function _applyFixtures(array $fixtures)
+ protected function _applyFixtures(array $fixtures, TestCase $test)
{
+ /** @var \Magento\TestFramework\Annotation\TestsIsolation $testsIsolation */
+ $testsIsolation = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ \Magento\TestFramework\Annotation\TestsIsolation::class
+ );
+ $dbIsolationState = $this->getDbIsolationState($test);
+ $testsIsolation->createDbSnapshot($test, $dbIsolationState);
+
/* Execute fixture scripts */
foreach ($fixtures as $oneFixture) {
$this->_applyOneFixture($oneFixture);
@@ -122,9 +128,10 @@ protected function _applyFixtures(array $fixtures)
/**
* Revert changes done by fixtures
*
+ * @param TestCase|null $test
* @return void
*/
- protected function _revertFixtures()
+ protected function _revertFixtures(?TestCase $test = null)
{
$resolver = Resolver::getInstance();
$resolver->setCurrentFixtureType($this->getAnnotation());
@@ -149,13 +156,22 @@ protected function _revertFixtures()
}
$this->_appliedFixtures = [];
$resolver->setCurrentFixtureType(null);
+
+ if (null !== $test) {
+ /** @var \Magento\TestFramework\Annotation\TestsIsolation $testsIsolation */
+ $testsIsolation = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ \Magento\TestFramework\Annotation\TestsIsolation::class
+ );
+ $dbIsolationState = $this->getDbIsolationState($test);
+ $testsIsolation->checkTestIsolation($test, $dbIsolationState);
+ }
}
/**
* Return is explicit set isolation state
*
* @param TestCase $test
- * @return bool|null
+ * @return array|null
*/
protected function getDbIsolationState(TestCase $test)
{
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php
index 02e53fc0a80ed..ffcdc186af520 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixture.php
@@ -32,7 +32,7 @@ public function startTestTransactionRequest(TestCase $test, Transaction $param):
if ($this->getDbIsolationState($test) !== ['disabled']) {
$param->requestTransactionStart();
} else {
- $this->_applyFixtures($fixtures);
+ $this->_applyFixtures($fixtures, $test);
}
}
}
@@ -51,7 +51,7 @@ public function endTestTransactionRequest(TestCase $test, Transaction $param): v
if ($this->getDbIsolationState($test) !== ['disabled']) {
$param->requestTransactionRollback();
} else {
- $this->_revertFixtures();
+ $this->_revertFixtures($test);
}
}
}
@@ -64,12 +64,13 @@ public function endTestTransactionRequest(TestCase $test, Transaction $param): v
*/
public function startTransaction(TestCase $test): void
{
- $this->_applyFixtures($this->_getFixtures($test));
+ $this->_applyFixtures($this->_getFixtures($test), $test);
}
/**
* Handler for 'rollbackTransaction' event
*
+ * @param TestCase $test
* @return void
*/
public function rollbackTransaction(): void
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php
index 5685fea44f734..b36aebfd84728 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/DataFixtureBeforeTransaction.php
@@ -24,7 +24,7 @@ public function startTest(TestCase $test)
{
$fixtures = $this->_getFixtures($test);
if ($fixtures) {
- $this->_applyFixtures($fixtures);
+ $this->_applyFixtures($fixtures, $test);
}
}
@@ -37,7 +37,7 @@ public function endTest(TestCase $test)
{
/* Isolate other tests from test-specific fixtures */
if ($this->_appliedFixtures && $this->_getFixtures($test)) {
- $this->_revertFixtures();
+ $this->_revertFixtures($test);
}
}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/TestsIsolation.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/TestsIsolation.php
new file mode 100644
index 0000000000000..119ee1013a15c
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/TestsIsolation.php
@@ -0,0 +1,194 @@
+get(ResourceConnection::class);
+ $connection = $resource->getConnection();
+ $select = $connection->select()->from($table);
+ return $connection->fetchAll($select);
+ }
+
+ /**
+ * Create DB snapshot before test run.
+ *
+ * @param TestCase $test
+ * @param array|null $dbIsolationState
+ * @return void
+ */
+ public function createDbSnapshot(TestCase $test, ?array $dbIsolationState): void
+ {
+ if (null !== $dbIsolationState
+ && ($dbIsolationState !== ['enabled'])
+ && ($this->checkIsolationRequired($test))
+ ) {
+ ++$this->isolationLevel;
+ if ($this->isolationLevel === 1) {
+ $this->saveDbStateBeforeTestRun($test);
+ }
+ }
+ }
+
+ /**
+ * Check DB isolation when test ended.
+ *
+ * @param TestCase $test
+ * @param array|null $dbIsolationState
+ * @return void
+ */
+ public function checkTestIsolation(TestCase $test, ?array $dbIsolationState): void
+ {
+ if (null !== $dbIsolationState
+ && ($dbIsolationState !== ['enabled'])
+ && ($this->checkIsolationRequired($test))
+ ) {
+ --$this->isolationLevel;
+ if ($this->isolationLevel === 1) {
+ $this->checkResidualData($test);
+ }
+ }
+ }
+
+ /**
+ * Saving DB snapshot before fixtures applying.
+ *
+ * @param TestCase $test
+ * @return void
+ */
+ private function saveDbStateBeforeTestRun(TestCase $test): void
+ {
+ try {
+ if (empty($this->dbTableState)) {
+ foreach ($this->dbStateTables as $table) {
+ $this->dbTableState[$table] = $this->pullDbState($table);
+ }
+ }
+ } catch (\Throwable $e) {
+ $test->getTestResultObject()->addFailure($test, new AssertionFailedError($e->getMessage()), 0);
+ }
+ }
+
+ /**
+ * Check if test isolation is required for given scope of tests.
+ *
+ * @param TestCase $test
+ * @return bool
+ */
+ private function checkIsolationRequired(TestCase $test): bool
+ {
+ $isRequired = false;
+ if (!$test->getTestResultObject()) {
+ return $isRequired;
+ }
+
+ $testFilename = $test->getTestResultObject()->topTestSuite()->getName();
+ foreach ($this->testTypesToCheckIsolation as $testType) {
+ if (false !== strpos($testFilename, \sprintf('/dev/tests/%s/', $testType))) {
+ $isRequired = true;
+ break;
+ }
+ }
+
+ return $isRequired;
+ }
+
+ /**
+ * Check if there's residual data in DB after test execution.
+ *
+ * @param TestCase $test
+ * @return void
+ */
+ private function checkResidualData(TestCase $test): void
+ {
+ $isolationProblem = [];
+ foreach ($this->dbTableState as $table => $isolationData) {
+ try {
+ $diff = $this->dataDiff($isolationData, $this->pullDbState($table));
+ if (!empty($diff)) {
+ $isolationProblem[$table] = $diff;
+ }
+ } catch (\Throwable $e) {
+ $test->getTestResultObject()->addFailure($test, new AssertionFailedError($e->getMessage()), 0);
+ }
+ }
+
+ if (!empty($isolationProblem)) {
+ $test->getTestResultObject()->addFailure(
+ $test,
+ new AssertionFailedError(
+ "There was a problem with isolation: " . var_export($isolationProblem, true)
+ ),
+ 0
+ );
+ }
+ }
+
+ /**
+ * Compare data difference for m-dimensional array
+ *
+ * @param array $dataBefore
+ * @param array $dataAfter
+ * @return array
+ */
+ private function dataDiff(array $dataBefore, array $dataAfter): array
+ {
+ $diff = [];
+ if (count($dataBefore) !== count($dataAfter)) {
+ $diff = \array_slice($dataAfter, count($dataBefore));
+ }
+
+ return $diff;
+ }
+}
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php b/dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php
new file mode 100644
index 0000000000000..85007ad560d53
--- /dev/null
+++ b/dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php
@@ -0,0 +1,24 @@
+getBootstrap()
->getApplication()
diff --git a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
index b3cfc2ae4fe79..3abe6ea4e061d 100644
--- a/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
+++ b/dev/tests/integration/framework/tests/unit/testsuite/Magento/Test/Annotation/DataFixtureTest.php
@@ -8,10 +8,12 @@
namespace Magento\Test\Annotation;
use Magento\Framework\Component\ComponentRegistrar;
+use Magento\Framework\ObjectManagerInterface;
use Magento\TestFramework\Annotation\DataFixture;
use Magento\TestFramework\Event\Param\Transaction;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
use PHPUnit\Framework\TestCase;
+use Magento\TestFramework\Annotation\TestsIsolation;
/**
* Test class for \Magento\TestFramework\Annotation\DataFixture.
@@ -25,6 +27,11 @@ class DataFixtureTest extends TestCase
*/
protected $object;
+ /**
+ * @var TestsIsolation|\PHPUnit\Framework\MockObject\MockObject
+ */
+ protected $testsIsolationMock;
+
/**
* @inheritdoc
*/
@@ -33,6 +40,18 @@ protected function setUp(): void
$this->object = $this->getMockBuilder(DataFixture::class)
->setMethods(['_applyOneFixture', 'getComponentRegistrar', 'getTestKey'])
->getMock();
+ $this->testsIsolationMock = $this->getMockBuilder(TestsIsolation::class)
+ ->setMethods(['createDbSnapshot', 'checkTestIsolation'])
+ ->getMock();
+ /** @var ObjectManagerInterface|\PHPUnit\Framework\MockObject\MockObject $objectManager */
+ $objectManager = $this->getMockBuilder(ObjectManagerInterface::class)
+ ->setMethods(['get'])
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
+ $objectManager->expects($this->atLeastOnce())->method('get')->with(TestsIsolation::class)
+ ->willReturn($this->testsIsolationMock);
+ \Magento\TestFramework\Helper\Bootstrap::setObjectManager($objectManager);
+
$directory = __DIR__;
if (!defined('INTEGRATION_TESTS_DIR')) {
define('INTEGRATION_TESTS_DIR', dirname($directory, 4));
diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php
index 1ce2b01b10212..f6b8a06d2e16f 100644
--- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php
+++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php
@@ -7,6 +7,7 @@
namespace Magento\AdvancedPricingImportExport\Model\Export;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\File\Csv;
use Magento\TestFramework\Indexer\TestCase;
use Magento\TestFramework\Helper\Bootstrap;
@@ -103,6 +104,8 @@ public function testExport()
$this->assertEquals(count($origPricingData[$index]), count($newPricingData));
$this->assertEqualsOtherThanSkippedAttributes($origPricingData[$index], $newPricingData, []);
}
+
+ $this->removeImportedProducts($skus);
}
/**
@@ -163,6 +166,7 @@ public function testExportMultipleWebsites()
$this->assertEquals(count($origPricingData[$index]), count($newPricingData));
$this->assertEqualsOtherThanSkippedAttributes($origPricingData[$index], $newPricingData, []);
}
+ $this->removeImportedProducts($skus);
}
/**
@@ -173,14 +177,16 @@ public function testExportMultipleWebsites()
*/
public function testExportImportOfAdvancedPricing(): void
{
+ $simpleSku = 'simple';
+ $secondSimpleSku = 'second_simple';
$csvfile = uniqid('importexport_') . '.csv';
$exportContent = $this->exportData($csvfile);
$this->assertStringContainsString(
- 'second_simple,"All Websites [USD]","ALL GROUPS",10.0000,3.00,Discount',
+ \sprintf('%s,"All Websites [USD]","ALL GROUPS",10.0000,3.00,Discount', $secondSimpleSku),
$exportContent
);
$this->assertStringContainsString(
- 'simple,"All Websites [USD]",General,5.0000,95.000000,Fixed',
+ \sprintf('%s,"All Websites [USD]",General,5.0000,95.000000,Fixed', $simpleSku),
$exportContent
);
$this->updateTierPriceDataInCsv($csvfile);
@@ -224,6 +230,8 @@ public function testExportImportOfAdvancedPricing(): void
],
0.1
);
+
+ $this->removeImportedProducts([$simpleSku, $secondSimpleSku]);
}
/**
@@ -331,4 +339,31 @@ private function assertEqualsOtherThanSkippedAttributes($expected, $actual, $ski
}
}
}
+
+ /**
+ * Cleanup test by removing imported product.
+ *
+ * @param string[] $skus
+ * @return void
+ */
+ private function removeImportedProducts(array $skus): void
+ {
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ $registry = $this->objectManager->get(\Magento\Framework\Registry::class);
+ /** @var ProductRepositoryInterface $productRepository */
+ $registry->unregister('isSecureArea');
+ $registry->register('isSecureArea', true);
+
+ foreach ($skus as $sku) {
+ try {
+ $productRepository->deleteById($sku);
+ } catch (NoSuchEntityException $e) {
+ // product already deleted
+ }
+ }
+
+ $registry->unregister('isSecureArea');
+ $registry->register('isSecureArea', false);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products_rollback.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products_rollback.php
new file mode 100644
index 0000000000000..a814a7faea34b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products_rollback.php
@@ -0,0 +1,32 @@
+create(Product::class);
+$productRepository = $objectManager->create(ProductRepositoryInterface::class);
+$skus = ['AdvancedPricingSimple 1', 'AdvancedPricingSimple 2'];
+foreach ($skus as $sku) {
+ try {
+ $product = $productRepository->getById($sku);
+ $productRepository->delete($product);
+ } catch (NoSuchEntityException $exception) {
+ // product already removed
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website_rollback.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website_rollback.php
new file mode 100644
index 0000000000000..c5678d3fdab4a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website_rollback.php
@@ -0,0 +1,12 @@
+requireDataFixture('Magento/Store/_files/website_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/AdvancedPricingImportExport/_files/create_products_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php
index 513c1fff62fb6..fc33758a9d01d 100644
--- a/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/product_with_tier_pricing_rollback.php
@@ -13,7 +13,7 @@
* bundled items should not contain products with required custom options.
* However, if to create such a bundle product, it will be always out of stock.
*/
-Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/products.php');
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/products_rollback.php');
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
/** @var \Magento\Framework\Registry $registry */
diff --git a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Import/Product/Type/BundleTest.php b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Import/Product/Type/BundleTest.php
index 361ceed5c02fe..89fe02f3dbc6c 100644
--- a/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Import/Product/Type/BundleTest.php
+++ b/dev/tests/integration/testsuite/Magento/BundleImportExport/Model/Import/Product/Type/BundleTest.php
@@ -5,6 +5,8 @@
*/
namespace Magento\BundleImportExport\Model\Import\Product\Type;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\Framework\App\Filesystem\DirectoryList;
@@ -34,6 +36,11 @@ class BundleTest extends \Magento\TestFramework\Indexer\TestCase
*/
protected $objectManager;
+ /**
+ * @var string[]
+ */
+ private $importedProductSkus;
+
/**
* List of Bundle options SKU
*
@@ -131,6 +138,7 @@ public function testBundleImport()
}
}
}
+ $this->importedProductSkus = ['Simple 1', 'Simple 2', 'Simple 3', 'Bundle 1'];
}
/**
@@ -192,6 +200,7 @@ public function testBundleImportWithMultipleStoreViews(): void
}
}
}
+ $this->importedProductSkus = ['Simple 1', 'Simple 2', 'Simple 3', 'Bundle 1'];
}
/**
@@ -199,6 +208,26 @@ public function testBundleImportWithMultipleStoreViews(): void
*/
protected function tearDown(): void
{
+ if (!empty($this->importedProductSkus)) {
+ $objectManager = Bootstrap::getObjectManager();
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = $objectManager->create(ProductRepositoryInterface::class);
+ $registry = $objectManager->get(\Magento\Framework\Registry::class);
+ /** @var ProductRepositoryInterface $productRepository */
+ $registry->unregister('isSecureArea');
+ $registry->register('isSecureArea', true);
+
+ foreach ($this->importedProductSkus as $sku) {
+ try {
+ $productRepository->deleteById($sku);
+ } catch (NoSuchEntityException $e) {
+ // product already deleted
+ }
+ }
+ $registry->unregister('isSecureArea');
+ $registry->register('isSecureArea', false);
+ }
+
parent::tearDown();
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php
new file mode 100644
index 0000000000000..c50c21a3328ae
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php
@@ -0,0 +1,621 @@
+helperProduct = $this->objectManager->get(HelperProduct::class);
+ $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php
+ * @return void
+ */
+ public function testRenderCustomOptionsWithoutOptions(): void
+ {
+ $product = $this->productRepository->get('simple');
+ $this->assertEquals(
+ 0,
+ Xpath::getElementsCountForXpath(
+ "//fieldset[@id='product_composite_configure_fields_options']",
+ $this->getOptionHtml($product)
+ ),
+ 'The option block is expected to be empty!'
+ );
+ }
+
+ /**
+ * Check that options from text group(field, area) render as expected.
+ *
+ * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php
+ * @dataProvider renderCustomOptionsFromTextGroupProvider
+ * @param array $optionData
+ * @param array $checkArray
+ * @return void
+ */
+ public function testRenderCustomOptionsFromTextGroup(array $optionData, array $checkArray): void
+ {
+ $this->assertTextOptionRenderingOnProduct('simple', $optionData, $checkArray);
+ }
+
+ /**
+ * Provides test data to verify the display of text type options.
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ * @return array
+ */
+ public function renderCustomOptionsFromTextGroupProvider(): array
+ {
+ return [
+ 'type_text_required_field' => [
+ [
+ Option::KEY_TITLE => 'Test option type text 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => 0,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 0,
+ ],
+ [
+ 'contains' => [
+ 'block_with_required_class' => '',
+ 'title' => 'Test option type text 1',
+ ],
+ 'equals_xpath' => [
+ 'zero_price' => [
+ 'xpath' => "//label[contains(@class, 'admin__field-label')]/span",
+ 'message' => 'Expected empty price is incorrect or missing!',
+ 'expected' => 0,
+ ],
+ ],
+ ],
+ ],
+ 'type_text_is_required_option' => [
+ [
+ Option::KEY_TITLE => 'Test option type text 2',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 1,
+ Option::KEY_PRICE => 0,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 0,
+ ],
+ [
+ 'contains' => [
+ 'block_with_required_class' => ' ',
+ ],
+ ],
+ ],
+ 'type_text_fixed_positive_price' => [
+ [
+ Option::KEY_TITLE => 'Test option type text 3',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => 50,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 0,
+ ],
+ [
+ 'contains' => [
+ 'price' => 'data-price-amount="50"',
+ ],
+ 'equals_xpath' => [
+ 'sign_price' => [
+ 'xpath' => "//label[contains(@class, 'admin__field-label')]/span[contains(text(), '+')]",
+ 'message' => 'Expected positive price is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_text_fixed_negative_price' => [
+ [
+ Option::KEY_TITLE => 'Test option type text 4',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => -50,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 0,
+ ],
+ [
+ 'contains' => [
+ 'price' => 'data-price-amount="50"',
+ ],
+ 'equals_xpath' => [
+ 'sign_price' => [
+ 'xpath' => "//label[contains(@class, 'admin__field-label')]/span[contains(text(), '-')]",
+ 'message' => 'Expected negative price is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_text_percent_price' => [
+ [
+ Option::KEY_TITLE => 'Test option type text 5',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => 50,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT,
+ Option::KEY_MAX_CHARACTERS => 0,
+ ],
+ [
+ 'contains' => [
+ 'price' => 'data-price-amount="5"',
+ ],
+ ],
+ ],
+ 'type_text_max_characters' => [
+ [
+ Option::KEY_TITLE => 'Test option type text 6',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => 10,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 99,
+ ],
+ [
+ 'max_characters' => (string)__('Maximum number of characters:') . ' 99',
+ ],
+ ],
+ 'type_field' => [
+ [
+ Option::KEY_TITLE => 'Test option type field 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => 10,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 0,
+ 'configure_option_value' => 'Type field option value',
+ ],
+ [
+ 'equals_xpath' => [
+ 'control_price_attribute' => [
+ 'xpath' => "//input[@id='options_%s_text' and @price='%s']",
+ 'message' => 'Expected input price is incorrect or missing!',
+ ],
+ 'default_option_value' => [
+ 'xpath' => "//input[@id='options_%s_text' and @value='Type field option value']",
+ 'message' => 'Expected input default value is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_area' => [
+ [
+ Option::KEY_TITLE => 'Test option type area 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA,
+ Option::KEY_IS_REQUIRE => 0,
+ Option::KEY_PRICE => 10,
+ Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Option::KEY_MAX_CHARACTERS => 0,
+ 'configure_option_value' => 'Type area option value',
+ ],
+ [
+ 'equals_xpath' => [
+ 'control_price_attribute' => [
+ 'xpath' => "//textarea[@id='options_%s_text' and @price='%s']",
+ 'message' => 'Expected textarea price is incorrect or missing!',
+ ],
+ 'default_option_value' => [
+ 'xpath' => "//textarea[@id='options_%s_text' "
+ . "and contains(text(), 'Type area option value')]",
+ 'message' => 'Expected textarea default value is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Check that options from select group(drop-down, radio buttons, checkbox, multiple select) render as expected.
+ *
+ * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php
+ * @dataProvider renderCustomOptionsFromSelectGroupProvider
+ * @param array $optionData
+ * @param array $optionValueData
+ * @param array $checkArray
+ * @return void
+ */
+ public function testRenderCustomOptionsFromSelectGroup(
+ array $optionData,
+ array $optionValueData,
+ array $checkArray
+ ): void {
+ $this->assertSelectOptionRenderingOnProduct('simple', $optionData, $optionValueData, $checkArray);
+ }
+
+ /**
+ * Provides test data to verify the display of select type options.
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ * @return array
+ */
+ public function renderCustomOptionsFromSelectGroupProvider(): array
+ {
+ return [
+ 'type_select_required_field' => [
+ [
+ Option::KEY_TITLE => 'Test option type select 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN,
+ Option::KEY_IS_REQUIRE => 0,
+ ],
+ [
+ Value::KEY_TITLE => 'Select value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'contains' => [
+ 'block_with_required_class' => ' ',
+ 'title' => ' Test option type select 1',
+ ],
+ 'equals_xpath' => [
+ 'required_element' => [
+ 'xpath' => "//select[@id='select_%s']",
+ 'message' => 'Expected select type is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_select_is_required_option' => [
+ [
+ Option::KEY_TITLE => 'Test option type select 2',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN,
+ Option::KEY_IS_REQUIRE => 1,
+ ],
+ [
+ Value::KEY_TITLE => 'Select value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'contains' => [
+ 'block_with_required_class' => ' ',
+ ],
+ ],
+ ],
+ 'type_drop_down_with_selected' => [
+ [
+ Option::KEY_TITLE => 'Test option type drop-down 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN,
+ Option::KEY_IS_REQUIRE => 0,
+ 'configure_option_value' => 'Drop-down value 1',
+ ],
+ [
+ Value::KEY_TITLE => 'Drop-down value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'equals_xpath' => [
+ 'element_type' => [
+ 'xpath' => "//select[contains(@class, 'admin__control-select')]",
+ 'message' => 'Expected drop down type is incorrect or missing!',
+ ],
+ 'default_value' => [
+ 'xpath' => "//option[contains(text(), '" . __('-- Please Select --') . "')]",
+ 'message' => 'Expected default value is incorrect or missing!',
+ ],
+ 'selected_value' => [
+ 'xpath' => "//option[@selected='selected' and contains(text(), 'Drop-down value 1')]",
+ 'message' => 'Expected selected value is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_multiple_with_selected' => [
+ [
+ Option::KEY_TITLE => 'Test option type multiple 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE,
+ Option::KEY_IS_REQUIRE => 0,
+ 'configure_option_value' => 'Multiple value 1',
+ ],
+ [
+ Value::KEY_TITLE => 'Multiple value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'equals_xpath' => [
+ 'element_type' => [
+ 'xpath' => "//select[contains(@class, 'admin__control-multiselect') "
+ . "and @multiple='multiple']",
+ 'message' => 'Expected multiple type is incorrect or missing!',
+ ],
+ 'selected_value' => [
+ 'xpath' => "//option[@selected='selected' and contains(text(), 'Multiple value 1')]",
+ 'message' => 'Expected selected value is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_checkable_required_field' => [
+ [
+ Option::KEY_TITLE => 'Test option type checkable 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO,
+ Option::KEY_IS_REQUIRE => 0,
+ ],
+ [
+ Value::KEY_TITLE => 'Checkable value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'equals_xpath' => [
+ 'required_checkable_option' => [
+ 'xpath' => "//div[@id='options-%s-list']",
+ 'message' => 'Expected checkable option is incorrect or missing!',
+ ],
+ 'option_value_title' => [
+ 'xpath' => "//label[@for='options_%s_2']/span[contains(text(), 'Checkable value 1')]",
+ 'message' => 'Expected option value title is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_radio_is_required_option' => [
+ [
+ Option::KEY_TITLE => 'Test option type radio 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO,
+ Option::KEY_IS_REQUIRE => 1,
+ ],
+ [
+ Value::KEY_TITLE => 'Radio value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'equals_xpath' => [
+ 'span_container' => [
+ 'xpath' => "//span[@id='options-%s-container']",
+ 'message' => 'Expected span container is incorrect or missing!',
+ ],
+ 'default_option_value' => [
+ 'xpath' => "//label[@for='options_%s']/span[contains(text(), '" . __('None') . "')]",
+ 'message' => 'Expected default option value is incorrect or missing!',
+ 'expected' => 0,
+ ],
+ ],
+ ],
+ ],
+ 'type_radio_with_selected' => [
+ [
+ Option::KEY_TITLE => 'Test option type radio 2',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO,
+ Option::KEY_IS_REQUIRE => 0,
+ 'configure_option_value' => 'Radio value 1',
+ ],
+ [
+ Value::KEY_TITLE => 'Radio value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ ],
+ [
+ 'equals_xpath' => [
+ 'default_option_value' => [
+ 'xpath' => "//label[@for='options_%s']/span[contains(text(), '" . __('None') . "')]",
+ 'message' => 'Expected default option value is incorrect or missing!',
+ ],
+ 'element_type' => [
+ 'xpath' => "//input[@id='options_%s_2' and contains(@class, 'admin__control-radio')]",
+ 'message' => 'Expected radio type is incorrect or missing!',
+ ],
+ 'selected_value' => [
+ 'xpath' => "//input[@id='options_%s_2' and @checked='checked']",
+ 'message' => 'Expected selected option value is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_checkbox_is_required_option' => [
+ [
+ Option::KEY_TITLE => 'Test option type checkbox 1',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX,
+ Option::KEY_IS_REQUIRE => 1,
+ ],
+ [
+ Value::KEY_TITLE => 'Checkbox value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Value::KEY_SKU => '',
+ ],
+ [
+ 'equals_xpath' => [
+ 'span_container' => [
+ 'xpath' => "//span[@id='options-%s-container']",
+ 'message' => 'Expected span container is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ 'type_checkbox_with_selected' => [
+ [
+ Option::KEY_TITLE => 'Test option type checkbox 2',
+ Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX,
+ Option::KEY_IS_REQUIRE => 0,
+ 'configure_option_value' => 'Checkbox value 1',
+ ],
+ [
+ Value::KEY_TITLE => 'Checkbox value 1',
+ Value::KEY_PRICE => 10,
+ Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED,
+ Value::KEY_SKU => '',
+ ],
+ [
+ 'equals_xpath' => [
+ 'element_type' => [
+ 'xpath' => "//input[@id='options_%s_2' and contains(@class, 'admin__control-checkbox')]",
+ 'message' => 'Expected checkbox type is incorrect or missing!',
+ ],
+ 'selected_value' => [
+ 'xpath' => "//input[@id='options_%s_2' and @checked='checked']",
+ 'message' => 'Expected selected option value is incorrect or missing!',
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function addOptionToProduct(
+ ProductInterface $product,
+ array $optionData,
+ array $optionValueData = []
+ ): ProductInterface {
+ $product = parent::addOptionToProduct($product, $optionData, $optionValueData);
+
+ if (isset($optionData['configure_option_value'])) {
+ $optionValue = $optionData['configure_option_value'];
+ $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]);
+ if (!empty($optionValueData)) {
+ $optionValueObject = $this->findOptionValueByTitle($option, $optionValue);
+ $optionValue = $option->getType() === Option::OPTION_TYPE_CHECKBOX
+ ? [$optionValueObject->getOptionTypeId()]
+ : $optionValueObject->getOptionTypeId();
+ }
+ /** @var DataObject $request */
+ $buyRequest = $this->dataObjectFactory->create();
+ $buyRequest->setData([
+ 'qty' => 1,
+ 'options' => [$option->getId() => $optionValue],
+ ]);
+ $this->helperProduct->prepareProductOptions($product, $buyRequest);
+ }
+
+ return $product;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function baseOptionAsserts(
+ ProductCustomOptionInterface $option,
+ string $optionHtml,
+ array $checkArray
+ ): void {
+ if (isset($checkArray['contains'])) {
+ foreach ($checkArray['contains'] as $needle) {
+ $this->assertStringContainsString($needle, $optionHtml);
+ }
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function additionalTypeTextAsserts(
+ ProductCustomOptionInterface $option,
+ string $optionHtml,
+ array $checkArray
+ ): void {
+ parent::additionalTypeTextAsserts($option, $optionHtml, $checkArray);
+
+ if (isset($checkArray['equals_xpath'])) {
+ foreach ($checkArray['equals_xpath'] as $key => $value) {
+ $value['args'] = $key === 'control_price_attribute' ? [(float)$option->getPrice()] : [];
+ $this->assertEqualsXpath($option, $optionHtml, $value);
+ }
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function additionalTypeSelectAsserts(
+ ProductCustomOptionInterface $option,
+ string $optionHtml,
+ array $checkArray
+ ): void {
+ parent::additionalTypeSelectAsserts($option, $optionHtml, $checkArray);
+
+ if (isset($checkArray['equals_xpath'])) {
+ foreach ($checkArray['equals_xpath'] as $value) {
+ $this->assertEqualsXpath($option, $optionHtml, $value);
+ }
+ }
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getHandlesList(): array
+ {
+ return [
+ 'default',
+ 'CATALOG_PRODUCT_COMPOSITE_CONFIGURE',
+ 'catalog_product_view_type_simple',
+ ];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getMaxCharactersCssClass(): string
+ {
+ return 'class="note"';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getOptionsBlockName(): string
+ {
+ return 'product.composite.fieldset.options';
+ }
+
+ /**
+ * Checks that the xpath string is equal to the expected value
+ *
+ * @param ProductCustomOptionInterface $option
+ * @param string $html
+ * @param array $xpathData
+ * @return void
+ */
+ private function assertEqualsXpath(ProductCustomOptionInterface $option, string $html, array $xpathData): void
+ {
+ $args = array_merge([$option->getOptionId()], $xpathData['args'] ?? []);
+ $expected = $xpathData['expected'] ?? 1;
+ $this->assertEquals(
+ $expected,
+ Xpath::getElementsCountForXpath(sprintf($xpathData['xpath'], ...$args), $html),
+ $xpathData['message']
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php
new file mode 100644
index 0000000000000..a51b51a73645f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php
@@ -0,0 +1,138 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Qty::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->helperProduct = $this->objectManager->get(HelperProduct::class);
+ $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('current_product');
+ $this->registry->unregister('product');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @return void
+ */
+ public function testGetProduct(): void
+ {
+ $product = $this->productRepository->get('simple-1');
+ $this->registerProduct($product);
+ $this->assertEquals(
+ $product->getId(),
+ $this->block->getProduct()->getId(),
+ 'The expected product is missing in the Qty block!'
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @dataProvider getQtyValueProvider
+ * @param bool $isQty
+ * @param int $qty
+ * @return void
+ */
+ public function testGetQtyValue(bool $isQty = false, int $qty = 1): void
+ {
+ $product = $this->productRepository->get('simple-1');
+ if ($isQty) {
+ /** @var DataObject $request */
+ $buyRequest = $this->dataObjectFactory->create();
+ $buyRequest->setData(['qty' => $qty]);
+ $this->helperProduct->prepareProductOptions($product, $buyRequest);
+ }
+ $this->registerProduct($product);
+ $this->assertEquals($qty, $this->block->getQtyValue(), 'Expected block qty value is incorrect!');
+ }
+
+ /**
+ * Provides test data to verify block qty value.
+ *
+ * @return array
+ */
+ public function getQtyValueProvider(): array
+ {
+ return [
+ 'with_qty' => [
+ 'is_qty' => true,
+ 'qty' => 5,
+ ],
+ 'without_qty' => [],
+ ];
+ }
+
+ /**
+ * Register the product
+ *
+ * @param ProductInterface $product
+ * @return void
+ */
+ private function registerProduct(ProductInterface $product): void
+ {
+ $this->registry->unregister('current_product');
+ $this->registry->unregister('product');
+ $this->registry->register('current_product', $product);
+ $this->registry->register('product', $product);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php
new file mode 100644
index 0000000000000..ab09314e18cc8
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php
@@ -0,0 +1,121 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('current_product');
+ $this->registry->unregister('product');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_with_options.php
+ * @return void
+ */
+ public function testRenderHtml(): void
+ {
+ $product = $this->productRepository->get('simple');
+ $this->registerProduct($product);
+ $this->preparePage();
+ $fieldsetBlock = $this->page->getLayout()->getBlock('product.composite.fieldset');
+ $this->assertNotFalse($fieldsetBlock, 'Expected fieldset block is missing!');
+ $html = $fieldsetBlock->toHtml();
+
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($this->fieldsetXpath, 'options'), $html),
+ 'Expected options block is missing!'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($this->fieldsetXpath, 'qty'), $html),
+ 'Expected qty block is missing!'
+ );
+ }
+
+ /**
+ * Prepare page layout
+ *
+ * @return void
+ */
+ private function preparePage(): void
+ {
+ $this->page->addHandle([
+ 'default',
+ 'CATALOG_PRODUCT_COMPOSITE_CONFIGURE',
+ 'catalog_product_view_type_simple',
+ ]);
+ $this->page->getLayout()->generateXml();
+ }
+
+ /**
+ * Register the product
+ *
+ * @param ProductInterface $product
+ * @return void
+ */
+ private function registerProduct(ProductInterface $product): void
+ {
+ $this->registry->unregister('current_product');
+ $this->registry->unregister('product');
+ $this->registry->register('current_product', $product);
+ $this->registry->register('product', $product);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php
index eb34696c70dbf..b575fc5e7033c 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php
@@ -9,14 +9,14 @@
use Magento\Catalog\Api\Data\ProductCustomOptionInterface;
use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory;
+use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterface;
use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Block\Product\View\Options;
use Magento\Catalog\Model\Product\Option;
-use Magento\Catalog\Model\Product\Option\Value;
-use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Result\Page;
+use Magento\Framework\View\Result\PageFactory;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\ObjectManager;
use PHPUnit\Framework\TestCase;
@@ -29,12 +29,12 @@ abstract class AbstractRenderCustomOptionsTest extends TestCase
/**
* @var ObjectManager
*/
- private $objectManager;
+ protected $objectManager;
/**
* @var ProductRepositoryInterface
*/
- private $productRepository;
+ protected $productRepository;
/**
* @var ProductCustomOptionInterfaceFactory
@@ -57,12 +57,13 @@ abstract class AbstractRenderCustomOptionsTest extends TestCase
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
- $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
$this->productCustomOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class);
$this->productCustomOptionValuesFactory = $this->objectManager->get(
ProductCustomOptionValuesInterfaceFactory::class
);
- $this->page = $this->objectManager->create(Page::class);
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
parent::setUp();
}
@@ -94,11 +95,26 @@ protected function assertTextOptionRenderingOnProduct(
$option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]);
$optionHtml = $this->getOptionHtml($product);
$this->baseOptionAsserts($option, $optionHtml, $checkArray);
+ $this->additionalTypeTextAsserts($option, $optionHtml, $checkArray);
+ }
- if ($optionData[Option::KEY_MAX_CHARACTERS] > 0) {
+ /**
+ * Additional asserts for rendering text type options.
+ *
+ * @param ProductCustomOptionInterface $option
+ * @param string $optionHtml
+ * @param array $checkArray
+ * @return void
+ */
+ protected function additionalTypeTextAsserts(
+ ProductCustomOptionInterface $option,
+ string $optionHtml,
+ array $checkArray
+ ): void {
+ if ($option->getMaxCharacters() > 0) {
$this->assertStringContainsString($checkArray['max_characters'], $optionHtml);
} else {
- $this->assertStringNotContainsString('class="character-counter', $optionHtml);
+ $this->assertStringNotContainsString($this->getMaxCharactersCssClass(), $optionHtml);
}
}
@@ -153,22 +169,36 @@ protected function assertSelectOptionRenderingOnProduct(
$product = $this->productRepository->get($productSku);
$product = $this->addOptionToProduct($product, $optionData, $optionValueData);
$option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]);
- $optionValues = $option->getValues();
- $optionValue = reset($optionValues);
$optionHtml = $this->getOptionHtml($product);
$this->baseOptionAsserts($option, $optionHtml, $checkArray);
+ $this->additionalTypeSelectAsserts($option, $optionHtml, $checkArray);
+ }
+ /**
+ * Additional asserts for rendering select type options.
+ *
+ * @param ProductCustomOptionInterface $option
+ * @param string $optionHtml
+ * @param array $checkArray
+ * @return void
+ */
+ protected function additionalTypeSelectAsserts(
+ ProductCustomOptionInterface $option,
+ string $optionHtml,
+ array $checkArray
+ ): void {
+ $optionValues = $option->getValues();
+ $optionValue = reset($optionValues);
if (isset($checkArray['not_contain_arr'])) {
foreach ($checkArray['not_contain_arr'] as $notContainPattern) {
$this->assertDoesNotMatchRegularExpression($notContainPattern, $optionHtml);
}
}
-
if (isset($checkArray['option_value_item'])) {
$checkArray['option_value_item'] = sprintf(
$checkArray['option_value_item'],
$optionValue->getOptionTypeId(),
- $optionValueData[Value::KEY_TITLE]
+ $optionValue->getTitle()
);
$this->assertMatchesRegularExpression($checkArray['option_value_item'], $optionHtml);
}
@@ -284,7 +314,7 @@ protected function assertDateOptionRenderingOnProduct(
* @param array $checkArray
* @return void
*/
- private function baseOptionAsserts(
+ protected function baseOptionAsserts(
ProductCustomOptionInterface $option,
string $optionHtml,
array $checkArray
@@ -317,7 +347,7 @@ private function baseOptionAsserts(
* @param array $optionValueData
* @return ProductInterface
*/
- private function addOptionToProduct(
+ protected function addOptionToProduct(
ProductInterface $product,
array $optionData,
array $optionValueData = []
@@ -341,28 +371,16 @@ private function addOptionToProduct(
* @param ProductInterface $product
* @return string
*/
- private function getOptionHtml(ProductInterface $product): string
- {
- $optionsBlock = $this->getOptionsBlock();
- $optionsBlock->setProduct($product);
-
- return $optionsBlock->toHtml();
- }
-
- /**
- * Get options block.
- *
- * @return Options
- */
- private function getOptionsBlock(): Options
+ protected function getOptionHtml(ProductInterface $product): string
{
$this->page->addHandle($this->getHandlesList());
$this->page->getLayout()->generateXml();
- /** @var Template $productInfoFormOptionsBlock */
- $productInfoFormOptionsBlock = $this->page->getLayout()->getBlock('product.info.form.options');
- $optionsWrapperBlock = $productInfoFormOptionsBlock->getChildBlock('product_options_wrapper');
+ /** @var Options $optionsBlock */
+ $optionsBlock = $this->page->getLayout()->getBlock($this->getOptionsBlockName());
+ $this->assertNotFalse($optionsBlock);
+ $optionsBlock->setProduct($product);
- return $optionsWrapperBlock->getChildBlock('product_options');
+ return $optionsBlock->toHtml();
}
/**
@@ -372,7 +390,7 @@ private function getOptionsBlock(): Options
* @param string $optionTitle
* @return null|Option
*/
- private function findOptionByTitle(ProductInterface $product, string $optionTitle): ?Option
+ protected function findOptionByTitle(ProductInterface $product, string $optionTitle): ?Option
{
$option = null;
foreach ($product->getOptions() as $customOption) {
@@ -385,10 +403,42 @@ private function findOptionByTitle(ProductInterface $product, string $optionTitl
return $option;
}
+ /**
+ * Find and return custom option value.
+ *
+ * @param ProductCustomOptionInterface $option
+ * @param string $optionValueTitle
+ * @return null|ProductCustomOptionValuesInterface
+ */
+ protected function findOptionValueByTitle(
+ ProductCustomOptionInterface $option,
+ string $optionValueTitle
+ ): ?ProductCustomOptionValuesInterface {
+ $optionValue = null;
+ foreach ($option->getValues() as $customOptionValue) {
+ if ($customOptionValue->getTitle() === $optionValueTitle) {
+ $optionValue = $customOptionValue;
+ break;
+ }
+ }
+
+ return $optionValue;
+ }
+
/**
* Return all need handles for load.
*
* @return array
*/
abstract protected function getHandlesList(): array;
+
+ /**
+ * @return string
+ */
+ abstract protected function getMaxCharactersCssClass(): string;
+
+ /**
+ * @return string
+ */
+ abstract protected function getOptionsBlockName(): string;
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php
index da31cfc74476a..83c249ed062e6 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php
@@ -10,7 +10,6 @@
/**
* Test cases related to check that simple product custom option renders as expected.
*
- * @magentoDbIsolation disabled
* @magentoAppArea frontend
*/
class RenderOptionsTest extends AbstractRenderCustomOptionsTest
@@ -89,4 +88,20 @@ protected function getHandlesList(): array
'catalog_product_view',
];
}
+
+ /**
+ * @inheritdoc
+ */
+ protected function getMaxCharactersCssClass(): string
+ {
+ return 'class="character-counter';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getOptionsBlockName(): string
+ {
+ return 'product.info.options';
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTest.php
index 0d2f9d63c5d7f..8c25a82e0f6fd 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTest.php
@@ -15,6 +15,8 @@
use Magento\Catalog\Model\ResourceModel\Category\Tree;
use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;
use Magento\Eav\Model\Entity\Attribute\Exception as AttributeException;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Math\Random;
use Magento\Framework\Url;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Store\Model\Store;
@@ -419,6 +421,29 @@ public function testCategoryCreateWithDifferentFields(array $data): void
$this->assertSame($data, $categoryData);
}
+ /**
+ * Test for Category Description field to be able to contain >64kb of data
+ *
+ * @throws NoSuchEntityException
+ * @throws \Exception
+ */
+ public function testMaximumDescriptionLength(): void
+ {
+ $random = Bootstrap::getObjectManager()->get(Random::class);
+ $longDescription = $random->getRandomString(70000);
+
+ $requiredData = [
+ 'name' => 'Test Category',
+ 'attribute_set_id' => '3',
+ 'parent_id' => 2,
+ 'description' => $longDescription
+ ];
+ $this->_model->setData($requiredData);
+ $this->categoryResource->save($this->_model);
+ $category = $this->categoryRepository->get($this->_model->getId());
+ $this->assertEquals($longDescription, $category->getDescription());
+ }
+
/**
* @return array
*/
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_base_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_base_rollback.php
index 67bdb3d5c5e59..4515297fd1e8a 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_base_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/Price/_files/products_base_rollback.php
@@ -44,15 +44,6 @@
$lastProductId = 0;
foreach ($testCases as $index => $testCase) {
- $category = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Catalog\Model\Category::class
- );
- $position = $index + 1;
- $categoryId = $index + 4;
- $category->load($categoryId);
- if ($category->getId()) {
- $category->delete();
- }
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
@@ -74,3 +65,11 @@
++$lastProductId;
}
}
+
+/** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
+$collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class);
+$collection
+ ->addAttributeToFilter('level', ['in' => [2, 3, 4]])
+ ->load()
+ ->delete();
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 f9d235493297f..2659f14c07c7a 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
@@ -20,6 +20,7 @@
use Magento\Framework\ObjectManagerInterface;
use Magento\Store\Api\StoreRepositoryInterface;
use Magento\Store\Model\Store;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
/**
@@ -79,6 +80,15 @@ class UpdateHandlerTest extends \PHPUnit\Framework\TestCase
*/
private $mediaAttributeId;
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+ /**
+ * @var int
+ */
+ private $currentStoreId;
+
/**
* @inheritdoc
*/
@@ -93,6 +103,8 @@ protected function setUp(): void
$this->productResource = $this->objectManager->create(ProductResource::class);
$this->mediaAttributeId = (int)$this->productResource->getAttribute('media_gallery')->getAttributeId();
$this->config = $this->objectManager->get(Config::class);
+ $this->storeManager = $this->objectManager->create(StoreManagerInterface::class);
+ $this->currentStoreId = $this->storeManager->getStore()->getId();
$this->mediaDirectory = $this->objectManager->get(Filesystem::class)
->getDirectoryWrite(DirectoryList::MEDIA);
$this->mediaDirectory->writeFile($this->fileName, 'Test');
@@ -274,7 +286,7 @@ public function testExecuteWithImageToDelete(): void
$this->updateHandler->execute($product);
$productImages = $this->galleryResource->loadProductGalleryByAttributeId($product, $this->mediaAttributeId);
$this->assertCount(0, $productImages);
- $this->assertFileNotExists(
+ $this->assertFileDoesNotExist(
$this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image)
);
$defaultImages = $this->productResource->getAttributeRawValue(
@@ -344,6 +356,7 @@ public function testExecuteWithTwoImagesOnStoreView(): void
*/
protected function tearDown(): void
{
+ $this->storeManager->setCurrentStore($this->currentStoreId);
parent::tearDown();
$this->mediaDirectory->getDriver()->deleteFile($this->mediaDirectory->getAbsolutePath($this->fileName));
$this->galleryResource->getConnection()
@@ -377,4 +390,91 @@ private function updateProductGalleryImages(ProductInterface $product, array $im
$product->setData('store_id', Store::DEFAULT_STORE_ID);
$product->setData('media_gallery', ['images' => ['image' => array_merge($image, $imageData)]]);
}
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_with_image.php
+ * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
+ * @magentoDbIsolation disabled
+ * @return void
+ */
+ public function testDeleteWithMultiWebsites(): void
+ {
+ $defaultWebsiteId = (int) $this->storeManager->getWebsite('base')->getId();
+ $secondWebsiteId = (int) $this->storeManager->getWebsite('test')->getId();
+ $defaultStoreId = (int) $this->storeManager->getStore('default')->getId();
+ $secondStoreId = (int) $this->storeManager->getStore('fixture_second_store')->getId();
+ $imageRoles = ['image', 'small_image', 'thumbnail'];
+ $globalScopeId = Store::DEFAULT_STORE_ID;
+ $this->storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
+ $product = $this->getProduct($globalScopeId);
+ // Assert that product has images
+ $this->assertNotEmpty($product->getMediaGalleryEntries());
+ $image = $product->getImage();
+ $path = $this->mediaDirectory->getAbsolutePath($this->config->getBaseMediaPath() . $image);
+ $this->assertFileExists($path);
+ // Assign product to default and second website and save changes
+ $product->setWebsiteIds([$defaultWebsiteId, $secondWebsiteId]);
+ $this->productRepository->save($product);
+ // Assert that product image has roles in global scope only
+ $imageRolesPerStore = $this->getProductStoreImageRoles($product);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['small_image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['thumbnail']);
+ $this->assertArrayNotHasKey($defaultStoreId, $imageRolesPerStore);
+ $this->assertArrayNotHasKey($secondStoreId, $imageRolesPerStore);
+ // Assign roles to product image on second store and save changes
+ $this->storeManager->setCurrentStore($secondStoreId);
+ $product = $this->getProduct($secondStoreId);
+ $product->addData(array_fill_keys($imageRoles, $image));
+ $this->productRepository->save($product);
+ // Assert that roles are assigned to product image for second store
+ $imageRolesPerStore = $this->getProductStoreImageRoles($product);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['small_image']);
+ $this->assertEquals($image, $imageRolesPerStore[$globalScopeId]['thumbnail']);
+ $this->assertArrayNotHasKey($defaultStoreId, $imageRolesPerStore);
+ $this->assertEquals($image, $imageRolesPerStore[$secondStoreId]['image']);
+ $this->assertEquals($image, $imageRolesPerStore[$secondStoreId]['small_image']);
+ $this->assertEquals($image, $imageRolesPerStore[$secondStoreId]['thumbnail']);
+ // Delete existing images and save changes
+ $this->storeManager->setCurrentStore($globalScopeId);
+ $product = $this->getProduct($globalScopeId);
+ $product->setMediaGalleryEntries([]);
+ $this->productRepository->save($product);
+ $product = $this->getProduct($globalScopeId);
+ // Assert that image was not deleted as it has roles in second store
+ $this->assertNotEmpty($product->getMediaGalleryEntries());
+ $this->assertFileExists($path);
+ // Unlink second website, delete existing images and save changes
+ $product->setWebsiteIds([$defaultWebsiteId]);
+ $product->setMediaGalleryEntries([]);
+ $this->productRepository->save($product);
+ $product = $this->getProduct($globalScopeId);
+ // Assert that image was deleted and product has no images
+ $this->assertEmpty($product->getMediaGalleryEntries());
+ $this->assertFileDoesNotExist($path);
+ // Load image roles
+ $imageRolesPerStore = $this->getProductStoreImageRoles($product);
+ // Assert that image roles are reset on global scope and removed on second store
+ // as the product is no longer assigned to second website
+ $this->assertEquals('no_selection', $imageRolesPerStore[$globalScopeId]['image']);
+ $this->assertEquals('no_selection', $imageRolesPerStore[$globalScopeId]['small_image']);
+ $this->assertEquals('no_selection', $imageRolesPerStore[$globalScopeId]['thumbnail']);
+ $this->assertArrayNotHasKey($defaultStoreId, $imageRolesPerStore);
+ $this->assertArrayNotHasKey($secondStoreId, $imageRolesPerStore);
+ }
+
+ /**
+ * @param Product $product
+ * @return array
+ */
+ private function getProductStoreImageRoles(Product $product): array
+ {
+ $imageRolesPerStore = [];
+ $stores = array_keys($this->storeManager->getStores(true));
+ foreach ($this->galleryResource->getProductImages($product, $stores) as $role) {
+ $imageRolesPerStore[$role['store_id']][$role['attribute_code']] = $role['filepath'];
+ }
+ return $imageRolesPerStore;
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
index 8908561702dd0..f1d1352bcb05b 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductRepositoryTest.php
@@ -33,6 +33,12 @@
*/
class ProductRepositoryTest extends TestCase
{
+ private const STUB_STORE_ID = 1;
+ private const STUB_STORE_ID_GLOBAL = 0;
+ private const STUB_PRODUCT_NAME = 'Simple Product';
+ private const STUB_UPDATED_PRODUCT_NAME = 'updated';
+ private const STUB_PRODUCT_SKU = 'simple';
+
/**
* @var ObjectManagerInterface
*/
@@ -273,4 +279,55 @@ private function assertProductNotExist(string $sku): void
));
$this->productRepository->get($sku);
}
+
+ /**
+ * Tests product repository update
+ *
+ * @dataProvider productUpdateDataProvider
+ * @magentoDataFixture Magento/Catalog/_files/product_simple.php
+ * @param int $storeId
+ * @param int $checkStoreId
+ * @param string $expectedNameStore
+ * @param string $expectedNameCheckedStore
+ */
+ public function testProductUpdate(
+ int $storeId,
+ int $checkStoreId,
+ string $expectedNameStore,
+ string $expectedNameCheckedStore
+ ): void {
+ $sku = self::STUB_PRODUCT_SKU;
+
+ $product = $this->productRepository->get($sku, false, $storeId);
+ $product->setName(self::STUB_UPDATED_PRODUCT_NAME);
+ $this->productRepository->save($product);
+ $productNameStoreId = $this->productRepository->get($sku, false, $storeId)->getName();
+ $productNameCheckedStoreId = $this->productRepository->get($sku, false, $checkStoreId)->getName();
+
+ $this->assertEquals($expectedNameStore, $productNameStoreId);
+ $this->assertEquals($expectedNameCheckedStore, $productNameCheckedStoreId);
+ }
+
+ /**
+ * Product update data provider
+ *
+ * @return array
+ */
+ public function productUpdateDataProvider(): array
+ {
+ return [
+ 'Updating for global store' => [
+ self::STUB_STORE_ID_GLOBAL,
+ self::STUB_STORE_ID,
+ self::STUB_UPDATED_PRODUCT_NAME,
+ self::STUB_UPDATED_PRODUCT_NAME,
+ ],
+ 'Updating for store' => [
+ self::STUB_STORE_ID,
+ self::STUB_STORE_ID_GLOBAL,
+ self::STUB_UPDATED_PRODUCT_NAME,
+ self::STUB_PRODUCT_NAME,
+ ],
+ ];
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php
index b56e9e502cce6..b0f36f250991b 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php
@@ -8,14 +8,19 @@
namespace Magento\Catalog\Model;
-use Magento\Eav\Model\Config as EavConfig;
-use Magento\Catalog\Model\Product;
-use Magento\Framework\App\Filesystem\DirectoryList;
-use Magento\TestFramework\ObjectManager;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Model\Product\Attribute\Source\Status;
+use Magento\Catalog\Model\Product\Visibility;
+use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Exception\CouldNotSaveException;
+use Magento\Framework\Exception\InputException;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Exception\StateException;
+use Magento\Framework\Math\Random;
use Magento\Framework\ObjectManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\ObjectManager;
/**
* Tests product model:
@@ -119,14 +124,62 @@ public function testCRUD()
)->setMetaDescription(
'meta description'
)->setVisibility(
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH
+ Visibility::VISIBILITY_BOTH
)->setStatus(
- \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
+ Status::STATUS_ENABLED
);
$crud = new \Magento\TestFramework\Entity($this->_model, ['sku' => uniqid()]);
$crud->testCrud();
}
+ /**
+ * Test for Product Description field to be able to contain >64kb of data
+ *
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ * @magentoAppArea adminhtml
+ * @throws NoSuchEntityException
+ * @throws CouldNotSaveException
+ * @throws InputException
+ * @throws StateException
+ * @throws LocalizedException
+ */
+ public function testMaximumDescriptionLength()
+ {
+ $sku = uniqid();
+ $random = Bootstrap::getObjectManager()->get(Random::class);
+ $longDescription = $random->getRandomString(70000);
+
+ $this->_model->setTypeId(
+ 'simple'
+ )->setAttributeSetId(
+ 4
+ )->setName(
+ 'Simple Product With Long Description'
+ )->setDescription(
+ $longDescription
+ )->setSku(
+ $sku
+ )->setPrice(
+ 10
+ )->setMetaTitle(
+ 'meta title'
+ )->setMetaKeyword(
+ 'meta keyword'
+ )->setMetaDescription(
+ 'meta description'
+ )->setVisibility(
+ Visibility::VISIBILITY_BOTH
+ )->setStatus(
+ Status::STATUS_ENABLED
+ );
+
+ $this->productRepository->save($this->_model);
+ $product = $this->productRepository->get($sku);
+
+ $this->assertEquals($longDescription, $product->getDescription());
+ }
+
/**
* Test clean cache
*
@@ -219,7 +272,7 @@ public function testDuplicate()
$this->assertNotEquals($duplicate->getId(), $this->_model->getId());
$this->assertNotEquals($duplicate->getSku(), $this->_model->getSku());
$this->assertEquals(
- \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED,
+ Status::STATUS_DISABLED,
$duplicate->getStatus()
);
$this->assertEquals(\Magento\Store\Model\Store::DEFAULT_STORE_ID, $duplicate->getStoreId());
@@ -275,35 +328,35 @@ protected function _undo($duplicate)
public function testVisibilityApi()
{
$this->assertEquals(
- [\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED],
+ [Status::STATUS_ENABLED],
$this->_model->getVisibleInCatalogStatuses()
);
$this->assertEquals(
- [\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED],
+ [Status::STATUS_ENABLED],
$this->_model->getVisibleStatuses()
);
- $this->_model->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED);
+ $this->_model->setStatus(Status::STATUS_DISABLED);
$this->assertFalse($this->_model->isVisibleInCatalog());
- $this->_model->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED);
+ $this->_model->setStatus(Status::STATUS_ENABLED);
$this->assertTrue($this->_model->isVisibleInCatalog());
$this->assertEquals(
[
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_SEARCH,
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG,
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH,
+ Visibility::VISIBILITY_IN_SEARCH,
+ Visibility::VISIBILITY_IN_CATALOG,
+ Visibility::VISIBILITY_BOTH,
],
$this->_model->getVisibleInSiteVisibilities()
);
$this->assertFalse($this->_model->isVisibleInSiteVisibility());
- $this->_model->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_SEARCH);
+ $this->_model->setVisibility(Visibility::VISIBILITY_IN_SEARCH);
$this->assertTrue($this->_model->isVisibleInSiteVisibility());
- $this->_model->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG);
+ $this->_model->setVisibility(Visibility::VISIBILITY_IN_CATALOG);
$this->assertTrue($this->_model->isVisibleInSiteVisibility());
- $this->_model->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH);
+ $this->_model->setVisibility(Visibility::VISIBILITY_BOTH);
$this->assertTrue($this->_model->isVisibleInSiteVisibility());
}
@@ -509,9 +562,9 @@ public function testValidate()
)->setMetaDescription(
'meta description'
)->setVisibility(
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH
+ Visibility::VISIBILITY_BOTH
)->setStatus(
- \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
+ Status::STATUS_ENABLED
)->setCollectExceptionMessages(
true
);
@@ -551,9 +604,9 @@ public function testValidateUniqueInputAttributeValue()
$attribute->getAttributeCode(),
'unique value'
)->setVisibility(
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH
+ Visibility::VISIBILITY_BOTH
)->setStatus(
- \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
+ Status::STATUS_ENABLED
)->setCollectExceptionMessages(
true
);
@@ -600,9 +653,9 @@ public function testValidateUniqueInputAttributeOnTheSameProduct()
$attribute->getAttributeCode(),
'unique value'
)->setVisibility(
- \Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH
+ Visibility::VISIBILITY_BOTH
)->setStatus(
- \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED
+ Status::STATUS_ENABLED
)->setCollectExceptionMessages(
true
);
@@ -675,10 +728,10 @@ public function testSaveWithBackordersEnabled(int $qty, int $stockStatus, bool $
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
*
* @return void
- * @throws \Magento\Framework\Exception\CouldNotSaveException
- * @throws \Magento\Framework\Exception\InputException
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- * @throws \Magento\Framework\Exception\StateException
+ * @throws CouldNotSaveException
+ * @throws InputException
+ * @throws NoSuchEntityException
+ * @throws StateException
*/
public function testProductStatusWhenCatalogFlatProductIsEnabled()
{
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php
new file mode 100644
index 0000000000000..68d5c43434daa
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php
@@ -0,0 +1,40 @@
+create(\Magento\Catalog\Model\Product::class);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setAttributeSetId(4)
+ ->setName('Simple Related Product')
+ ->setSku('simple')
+ ->setPrice(10)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED)
+ ->setWebsiteIds([1])
+ ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1])
+ ->save();
+
+/** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */
+$productLink = $objectManager->create(\Magento\Catalog\Api\Data\ProductLinkInterface::class);
+$productLink->setSku('simple_with_cross');
+$productLink->setLinkedProductSku('simple');
+$productLink->setPosition(1);
+$productLink->setLinkType('related');
+
+$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
+$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
+ ->setAttributeSetId(4)
+ ->setName('Simple Product With Related Product')
+ ->setSku('simple_with_cross')
+ ->setPrice(10)
+ ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
+ ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
+ ->setWebsiteIds([1])
+ ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1])
+ ->setProductLinks([$productLink])
+ ->save();
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php
new file mode 100644
index 0000000000000..958398660b132
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php
@@ -0,0 +1,33 @@
+get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+
+try {
+ $firstProduct = $productRepository->get('simple', false, null, true);
+ $productRepository->delete($firstProduct);
+} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
+ //Product already removed
+}
+
+try {
+ $secondProduct = $productRepository->get('simple_with_cross', false, null, true);
+ $productRepository->delete($secondProduct);
+} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
+ //Product already removed
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_product_simple_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_product_simple_rollback.php
index b045bfe4f6977..09e13c381aaed 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/second_product_simple_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/second_product_simple_rollback.php
@@ -9,6 +9,7 @@
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Registry;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\UrlRewrite\Model\UrlRewrite;
$objectManager = Bootstrap::getObjectManager();
/** @var Registry $registry */
@@ -26,5 +27,9 @@
//Product already removed
}
+$urlRewrite = $objectManager->create(UrlRewrite::class);
+$urlRewrite->load('simple2.html', 'request_path');
+$urlRewrite->delete();
+
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/url_rewrites_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/url_rewrites_rollback.php
index 1fa44427b3fbe..f671c43004ffa 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/url_rewrites_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/url_rewrites_rollback.php
@@ -25,7 +25,7 @@
$collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class);
$collection
- ->addAttributeToFilter('level', 2)
+ ->addAttributeToFilter('name', ['in' => ['Old Root', 'Category 2', 'Category 1']])
->load()
->delete();
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 4502501da4f4f..a9699ea4a8050 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php
@@ -1815,6 +1815,9 @@ public function testExistingProductWithUrlKeys()
'simple2' => 'url-key2',
'simple3' => 'url-key3'
];
+ // added by _files/products_to_import_with_valid_url_keys.csv
+ $this->importedProducts[] = 'simple3';
+
$filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->create(\Magento\Framework\Filesystem::class);
$directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
@@ -1855,6 +1858,9 @@ public function testAddUpdateProductWithInvalidUrlKeys() : void
'simple2' => 'normal-url',
'simple3' => 'some!wrong\'url'
];
+ // added by _files/products_to_import_with_invalid_url_keys.csv
+ $this->importedProducts[] = 'simple3';
+
$filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->create(\Magento\Framework\Filesystem::class);
$directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
@@ -2004,6 +2010,9 @@ public function testImportWithoutUrlKeys()
'simple2' => 'simple-2',
'simple3' => 'simple-3'
];
+ // added by _files/products_to_import_without_url_keys.csv
+ $this->importedProducts[] = 'simple3';
+
$filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class);
$directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT);
$source = $this->objectManager->create(
@@ -2221,11 +2230,15 @@ function (ProductInterface $item) {
$registry->register('isSecureArea', true);
$productSkuList = ['simple1', 'simple2', 'simple3'];
+ $categoryIds = [];
foreach ($productSkuList as $sku) {
try {
+ /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+ /** @var \Magento\Catalog\Model\Product $product */
$product = $productRepository->get($sku, true);
+ $categoryIds[] = $product->getCategoryIds();
if ($product->getId()) {
$productRepository->delete($product);
}
@@ -2235,6 +2248,14 @@ function (ProductInterface $item) {
}
}
+ /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
+ $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+ ->create(\Magento\Catalog\Model\ResourceModel\Category\Collection::class);
+ $collection
+ ->addAttributeToFilter('entity_id', ['in' => \array_unique(\array_merge(...$categoryIds))])
+ ->load()
+ ->delete();
+
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
}
@@ -3169,6 +3190,12 @@ public function testEmptyAttributeValueShouldBeIgnoredAfterUpdateProductByImport
*/
public function testCheckDoubleImportOfProducts()
{
+ $this->importedProducts = [
+ 'simple1',
+ 'simple2',
+ 'simple3',
+ ];
+
/** @var SearchCriteria $searchCriteria */
$searchCriteria = $this->searchCriteriaBuilder->create();
diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php
index 775d405654fdf..3622cecb7143d 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score_rollback.php
@@ -18,7 +18,16 @@
$productRepository = $objectManager->create(ProductRepositoryInterface::class);
/** @var Registry $registry */
$registry = $objectManager->get(Registry::class);
-$productSkus = ['1234-1234-1234-1234', 'Simple', 'product_with_description', 'product_with_attribute'];
+$productSkus = [
+ '1234-1234-1234-1234',
+ 'Simple',
+ 'product_with_description',
+ 'product_with_attribute',
+ 'nintendo-wii',
+ 'xbox',
+ 'console_description',
+ 'gamecube_attribute',
+];
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', true);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php
index 78b4f5ec238df..25fe62b91c6da 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key_rollback.php
@@ -5,18 +5,6 @@
*/
declare(strict_types=1);
-use Magento\Framework\Registry;
-use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
-$objectManager = Bootstrap::getObjectManager();
-
-/** @var Registry $registry */
-$registry = $objectManager->get(Registry::class);
-$registry->unregister('isSecureArea');
-$registry->register('isSecureArea', true);
-
Resolver::getInstance()->requireDataFixture('Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php');
-
-$registry->unregister('isSecureArea');
-$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php
index 6be7354911654..fab5b173625d3 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php
@@ -46,6 +46,8 @@
$urlRewrite = $objectManager->create(UrlRewrite::class);
$urlRewrite->load('non-exist-product.html', 'request_path');
$urlRewrite->delete();
+$urlRewrite->load('.html', 'request_path');
+$urlRewrite->delete();
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php
index 86f0ce34af00c..6b7d4072ead9a 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_stores_rollback.php
@@ -6,9 +6,12 @@
declare(strict_types=1);
use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
\Magento\TestFramework\Helper\Bootstrap::getInstance()->getInstance()->reinitialize();
+Resolver::getInstance()->requireDataFixture('Magento/CatalogUrlRewrite/_files/categories_with_stores_rollback.php');
+
/** @var \Magento\Framework\Registry $registry */
$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
index 32968572b4ac8..44d900a8f2aca 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/SessionTest.php
@@ -199,6 +199,11 @@ public function testLoadCustomerQuoteCustomerWithoutQuote(): void
$this->quote->getCustomerEmail(),
'Precondition failed: Customer data must not be set to quote'
);
+ self::assertEquals(
+ '1',
+ $this->quote->getCustomerIsGuest(),
+ 'Precondition failed: Customer must be as guest in quote'
+ );
$customer = $this->customerRepository->getById(1);
$this->customerSession->setCustomerDataObject($customer);
$this->quote = $this->checkoutSession->getQuote();
@@ -244,6 +249,17 @@ public function testGetQuoteWithProductWithTierPrice(): void
$this->assertEquals($tierPriceValue, $quoteProduct->getTierPrice(1));
}
+ /**
+ * Test covers case when quote is not yet initialized and customer is guest
+ *
+ * Expected result - quote object should be loaded with customer as guest
+ */
+ public function testGetQuoteNotInitializedGuest()
+ {
+ $quote = $this->checkoutSession->getQuote();
+ self::assertEquals('1', $quote->getCustomerIsGuest());
+ }
+
/**
* @magentoDataFixture Magento/Checkout/_files/quote_with_simple_product_saved.php
* @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
@@ -288,5 +304,10 @@ private function validateCustomerDataInQuote(CartInterface $quote): void
$quote->getCustomerFirstname(),
'Customer first name was not set to Quote correctly.'
);
+ self::assertEquals(
+ '0',
+ $quote->getCustomerIsGuest(),
+ 'Customer should not be as guest in Quote.'
+ );
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php
new file mode 100644
index 0000000000000..5cca93ce3478c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php
@@ -0,0 +1,55 @@
+requireDataFixture('Magento/Customer/_files/customer.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_address.php');
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_duplicated.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var CartRepositoryInterface $quoteRepository */
+$quoteRepository = $objectManager->get(CartRepositoryInterface::class);
+/** @var AddressInterface $quoteShippingAddress */
+$quoteShippingAddress = $objectManager->get(AddressInterfaceFactory::class)->create();
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = $objectManager->get(CustomerRepositoryInterface::class);
+/** @var AddressRepositoryInterface $addressRepository */
+$addressRepository = $objectManager->get(AddressRepositoryInterface::class);
+$quoteShippingAddress->importCustomerAddressData($addressRepository->getById(1));
+$customer = $customerRepository->getById(1);
+
+/** @var CartInterface $quote */
+$quote = $objectManager->get(CartInterfaceFactory::class)->create();
+$quote->setStoreId(1)
+ ->setIsActive(true)
+ ->setIsMultiShipping(0)
+ ->assignCustomerWithAddressChange($customer)
+ ->setShippingAddress($quoteShippingAddress)
+ ->setBillingAddress($quoteShippingAddress)
+ ->setCheckoutMethod(Onepage::METHOD_CUSTOMER)
+ ->setReservedOrderId('55555555')
+ ->setEmail($customer->getEmail());
+$quote->addProduct($productRepository->get('simple-1'), 55);
+$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate');
+$quote->getShippingAddress()->setCollectShippingRates(true);
+$quote->getShippingAddress()->collectShippingRates();
+$quote->getPayment()->setMethod('checkmo');
+$quoteRepository->save($quote);
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php
new file mode 100644
index 0000000000000..a599d008cf89c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php
@@ -0,0 +1,26 @@
+get(CartRepositoryInterface::class);
+/** @var GetQuoteByReservedOrderId $getQuoteByReservedOrderId */
+$getQuoteByReservedOrderId = $objectManager->get(GetQuoteByReservedOrderId::class);
+$quote = $getQuoteByReservedOrderId->execute('55555555');
+if ($quote) {
+ $quoteRepository->delete($quote);
+}
+
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_duplicated_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_duplicated_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_address_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php
new file mode 100644
index 0000000000000..c74e76f74115f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php
@@ -0,0 +1,39 @@
+requireDataFixture('Magento/Customer/_files/customer.php');
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/taxable_simple_product.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+/** @var CartRepositoryInterface $quoteRepository */
+$quoteRepository = $objectManager->get(CartRepositoryInterface::class);
+/** @var CustomerRepositoryInterface $customerRepository */
+$customerRepository = $objectManager->get(CustomerRepositoryInterface::class);
+$customer = $customerRepository->get('customer@example.com');
+
+/** @var CartInterface $quote */
+$quote = $objectManager->get(CartInterfaceFactory::class)->create();
+$quote->setStoreId(1)
+ ->setIsActive(false)
+ ->setIsMultiShipping(0)
+ ->setCustomer($customer)
+ ->setCheckoutMethod(Onepage::METHOD_CUSTOMER)
+ ->setReservedOrderId('test_order_with_customer_inactive_quote')
+ ->addProduct($productRepository->get('taxable_product'), 1);
+$quoteRepository->save($quote);
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php
new file mode 100644
index 0000000000000..d45cbb547d29d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php
@@ -0,0 +1,24 @@
+get(CartRepositoryInterface::class);
+/** @var GetQuoteByReservedOrderId $getQuoteByReservedOrderId */
+$getQuoteByReservedOrderId = $objectManager->get(GetQuoteByReservedOrderId::class);
+$quote = $getQuoteByReservedOrderId->execute('test_order_with_customer_inactive_quote');
+if ($quote !== null) {
+ $quoteRepository->delete($quote);
+}
+
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/taxable_simple_product_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php
index de1a78c87953c..9f8a620b8d2a5 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Controller/Adminhtml/PageDesignTest.php
@@ -11,18 +11,21 @@
use Magento\Cms\Api\Data\PageInterface;
use Magento\Cms\Api\GetPageByIdentifierInterface;
use Magento\Cms\Model\Page;
-use Magento\Cms\Model\PageFactory;
use Magento\Framework\Acl\Builder;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\Framework\Message\MessageInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\AbstractBackendController;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
+use Magento\UrlRewrite\Model\UrlRewrite;
/**
* Test the saving CMS pages design via admin area interface.
*
* @magentoAppArea adminhtml
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class PageDesignTest extends AbstractBackendController
{
@@ -77,6 +80,7 @@ protected function setUp(): void
$this->aclBuilder = Bootstrap::getObjectManager()->get(Builder::class);
$this->pageRetriever = Bootstrap::getObjectManager()->get(GetPageByIdentifierInterface::class);
$this->scopeConfig = Bootstrap::getObjectManager()->get(ScopeConfigInterface::class);
+ $this->pagesToDelete = [];
}
/**
@@ -86,11 +90,40 @@ protected function tearDown(): void
{
parent::tearDown();
+ $pageIds = [];
foreach ($this->pagesToDelete as $identifier) {
- $page = $this->pageRetriever->execute($identifier);
+ $pageIds[] = $identifier;
+ $page = $this->pageRetriever->execute($identifier, 0);
$page->delete();
}
- $this->pagesToDelete = [];
+ $this->removeUrlRewrites();
+ }
+
+ /**
+ * Removes url rewrites created during test execution.
+ *
+ * @return void
+ */
+ private function removeUrlRewrites(): void
+ {
+ if (!empty($this->pagesToDelete)) {
+ /** @var UrlRewriteCollectionFactory $urlRewriteCollectionFactory */
+ $urlRewriteCollectionFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ UrlRewriteCollectionFactory::class
+ );
+ /** @var UrlRewriteCollection $urlRewriteCollection */
+ $urlRewriteCollection = $urlRewriteCollectionFactory->create();
+ $urlRewriteCollection->addFieldToFilter('request_path', ['in' => $this->pagesToDelete]);
+ $urlRewrites = $urlRewriteCollection->getItems();
+ /** @var UrlRewrite $urlRewrite */
+ foreach ($urlRewrites as $urlRewrite) {
+ try {
+ $urlRewrite->delete();
+ } catch (\Exception $exception) {
+ // already removed
+ }
+ }
+ }
}
/**
@@ -150,6 +183,7 @@ public function testSaveDesign(): void
self::equalTo($sessionMessages),
MessageInterface::TYPE_ERROR
);
+ $this->pagesToDelete = [$id];
}
/**
@@ -181,6 +215,7 @@ public function testSaveDesignWithDefaults(): void
$this->assertNotEmpty($page->getId());
$this->assertNotNull($page->getPageLayout());
$this->assertEquals($defaultLayout, $page->getPageLayout());
+ $this->pagesToDelete = [$id];
}
/**
@@ -227,5 +262,6 @@ public function testSaveLayoutXml(): void
$updated = $this->pageRetriever->execute('test_custom_layout_page_1', 0);
$this->assertEmpty($updated->getCustomLayoutUpdateXml());
$this->assertEmpty($updated->getLayoutUpdateXml());
+ $this->pagesToDelete = ['test_custom_layout_page_1'];
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/GetInsertImageContentTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/GetInsertImageContentTest.php
new file mode 100644
index 0000000000000..076a669f3f8ad
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/GetInsertImageContentTest.php
@@ -0,0 +1,133 @@
+getInsertImageContent = Bootstrap::getObjectManager()->get(GetInsertImageContent::class);
+ $this->imagesHelper = Bootstrap::getObjectManager()->get(ImagesHelper::class);
+ $this->urlEncoder = Bootstrap::getObjectManager()->get(EncoderInterface::class);
+ $this->url = Bootstrap::getObjectManager()->get(UrlInterface::class);
+ }
+
+ /**
+ * Test for GetInsertImageContent::execute
+ *
+ * @dataProvider imageDataProvider
+ * @param string $filename
+ * @param bool $forceStaticPath
+ * @param bool $renderAsTag
+ * @param int|null $storeId
+ * @param string $expectedResult
+ */
+ public function testExecute(
+ string $filename,
+ bool $forceStaticPath,
+ bool $renderAsTag,
+ ?int $storeId,
+ string $expectedResult
+ ): void {
+ if (!$forceStaticPath && !$renderAsTag && !$this->imagesHelper->isUsingStaticUrlsAllowed()) {
+ $expectedResult = $this->url->getUrl(
+ 'cms/wysiwyg/directive',
+ [
+ '___directive' => $this->urlEncoder->encode($expectedResult),
+ '_escape_params' => false
+ ]
+ );
+ }
+
+ $this->assertEquals(
+ $expectedResult,
+ $this->getInsertImageContent->execute(
+ $this->imagesHelper->idEncode($filename),
+ $forceStaticPath,
+ $renderAsTag,
+ $storeId
+ )
+ );
+ }
+
+ /**
+ * Data provider for testExecute
+ *
+ * @return array[]
+ */
+ public function imageDataProvider(): array
+ {
+ return [
+ [
+ 'test-image.jpg',
+ false,
+ true,
+ 1,
+ ' '
+ ],
+ [
+ 'catalog/category/test-image.jpg',
+ true,
+ false,
+ 1,
+ '/pub/media/catalog/category/test-image.jpg'
+ ],
+ [
+ 'test-image.jpg',
+ false,
+ false,
+ 1,
+ '{{media url="test-image.jpg"}}'
+ ],
+ [
+ '/test-image.jpg',
+ false,
+ true,
+ 2,
+ ' '
+ ],
+ [
+ 'test-image.jpg',
+ false,
+ true,
+ null,
+ ' '
+ ],
+ ];
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
index a68a546c20bc6..cb96ca2a14cac 100644
--- a/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
+++ b/dev/tests/integration/testsuite/Magento/Cms/Model/Wysiwyg/Images/StorageTest.php
@@ -6,7 +6,13 @@
*/
namespace Magento\Cms\Model\Wysiwyg\Images;
+use Magento\Cms\Model\Wysiwyg\Images\Storage\Collection;
use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\DataObject;
+use Magento\Framework\Filesystem;
+use Magento\Framework\Filesystem\Driver\File;
+use Magento\Framework\Filesystem\DriverInterface;
+use Magento\TestFramework\Helper\Bootstrap;
/**
* Test methods of class Storage
@@ -29,22 +35,27 @@ class StorageTest extends \PHPUnit\Framework\TestCase
private $objectManager;
/**
- * @var \Magento\Framework\Filesystem
+ * @var Filesystem
*/
private $filesystem;
/**
- * @var \Magento\Cms\Model\Wysiwyg\Images\Storage
+ * @var Storage
*/
private $storage;
+ /**
+ * @var DriverInterface
+ */
+ private $driver;
+
/**
* @inheritdoc
*/
// phpcs:disable
public static function setUpBeforeClass(): void
{
- self::$_baseDir = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ self::$_baseDir = Bootstrap::getObjectManager()->get(
\Magento\Cms\Helper\Wysiwyg\Images::class
)->getCurrentPath() . 'MagentoCmsModelWysiwygImagesStorageTest';
if (!file_exists(self::$_baseDir)) {
@@ -60,8 +71,8 @@ public static function setUpBeforeClass(): void
// phpcs:ignore
public static function tearDownAfterClass(): void
{
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Framework\Filesystem\Driver\File::class
+ Bootstrap::getObjectManager()->create(
+ File::class
)->deleteDirectory(
self::$_baseDir
);
@@ -72,9 +83,10 @@ public static function tearDownAfterClass(): void
*/
protected function setUp(): void
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $this->filesystem = $this->objectManager->get(\Magento\Framework\Filesystem::class);
- $this->storage = $this->objectManager->create(\Magento\Cms\Model\Wysiwyg\Images\Storage::class);
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->filesystem = $this->objectManager->get(Filesystem::class);
+ $this->storage = $this->objectManager->create(Storage::class);
+ $this->driver = Bootstrap::getObjectManager()->get(DriverInterface::class);
}
/**
@@ -83,16 +95,31 @@ protected function setUp(): void
*/
public function testGetFilesCollection(): void
{
- \Magento\TestFramework\Helper\Bootstrap::getInstance()
+ Bootstrap::getInstance()
->loadArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE);
- $collection = $this->storage->getFilesCollection(self::$_baseDir, 'media');
- $this->assertInstanceOf(\Magento\Cms\Model\Wysiwyg\Images\Storage\Collection::class, $collection);
+ $fileName = 'magento_image.jpg';
+ $imagePath = realpath(__DIR__ . '/../../../../Catalog/_files/' . $fileName);
+ $mediaDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
+ $modifiableFilePath = $mediaDirectory->getAbsolutePath('MagentoCmsModelWysiwygImagesStorageTest/' . $fileName);
+ $this->driver->copy(
+ $imagePath,
+ $modifiableFilePath
+ );
+ $this->storage->resizeFile($modifiableFilePath);
+ $collection = $this->storage->getFilesCollection(self::$_baseDir, 'image');
+ $this->assertInstanceOf(Collection::class, $collection);
foreach ($collection as $item) {
- $this->assertInstanceOf(\Magento\Framework\DataObject::class, $item);
- $this->assertStringEndsWith('/1.swf', $item->getUrl());
- $this->assertStringMatchesFormat(
- 'http://%s/static/%s/adminhtml/%s/%s/Magento_Cms/images/placeholder_thumbnail.jpg',
- $item->getThumbUrl()
+ $this->assertInstanceOf(DataObject::class, $item);
+ $this->assertStringEndsWith('/' . $fileName, $item->getUrl());
+ $this->assertEquals(
+ '/pub/media/.thumbsMagentoCmsModelWysiwygImagesStorageTest/magento_image.jpg',
+ parse_url($item->getThumbUrl(), PHP_URL_PATH),
+ "Check if Thumbnail URL is equal to the generated URL"
+ );
+ $this->assertEquals(
+ 'image/jpeg',
+ $item->getMimeType(),
+ "Check if Mime Type is equal to the image in the file system"
);
return;
}
@@ -121,7 +148,7 @@ public function testDeleteDirectory(): void
$this->storage->createDirectory($dir, $path);
$this->assertFileExists($fullPath);
$this->storage->deleteDirectory($fullPath);
- $this->assertFileNotExists($fullPath);
+ $this->assertFileDoesNotExist($fullPath);
}
/**
@@ -142,7 +169,7 @@ public function testDeleteDirectoryWithExcludedDirPath(): void
public function testUploadFile(): void
{
$fileName = 'magento_small_image.jpg';
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$fixtureDir = realpath(__DIR__ . '/../../../../Catalog/_files');
@@ -172,7 +199,7 @@ public function testUploadFileWithExcludedDirPath(): void
);
$fileName = 'magento_small_image.jpg';
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$fixtureDir = realpath(__DIR__ . '/../../../../Catalog/_files');
@@ -204,7 +231,7 @@ public function testUploadFileWithWrongExtension(string $fileName, string $fileT
$this->expectException(\Magento\Framework\Exception\LocalizedException::class);
$this->expectExceptionMessage('File validation failed.');
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$fixtureDir = realpath(__DIR__ . '/../../../_files');
@@ -251,7 +278,7 @@ public function testUploadFileWithWrongFile(): void
$this->expectExceptionMessage('File validation failed.');
$fileName = 'file.gif';
- $tmpDirectory = $this->filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::SYS_TMP);
+ $tmpDirectory = $this->filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
$filePath = $tmpDirectory->getAbsolutePath($fileName);
// phpcs:disable
$file = fopen($filePath, "wb");
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php
new file mode 100644
index 0000000000000..88c8fb726c472
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php
@@ -0,0 +1,108 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->serializer = $this->objectManager->get(SerializerInterface::class);
+ $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
+ $this->productRepository->cleanCache();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('product');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php
+ * @return void
+ */
+ public function testGetProduct(): void
+ {
+ $product = $this->productRepository->get('simple-1');
+ $this->registerProduct($product);
+ $blockProduct = $this->block->getProduct();
+ $this->assertSame($product, $blockProduct);
+ $this->assertEquals(
+ $product->getId(),
+ $blockProduct->getId(),
+ 'The expected product is missing in the Configurable block!'
+ );
+ $this->assertNotNull($blockProduct->getTypeInstance()->getStoreFilter($blockProduct));
+ }
+
+ /**
+ * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php
+ * @return void
+ */
+ public function testGetJsonConfig(): void
+ {
+ $product = $this->productRepository->get('configurable');
+ $this->registerProduct($product);
+ $config = $this->serializer->unserialize($this->block->getJsonConfig());
+ $this->assertTrue($config['disablePriceReload']);
+ $this->assertTrue($config['stablePrices']);
+ }
+
+ /**
+ * Register the product
+ *
+ * @param ProductInterface $product
+ * @return void
+ */
+ private function registerProduct(ProductInterface $product): void
+ {
+ $this->registry->unregister('product');
+ $this->registry->register('product', $product);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php
index 55f8b91f07093..303a32d34bf6c 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php
@@ -12,7 +12,6 @@
/**
* Test cases related to check that configurable product custom option renders as expected.
*
- * @magentoDbIsolation disabled
* @magentoAppArea frontend
*/
class RenderOptionsTest extends AbstractRenderCustomOptionsTest
@@ -93,4 +92,20 @@ protected function getHandlesList(): array
'catalog_product_view_type_configurable',
];
}
+
+ /**
+ * @inheritdoc
+ */
+ protected function getMaxCharactersCssClass(): string
+ {
+ return 'class="character-counter';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function getOptionsBlockName(): string
+ {
+ return 'product.info.options';
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php
index 39ed7965ea9e9..0344d467a3cc2 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php
@@ -9,9 +9,13 @@
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Catalog\Helper\Product as HelperProduct;
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute as ConfigurableAttribute;
use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection;
use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\DataObject;
+use Magento\Framework\DataObjectFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\View\LayoutInterface;
@@ -26,6 +30,7 @@
* @magentoAppIsolation enabled
* @magentoDbIsolation enabled
* @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ConfigurableTest extends TestCase
{
@@ -64,6 +69,14 @@ class ConfigurableTest extends TestCase
*/
private $product;
+ /**
+ * @var HelperProduct
+ */
+ private $helperProduct;
+
+ /** @var DataObjectFactory */
+ private $dataObjectFactory;
+
/**
* @inheritdoc
*/
@@ -79,6 +92,8 @@ protected function setUp(): void
$this->product = $this->productRepository->get('configurable');
$this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class);
$this->block->setProduct($this->product);
+ $this->helperProduct = $this->objectManager->get(HelperProduct::class);
+ $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class);
}
/**
@@ -128,6 +143,29 @@ public function testGetJsonConfig(): void
$this->assertCount(0, $config['images']);
}
+ /**
+ * @return void
+ */
+ public function testGetJsonConfigWithPreconfiguredValues(): void
+ {
+ /** @var ConfigurableAttribute $attribute */
+ $attribute = $this->product->getExtensionAttributes()->getConfigurableProductOptions()[0];
+ $expectedAttributeValue = [
+ $attribute->getAttributeId() => $attribute->getOptions()[0]['value_index'],
+ ];
+ /** @var DataObject $request */
+ $buyRequest = $this->dataObjectFactory->create();
+ $buyRequest->setData([
+ 'qty' => 1,
+ 'super_attribute' => $expectedAttributeValue,
+ ]);
+ $this->helperProduct->prepareProductOptions($this->product, $buyRequest);
+
+ $config = $this->serializer->unserialize($this->block->getJsonConfig());
+ $this->assertArrayHasKey('defaultValues', $config);
+ $this->assertEquals($expectedAttributeValue, $config['defaultValues']);
+ }
+
/**
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images.php
* @return void
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php
index 3a6052da3964f..cd206ec8ec273 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/MultiStoreConfigurableViewOnProductPageTest.php
@@ -184,7 +184,10 @@ private function prepareConfigurableProduct(string $sku, string $storeCode): voi
{
$product = $this->productRepository->get($sku, false, null, true);
$productToUpdate = $product->getTypeInstance()->getUsedProductCollection($product)
- ->setPageSize(1)->getFirstItem();
+ ->addStoreFilter($storeCode)
+ ->setPageSize(1)
+ ->getFirstItem();
+
$this->assertNotEmpty($productToUpdate->getData(), 'Configurable product does not have a child');
$this->executeInStoreContext->execute($storeCode, [$this, 'setProductDisabled'], $productToUpdate);
}
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php
index 61c2bf7b5fa72..f6e6261c75662 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_products.php
@@ -44,7 +44,6 @@
$product = $objectManager->create(Product::class);
$productId = array_shift($productIds);
$product->setTypeId(Type::TYPE_SIMPLE)
- ->setId($productId)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Option' . $option->getLabel())
@@ -84,7 +83,6 @@
$product->setExtensionAttributes($extensionConfigurableAttributes);
$product->setTypeId(Configurable::TYPE_CODE)
- ->setId(1)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Product')
@@ -110,7 +108,6 @@
$product = $objectManager->create(Product::class);
$productId = array_shift($productIds);
$product->setTypeId(Type::TYPE_SIMPLE)
- ->setId($productId)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Option' . $option->getLabel())
@@ -155,7 +152,6 @@
$product->setExtensionAttributes($extensionConfigurableAttributes);
$product->setTypeId(Configurable::TYPE_CODE)
- ->setId(11)
->setAttributeSetId($attributeSetId)
->setWebsiteIds([1])
->setName('Configurable Product 12345')
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php
index 072c0cd8f9118..1f0dee32ce4a2 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/quote_with_configurable_product_last_variation.php
@@ -14,11 +14,11 @@
$quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
/** @var ProductRepositoryInterface $productRepository */
$productRepository = $objectManager->create(ProductRepositoryInterface::class);
-$product = $productRepository->getById(10);
+$product = $productRepository->get('simple_10');
$product->setStockData(['use_config_manage_stock' => 1, 'qty' => 1, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
$productRepository->save($product);
-$product = $productRepository->getById(20);
+$product = $productRepository->get('simple_20');
$product->setStockData(['use_config_manage_stock' => 1, 'qty' => 0, 'is_qty_decimal' => 0, 'is_in_stock' => 0]);
$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php
index 9c382068ceebc..12585992d084c 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php
@@ -3,126 +3,175 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Customer\Block\Address;
+use Magento\Customer\Model\AddressRegistry;
+use Magento\Customer\Model\CustomerRegistry;
+use Magento\Customer\Model\Session;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\View\Result\Page;
+use Magento\Framework\View\Result\PageFactory;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\Xpath;
+use PHPUnit\Framework\TestCase;
+
/**
* Tests Address Edit Block
+ *
+ * @magentoAppArea frontend
+ * @magentoAppIsolation enabled
*/
-class EditTest extends \PHPUnit\Framework\TestCase
+class EditTest extends TestCase
{
+ /** @var ObjectManagerInterface */
+ private $objectManager;
+
/** @var Edit */
- protected $_block;
+ private $block;
- /** @var \Magento\Customer\Model\Session */
- protected $_customerSession;
+ /** @var Session */
+ private $customerSession;
- /** @var \Magento\Backend\Block\Template\Context */
- protected $_context;
+ /** @var AddressRegistry */
+ private $addressRegistry;
- /** @var string */
- protected $_requestId;
+ /** @var CustomerRegistry */
+ private $customerRegistry;
+ /** @var RequestInterface */
+ private $request;
+
+ /**
+ * @inheritdoc
+ */
protected function setUp(): void
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-
- $this->_customerSession = $objectManager->get(\Magento\Customer\Model\Session::class);
- $this->_customerSession->setCustomerId(1);
-
- $this->_context = $objectManager->get(\Magento\Backend\Block\Template\Context::class);
- $this->_requestId = $this->_context->getRequest()->getParam('id');
- $this->_context->getRequest()->setParam('id', '1');
-
- $objectManager->get(\Magento\Framework\App\State::class)->setAreaCode('frontend');
-
- /** @var $layout \Magento\Framework\View\Layout */
- $layout = $objectManager->get(\Magento\Framework\View\LayoutInterface::class);
- $currentCustomer = $objectManager->create(
- \Magento\Customer\Helper\Session\CurrentCustomer::class,
- ['customerSession' => $this->_customerSession]
- );
- $this->_block = $layout->createBlock(
- \Magento\Customer\Block\Address\Edit::class,
- '',
- ['customerSession' => $this->_customerSession, 'currentCustomer' => $currentCustomer]
- );
+ parent::setUp();
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->customerSession->setCustomerId(1);
+ $this->request = $this->objectManager->get(RequestInterface::class);
+ $this->request->setParam('id', '1');
+ /** @var Page $page */
+ $page = $this->objectManager->get(PageFactory::class)->create();
+ $page->addHandle(['default', 'customer_address_form']);
+ $page->getLayout()->generateXml();
+ $this->block = $page->getLayout()->getBlock('customer_address_edit');
+ $this->addressRegistry = $this->objectManager->get(AddressRegistry::class);
+ $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class);
}
+ /**
+ * @inheritdoc
+ */
protected function tearDown(): void
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $this->_customerSession->setCustomerId(null);
- $this->_context->getRequest()->setParam('id', $this->_requestId);
- /** @var \Magento\Customer\Model\AddressRegistry $addressRegistry */
- $addressRegistry = $objectManager->get(\Magento\Customer\Model\AddressRegistry::class);
+ parent::tearDown();
+ $this->customerSession->setCustomerId(null);
+ $this->request->setParam('id', null);
//Cleanup address from registry
- $addressRegistry->remove(1);
- $addressRegistry->remove(2);
-
- /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */
- $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class);
+ $this->addressRegistry->remove(1);
+ $this->addressRegistry->remove(2);
//Cleanup customer from registry
- $customerRegistry->remove(1);
+ $this->customerRegistry->remove(1);
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
+ * @return void
*/
- public function testGetSaveUrl()
+ public function testGetSaveUrl(): void
{
- $this->assertEquals('http://localhost/index.php/customer/address/formPost/', $this->_block->getSaveUrl());
+ $this->assertEquals('http://localhost/index.php/customer/address/formPost/', $this->block->getSaveUrl());
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/Customer/_files/customer_address.php
+ * @return void
*/
- public function testGetRegionId()
+ public function testGetRegionId(): void
{
- $this->assertEquals(1, $this->_block->getRegionId());
+ $this->assertEquals(1, $this->block->getRegionId());
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/Customer/_files/customer_address.php
+ * @return void
*/
- public function testGetCountryId()
+ public function testGetCountryId(): void
{
- $this->assertEquals('US', $this->_block->getCountryId());
+ $this->assertEquals('US', $this->block->getCountryId());
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php
+ * @return void
*/
- public function testGetCustomerAddressCount()
+ public function testGetCustomerAddressCount(): void
{
- $this->assertEquals(2, $this->_block->getCustomerAddressCount());
+ $this->assertEquals(2, $this->block->getCustomerAddressCount());
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
+ * @return void
*/
- public function testCanSetAsDefaultShipping()
+ public function testCanSetAsDefaultShipping(): void
{
- $this->assertEquals(0, $this->_block->canSetAsDefaultShipping());
+ $this->assertEquals(0, $this->block->canSetAsDefaultShipping());
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
+ * @return void
*/
- public function testIsDefaultBilling()
+ public function testIsDefaultBilling(): void
{
- $this->assertFalse($this->_block->isDefaultBilling());
+ $this->assertFalse($this->block->isDefaultBilling());
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
* @magentoDataFixture Magento/Customer/_files/customer_address.php
+ * @return void
+ */
+ public function testGetStreetLine(): void
+ {
+ $this->assertEquals('Green str, 67', $this->block->getStreetLine(1));
+ $this->assertEquals('', $this->block->getStreetLine(2));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoConfigFixture current_store customer/create_account/vat_frontend_visibility 1
+ * @return void
+ */
+ public function testVatIdFieldVisible(): void
+ {
+ $html = $this->block->toHtml();
+ $labelXpath = "//div[contains(@class, 'taxvat')]//label/span[normalize-space(text()) = '%s']";
+ $this->assertEquals(1, Xpath::getElementsCountForXpath(sprintf($labelXpath, __('VAT Number')), $html));
+ $inputXpath = "//div[contains(@class, 'taxvat')]//div/input[contains(@id,'vat_id') and @type='text']";
+ $this->assertEquals(1, Xpath::getElementsCountForXpath($inputXpath, $html));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoConfigFixture current_store customer/create_account/vat_frontend_visibility 0
+ * @return void
*/
- public function testGetStreetLine()
+ public function testVatIdFieldNotVisible(): void
{
- $this->assertEquals('Green str, 67', $this->_block->getStreetLine(1));
- $this->assertEquals('', $this->_block->getStreetLine(2));
+ $html = $this->block->toHtml();
+ $labelXpath = "//div[contains(@class, 'taxvat')]//label/span[normalize-space(text()) = '%s']";
+ $this->assertEquals(0, Xpath::getElementsCountForXpath(sprintf($labelXpath, __('VAT Number')), $html));
+ $inputXpath = "//div[contains(@class, 'taxvat')]//div/input[contains(@id,'vat_id') and @type='text']";
+ $this->assertEquals(0, Xpath::getElementsCountForXpath($inputXpath, $html));
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php
new file mode 100644
index 0000000000000..8ce1d2ae9ccf9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php
@@ -0,0 +1,303 @@
+transportBuilderMock = $this->_objectManager->get(TransportBuilderMock::class);
+ $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class);
+ $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
+ $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class);
+ $this->cookieManager = $this->_objectManager->get(CookieManagerInterface::class);
+ $this->urlBuilder = $this->_objectManager->get(UrlInterface::class);
+ }
+
+ /**
+ * Tests that without form key user account won't be created
+ * and user will be redirected on account creation page again.
+ *
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ * @return void
+ */
+ public function testNoFormKeyCreatePostAction(): void
+ {
+ $this->fillRequestWithAccountData('test1@email.com');
+ $this->getRequest()->setPostValue('form_key', null);
+ $this->dispatch('customer/account/createPost');
+
+ $this->assertCustomerNotExists('test1@email.com');
+ $this->assertRedirect($this->stringEndsWith('customer/account/create/'));
+ $this->assertSessionMessages(
+ $this->stringContains((string)__('Invalid Form Key. Please refresh the page.')),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ * @magentoConfigFixture current_website customer/create_account/confirm 0
+ * @magentoConfigFixture current_store customer/create_account/default_group 1
+ * @magentoConfigFixture current_store customer/create_account/generate_human_friendly_id 0
+ *
+ * @return void
+ */
+ public function testNoConfirmCreatePostAction(): void
+ {
+ $this->fillRequestWithAccountData('test1@email.com');
+ $this->dispatch('customer/account/createPost');
+ $this->assertRedirect($this->stringEndsWith('customer/account/'));
+ $this->assertSessionMessages(
+ $this->containsEqual(
+ (string)__('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName())
+ ),
+ MessageInterface::TYPE_SUCCESS
+ );
+ $customer = $this->customerRegistry->retrieveByEmail('test1@email.com');
+ //Assert customer group
+ $this->assertEquals(1, $customer->getDataModel()->getGroupId());
+ //Assert customer increment id generation
+ $this->assertNull($customer->getData('increment_id'));
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ * @magentoConfigFixture current_website customer/create_account/confirm 0
+ * @magentoConfigFixture current_store customer/create_account/default_group 2
+ * @magentoConfigFixture current_store customer/create_account/generate_human_friendly_id 1
+ * @return void
+ */
+ public function testCreatePostWithCustomConfiguration(): void
+ {
+ $this->fillRequestWithAccountData('test@email.com');
+ $this->dispatch('customer/account/createPost');
+ $this->assertRedirect($this->stringEndsWith('customer/account/'));
+ $this->assertSessionMessages(
+ $this->containsEqual(
+ (string)__('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName())
+ ),
+ MessageInterface::TYPE_SUCCESS
+ );
+ $customer = $this->customerRegistry->retrieveByEmail('test@email.com');
+ //Assert customer group
+ $this->assertEquals(2, $customer->getDataModel()->getGroupId());
+ //Assert customer increment id generation
+ $this->assertNotNull($customer->getData('increment_id'));
+ $this->assertMatchesRegularExpression('/\d{8}/', $customer->getData('increment_id'));
+ }
+
+ /**
+ * @magentoDbIsolation enabled
+ * @magentoAppIsolation enabled
+ * @magentoConfigFixture current_website customer/create_account/confirm 1
+ *
+ * @return void
+ */
+ public function testWithConfirmCreatePostAction(): void
+ {
+ $email = 'test2@email.com';
+ $this->fillRequestWithAccountData($email);
+ $this->dispatch('customer/account/createPost');
+ $this->assertRedirect($this->stringContains('customer/account/index/'));
+ $message = 'You must confirm your account.'
+ . ' Please check your email for the confirmation link or click here for a new link.';
+ $url = $this->urlBuilder->getUrl('customer/account/confirmation', ['_query' => ['email' => $email]]);
+ $this->assertSessionMessages(
+ $this->containsEqual((string)__($message, $url)),
+ MessageInterface::TYPE_SUCCESS
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testExistingEmailCreatePostAction(): void
+ {
+ $this->fillRequestWithAccountData('customer@example.com');
+ $this->dispatch('customer/account/createPost');
+ $this->assertRedirect($this->stringContains('customer/account/create/'));
+ $message = 'There is already an account with this email address.'
+ . ' If you are sure that it is your email address, click here '
+ . 'to get your password and access your account.';
+ $url = $this->urlBuilder->getUrl('customer/account/forgotpassword');
+ $this->assertSessionMessages($this->containsEqual((string)__($message, $url)), MessageInterface::TYPE_ERROR);
+ }
+
+ /**
+ * Register Customer with email confirmation.
+ *
+ * @magentoAppArea frontend
+ * @magentoConfigFixture current_website customer/create_account/confirm 1
+ *
+ * @return void
+ */
+ public function testRegisterCustomerWithEmailConfirmation(): void
+ {
+ $email = 'test_example@email.com';
+ $this->fillRequestWithAccountData($email);
+ $this->dispatch('customer/account/createPost');
+ $this->assertRedirect($this->stringContains('customer/account/index/'));
+ $message = 'You must confirm your account.'
+ . ' Please check your email for the confirmation link or click here for a new link.';
+ $url = $this->urlBuilder->getUrl('customer/account/confirmation', ['_query' => ['email' => $email]]);
+ $this->assertSessionMessages($this->containsEqual((string)__($message, $url)), MessageInterface::TYPE_SUCCESS);
+ /** @var CustomerInterface $customer */
+ $customer = $this->customerRepository->get($email);
+ $confirmation = $customer->getConfirmation();
+ $sendMessage = $this->transportBuilderMock->getSentMessage();
+ $this->assertNotNull($sendMessage);
+ $rawMessage = $sendMessage->getBody()->getParts()[0]->getRawContent();
+ $this->assertStringContainsString(
+ (string)__(
+ 'You must confirm your %customer_email email before you can sign in (link is only valid once):',
+ ['customer_email' => $email]
+ ),
+ $rawMessage
+ );
+ $this->assertStringContainsString(
+ sprintf('customer/account/confirm/?id=%s&key=%s', $customer->getId(), $confirmation),
+ $rawMessage
+ );
+ $this->resetRequest();
+ $this->getRequest()
+ ->setParam('id', $customer->getId())
+ ->setParam('key', $confirmation);
+ $this->dispatch('customer/account/confirm');
+ $this->assertRedirect($this->stringContains('customer/account/index/'));
+ $this->assertSessionMessages(
+ $this->containsEqual(
+ (string)__('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName())
+ ),
+ MessageInterface::TYPE_SUCCESS
+ );
+ $this->assertEmpty($this->customerRepository->get($email)->getConfirmation());
+ }
+
+ /**
+ * Fills request with customer data.
+ *
+ * @param string $email
+ * @return void
+ */
+ private function fillRequestWithAccountData(string $email): void
+ {
+ $this->getRequest()
+ ->setMethod(HttpRequest::METHOD_POST)
+ ->setParam(CustomerInterface::FIRSTNAME, 'firstname1')
+ ->setParam(CustomerInterface::LASTNAME, 'lastname1')
+ ->setParam(CustomerInterface::EMAIL, $email)
+ ->setParam('password', '_Password1')
+ ->setParam('password_confirmation', '_Password1')
+ ->setParam('telephone', '5123334444')
+ ->setParam('street', ['1234 fake street', ''])
+ ->setParam('city', 'Austin')
+ ->setParam('postcode', '78701')
+ ->setParam('country_id', 'US')
+ ->setParam('default_billing', '1')
+ ->setParam('default_shipping', '1')
+ ->setParam('is_subscribed', '0')
+ ->setPostValue('create_address', true);
+ }
+
+ /**
+ * Asserts that customer does not exists.
+ *
+ * @param string $email
+ * @return void
+ */
+ private function assertCustomerNotExists(string $email): void
+ {
+ $this->expectException(NoSuchEntityException::class);
+ $this->expectExceptionMessage(
+ (string)__(
+ 'No such entity with %fieldName = %fieldValue, %field2Name = %field2Value',
+ [
+ 'fieldName' => 'email',
+ 'fieldValue' => $email,
+ 'field2Name' => 'websiteId',
+ 'field2Value' => 1
+ ]
+ )
+ );
+ $this->assertNull($this->customerRepository->get($email));
+ }
+
+ /**
+ * Clears request.
+ *
+ * @return void
+ */
+ protected function resetRequest(): void
+ {
+ parent::resetRequest();
+ $this->cookieManager->deleteCookie(MessagePlugin::MESSAGES_COOKIES_NAME);
+ $this->_objectManager->removeSharedInstance(Http::class);
+ $this->_objectManager->removeSharedInstance(Request::class);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
index c2e55029cab13..6abbff18c645c 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php
@@ -10,27 +10,26 @@
use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Customer\Model\CustomerRegistry;
use Magento\Customer\Model\Session;
-use Magento\Framework\Api\FilterBuilder;
-use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\Http;
use Magento\Framework\App\Request\Http as HttpRequest;
use Magento\Framework\Data\Form\FormKey;
use Magento\Framework\Message\MessageInterface;
-use Magento\Framework\Phrase;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\Stdlib\CookieManagerInterface;
use Magento\Store\Model\StoreManager;
use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\Xpath;
use Magento\TestFramework\Mail\Template\TransportBuilderMock;
use Magento\TestFramework\Request;
+use Magento\TestFramework\TestCase\AbstractController;
use Magento\Theme\Controller\Result\MessagePlugin;
use PHPUnit\Framework\Constraint\StringContains;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class AccountTest extends \Magento\TestFramework\TestCase\AbstractController
+class AccountTest extends AbstractController
{
/**
* @var TransportBuilderMock
@@ -54,9 +53,8 @@ protected function setUp(): void
*/
protected function login($customerId)
{
- /** @var \Magento\Customer\Model\Session $session */
- $session = Bootstrap::getObjectManager()
- ->get(\Magento\Customer\Model\Session::class);
+ /** @var Session $session */
+ $session = Bootstrap::getObjectManager()->get(Session::class);
$session->loginById($customerId);
}
@@ -148,8 +146,8 @@ public function testCreatepasswordActionWithSession()
$customer->setData('confirmation', 'confirmation');
$customer->save();
- /** @var \Magento\Customer\Model\Session $customer */
- $session = Bootstrap::getObjectManager()->get(\Magento\Customer\Model\Session::class);
+ /** @var Session $customer */
+ $session = Bootstrap::getObjectManager()->get(Session::class);
$session->setRpToken($token);
$session->setRpCustomerId($customer->getId());
@@ -219,83 +217,6 @@ public function testConfirmActionAlreadyActive()
$this->getResponse()->getBody();
}
- /**
- * Tests that without form key user account won't be created
- * and user will be redirected on account creation page again.
- */
- public function testNoFormKeyCreatePostAction()
- {
- $this->fillRequestWithAccountData('test1@email.com');
- $this->getRequest()->setPostValue('form_key', null);
- $this->dispatch('customer/account/createPost');
-
- $this->assertNull($this->getCustomerByEmail('test1@email.com'));
- $this->assertRedirect($this->stringEndsWith('customer/account/create/'));
- $this->assertSessionMessages(
- $this->equalTo([new Phrase('Invalid Form Key. Please refresh the page.')]),
- MessageInterface::TYPE_ERROR
- );
- }
-
- /**
- * @magentoDbIsolation enabled
- * @magentoAppIsolation enabled
- * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_disable.php
- */
- public function testNoConfirmCreatePostAction()
- {
- $this->fillRequestWithAccountDataAndFormKey('test1@email.com');
- $this->dispatch('customer/account/createPost');
- $this->assertRedirect($this->stringEndsWith('customer/account/'));
- $this->assertSessionMessages(
- $this->equalTo(['Thank you for registering with Main Website Store.']),
- MessageInterface::TYPE_SUCCESS
- );
- }
-
- /**
- * @magentoDbIsolation enabled
- * @magentoAppIsolation enabled
- * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php
- */
- public function testWithConfirmCreatePostAction()
- {
- $this->fillRequestWithAccountDataAndFormKey('test2@email.com');
- $this->dispatch('customer/account/createPost');
- $this->assertRedirect($this->stringContains('customer/account/index/'));
- $this->assertSessionMessages(
- $this->equalTo(
- [
- 'You must confirm your account. Please check your email for the confirmation link or '
- . ' click here for a new link.'
- ]
- ),
- MessageInterface::TYPE_SUCCESS
- );
- }
-
- /**
- * @magentoDataFixture Magento/Customer/_files/customer.php
- */
- public function testExistingEmailCreatePostAction()
- {
- $this->fillRequestWithAccountDataAndFormKey('customer@example.com');
- $this->dispatch('customer/account/createPost');
- $this->assertRedirect($this->stringContains('customer/account/create/'));
- $this->assertSessionMessages(
- $this->equalTo(
- [
- 'There is already an account with this email address. ' .
- 'If you are sure that it is your email address, ' .
- ' click here' .
- ' to get your password and access your account.',
- ]
- ),
- MessageInterface::TYPE_ERROR
- );
- }
-
/**
* @magentoDataFixture Magento/Customer/_files/inactive_customer.php
*/
@@ -404,18 +325,16 @@ public function testEditAction()
$this->assertEquals(200, $this->getResponse()->getHttpResponseCode(), $body);
$this->assertStringContainsString(' ', $body);
// Verify the password check box is not checked
- $expectedString = <<
-EXPECTED_HTML;
- $this->assertStringContainsString($expectedString, $body);
+ $checkboxXpath = '//input[@type="checkbox"][@name="change_password"][@id="change-password"][not (@checked)]' .
+ '[@data-role="change-password"][@value="1"][@title="Change Password"][@class="checkbox"]';
+
+ $this->assertEquals(1, Xpath::getElementsCountForXpath($checkboxXpath, $body));
}
/**
* @magentoDataFixture Magento/Customer/_files/customer.php
*/
- public function testChangePasswordEditAction()
+ public function testChangePasswordEditAction(): void
{
$this->login(1);
@@ -425,12 +344,11 @@ public function testChangePasswordEditAction()
$this->assertEquals(200, $this->getResponse()->getHttpResponseCode(), $body);
$this->assertStringContainsString(' ', $body);
// Verify the password check box is checked
- $expectedString = <<
-EXPECTED_HTML;
- $this->assertStringContainsString($expectedString, $body);
+ $checkboxXpath = '//input[@type="checkbox"][@name="change_password"][@id="change-password"]' .
+ '[@data-role="change-password"][@value="1"][@title="Change Password"][@checked="checked"]' .
+ '[@class="checkbox"]';
+
+ $this->assertEquals(1, Xpath::getElementsCountForXpath($checkboxXpath, $body));
}
/**
@@ -615,70 +533,6 @@ public function testWrongConfirmationEditPostAction()
);
}
- /**
- * Register Customer with email confirmation.
- *
- * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php
- * @return void
- * @throws \Magento\Framework\Exception\InputException
- * @throws \Magento\Framework\Exception\LocalizedException
- * @throws \Magento\Framework\Exception\NoSuchEntityException
- * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException
- */
- public function testRegisterCustomerWithEmailConfirmation(): void
- {
- $email = 'test_example@email.com';
- $this->fillRequestWithAccountDataAndFormKey($email);
- $this->dispatch('customer/account/createPost');
- $this->assertRedirect($this->stringContains('customer/account/index/'));
- $this->assertSessionMessages(
- $this->equalTo(
- [
- 'You must confirm your account. Please check your email for the confirmation link or '
- . ' click here for a new link.'
- ]
- ),
- MessageInterface::TYPE_SUCCESS
- );
- /** @var CustomerRepositoryInterface $customerRepository */
- $customerRepository = $this->_objectManager->create(CustomerRepositoryInterface::class);
- /** @var CustomerInterface $customer */
- $customer = $customerRepository->get($email);
- $confirmation = $customer->getConfirmation();
- $message = $this->transportBuilderMock->getSentMessage();
- $rawMessage = $message->getBody()->getParts()[0]->getRawContent();
- $messageConstraint = $this->logicalAnd(
- new StringContains("You must confirm your {$email} email before you can sign in (link is only valid once"),
- new StringContains("customer/account/confirm/?id={$customer->getId()}&key={$confirmation}")
- );
- $this->assertThat($rawMessage, $messageConstraint);
-
- /** @var CookieManagerInterface $cookieManager */
- $cookieManager = $this->_objectManager->get(CookieManagerInterface::class);
- $cookieManager->deleteCookie(MessagePlugin::MESSAGES_COOKIES_NAME);
-
- $this->_objectManager->removeSharedInstance(Http::class);
- $this->_objectManager->removeSharedInstance(Request::class);
- $this->_request = null;
-
- $this->getRequest()
- ->setParam('id', $customer->getId())
- ->setParam('key', $confirmation);
- $this->dispatch('customer/account/confirm');
-
- /** @var StoreManager $store */
- $store = $this->_objectManager->get(StoreManagerInterface::class);
- $name = $store->getStore()->getFrontendName();
-
- $this->assertRedirect($this->stringContains('customer/account/index/'));
- $this->assertSessionMessages(
- $this->equalTo(["Thank you for registering with {$name}."]),
- MessageInterface::TYPE_SUCCESS
- );
- $this->assertEmpty($customerRepository->get($email)->getConfirmation());
- }
-
/**
* Test that confirmation email address displays special characters correctly.
*
@@ -869,74 +723,6 @@ protected function resetRequest(): void
parent::resetRequest();
}
- /**
- * @param string $email
- * @return void
- */
- private function fillRequestWithAccountData($email)
- {
- $this->getRequest()
- ->setMethod('POST')
- ->setParam('firstname', 'firstname1')
- ->setParam('lastname', 'lastname1')
- ->setParam('company', '')
- ->setParam('email', $email)
- ->setParam('password', '_Password1')
- ->setParam('password_confirmation', '_Password1')
- ->setParam('telephone', '5123334444')
- ->setParam('street', ['1234 fake street', ''])
- ->setParam('city', 'Austin')
- ->setParam('region_id', 57)
- ->setParam('region', '')
- ->setParam('postcode', '78701')
- ->setParam('country_id', 'US')
- ->setParam('default_billing', '1')
- ->setParam('default_shipping', '1')
- ->setParam('is_subscribed', '0')
- ->setPostValue('create_address', true);
- }
-
- /**
- * @param string $email
- * @return void
- */
- private function fillRequestWithAccountDataAndFormKey($email)
- {
- $this->fillRequestWithAccountData($email);
- $formKey = $this->_objectManager->get(FormKey::class);
- $this->getRequest()->setParam('form_key', $formKey->getFormKey());
- }
-
- /**
- * Returns stored customer by email.
- *
- * @param string $email
- * @return CustomerInterface
- */
- private function getCustomerByEmail($email)
- {
- /** @var FilterBuilder $filterBuilder */
- $filterBuilder = $this->_objectManager->get(FilterBuilder::class);
- $filters = [
- $filterBuilder->setField(CustomerInterface::EMAIL)
- ->setValue($email)
- ->create()
- ];
-
- /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
- $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class);
- $searchCriteria = $searchCriteriaBuilder->addFilters($filters)
- ->create();
-
- $customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
- $customers = $customerRepository->getList($searchCriteria)
- ->getItems();
-
- $customer = array_pop($customers);
-
- return $customer;
- }
-
/**
* Add new request info (request uri, path info, action name).
*
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php
index e12068ef62b21..bd2c26e449d72 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php
@@ -13,9 +13,12 @@
use Magento\Customer\Api\Data\CustomerInterfaceFactory;
use Magento\Customer\Model\Customer;
use Magento\Customer\Model\CustomerFactory;
+use Magento\Customer\Model\EmailNotification;
+use Magento\Email\Model\ResourceModel\Template\CollectionFactory as TemplateCollectionFactory;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\Api\SimpleDataObjectConverter;
+use Magento\Framework\App\Config\MutableScopeConfigInterface;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
@@ -23,6 +26,7 @@
use Magento\Framework\Math\Random;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Validator\Exception;
+use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Helper\Xpath;
@@ -101,6 +105,16 @@ class CreateAccountTest extends TestCase
*/
private $encryptor;
+ /**
+ * @var MutableScopeConfigInterface
+ */
+ private $mutableScopeConfig;
+
+ /**
+ * @var TemplateCollectionFactory
+ */
+ private $templateCollectionFactory;
+
/**
* @inheritdoc
*/
@@ -117,9 +131,20 @@ protected function setUp(): void
$this->customerModelFactory = $this->objectManager->get(CustomerFactory::class);
$this->random = $this->objectManager->get(Random::class);
$this->encryptor = $this->objectManager->get(EncryptorInterface::class);
+ $this->mutableScopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class);
+ $this->templateCollectionFactory = $this->objectManager->get(TemplateCollectionFactory::class);
parent::setUp();
}
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ parent::tearDown();
+ $this->mutableScopeConfig->clean();
+ }
+
/**
* @dataProvider createInvalidAccountDataProvider
* @param array $customerData
@@ -220,6 +245,98 @@ public function createInvalidAccountDataProvider(): array
];
}
+ /**
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Customer/_files/customer_welcome_email_template.php
+ * @return void
+ */
+ public function testCreateAccountWithConfiguredWelcomeEmail(): void
+ {
+ $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_template');
+ $this->setConfig([EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE => $emailTemplate,]);
+ $this->accountManagement->createAccount(
+ $this->populateCustomerEntity($this->defaultCustomerData),
+ '_Password1'
+ );
+ $this->assertEmailData(
+ [
+ 'name' => 'Owner',
+ 'email' => 'owner@example.com',
+ 'message' => 'Customer create account email template',
+ ]
+ );
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Customer/_files/customer_welcome_no_password_email_template.php
+ * @magentoConfigFixture current_store customer/create_account/email_identity support
+ * @return void
+ */
+ public function testCreateAccountWithConfiguredWelcomeNoPasswordEmail(): void
+ {
+ $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_no_password_template');
+ $this->setConfig([EmailNotification::XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE => $emailTemplate,]);
+ $this->accountManagement->createAccount($this->populateCustomerEntity($this->defaultCustomerData));
+ $this->assertEmailData(
+ [
+ 'name' => 'CustomerSupport',
+ 'email' => 'support@example.com',
+ 'message' => 'Customer create account email no password template',
+ ]
+ );
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmation_email_template.php
+ * @magentoConfigFixture current_website customer/create_account/confirm 1
+ * @magentoConfigFixture current_store customer/create_account/email_identity custom1
+ * @return void
+ */
+ public function testCreateAccountWithConfiguredConfirmationEmail(): void
+ {
+ $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_confirmation_template');
+ $this->setConfig([EmailNotification::XML_PATH_CONFIRM_EMAIL_TEMPLATE => $emailTemplate,]);
+ $this->accountManagement->createAccount(
+ $this->populateCustomerEntity($this->defaultCustomerData),
+ '_Password1'
+ );
+ $this->assertEmailData(
+ [
+ 'name' => 'Custom 1',
+ 'email' => 'custom1@example.com',
+ 'message' => 'Customer create account email confirmation template',
+ ]
+ );
+ }
+
+ /**
+ * @magentoAppArea frontend
+ * @magentoDataFixture Magento/Customer/_files/customer_confirmed_email_template.php
+ * @magentoConfigFixture current_store customer/create_account/email_identity custom1
+ * @magentoConfigFixture current_website customer/create_account/confirm 1
+ * @return void
+ */
+ public function testCreateAccountWithConfiguredConfirmedEmail(): void
+ {
+ $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_confirmed_template');
+ $this->setConfig([EmailNotification::XML_PATH_CONFIRMED_EMAIL_TEMPLATE => $emailTemplate,]);
+ $this->accountManagement->createAccount(
+ $this->populateCustomerEntity($this->defaultCustomerData),
+ '_Password1'
+ );
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->accountManagement->activate($customer->getEmail(), $customer->getConfirmation());
+ $this->assertEmailData(
+ [
+ 'name' => 'Custom 1',
+ 'email' => 'custom1@example.com',
+ 'message' => 'Customer create account email confirmed template',
+ ]
+ );
+ }
+
/**
* Assert that when you create customer account via admin, link with "set password" is send to customer email.
*
@@ -589,4 +706,53 @@ private function assertCustomerData(
);
}
}
+
+ /**
+ * Sets config data.
+ *
+ * @param array $configs
+ * @return void
+ */
+ private function setConfig(array $configs): void
+ {
+ foreach ($configs as $path => $value) {
+ $this->mutableScopeConfig->setValue($path, $value, ScopeInterface::SCOPE_STORE, 'default');
+ }
+ }
+
+ /**
+ * Assert email data.
+ *
+ * @param array $expectedData
+ * @return void
+ */
+ private function assertEmailData(array $expectedData): void
+ {
+ $message = $this->transportBuilderMock->getSentMessage();
+ $this->assertNotNull($message);
+ $messageFrom = $message->getFrom();
+ $this->assertNotNull($messageFrom);
+ $messageFrom = reset($messageFrom);
+ $this->assertEquals($expectedData['name'], $messageFrom->getName());
+ $this->assertEquals($expectedData['email'], $messageFrom->getEmail());
+ $this->assertStringContainsString(
+ $expectedData['message'],
+ $message->getBody()->getParts()[0]->getRawContent(),
+ 'Expected message wasn\'t found in email content.'
+ );
+ }
+
+ /**
+ * Returns email template id by template code.
+ *
+ * @param string $templateCode
+ * @return int
+ */
+ private function getCustomTemplateId(string $templateCode): int
+ {
+ return (int)$this->templateCollectionFactory->create()
+ ->addFieldToFilter('template_code', $templateCode)
+ ->getFirstItem()
+ ->getId();
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php
index ac55f93bc9e4b..eb638eeb329aa 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php
@@ -14,11 +14,16 @@
use Magento\Customer\Model\AddressRegistry;
use Magento\Customer\Model\CustomerRegistry;
use Magento\Customer\Model\ResourceModel\Address;
+use Magento\Customer\Model\Vat;
+use Magento\Customer\Observer\AfterAddressSaveObserver;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\DataObjectFactory;
use Magento\Framework\Exception\InputException;
use Magento\TestFramework\Directory\Model\GetRegionIdByName;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\ObjectManager;
use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface as PsrLogger;
/**
* Assert that address was created as expected or address create throws expected error.
@@ -88,6 +93,11 @@ class CreateAddressTest extends TestCase
*/
private $createdAddressesIds = [];
+ /**
+ * @var DataObjectFactory
+ */
+ private $dataObjectFactory;
+
/**
* @inheritdoc
*/
@@ -101,6 +111,7 @@ protected function setUp(): void
$this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
$this->addressRegistry = $this->objectManager->get(AddressRegistry::class);
$this->addressResource = $this->objectManager->get(Address::class);
+ $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class);
parent::setUp();
}
@@ -112,6 +123,7 @@ protected function tearDown(): void
foreach ($this->createdAddressesIds as $createdAddressesId) {
$this->addressRegistry->remove($createdAddressesId);
}
+ $this->objectManager->removeSharedInstance(AfterAddressSaveObserver::class);
parent::tearDown();
}
@@ -326,6 +338,92 @@ public function createWrongAddressesDataProvider(): array
];
}
+ /**
+ * Assert that after address creation customer group is Group for Valid VAT ID - Domestic.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ * @magentoConfigFixture current_store general/store_information/country_id AT
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ * @magentoConfigFixture current_store customer/create_account/viv_domestic_group 2
+ * @return void
+ */
+ public function testAddressCreatedWithGroupAssignByDomesticVatId(): void
+ {
+ $this->createVatMock(true, true);
+ $addressData = array_merge(
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT']
+ );
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->createAddress((int)$customer->getId(), $addressData, false, true);
+ $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com'));
+ }
+
+ /**
+ * Assert that after address creation customer group is Group for Valid VAT ID - Intra-Union.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ * @magentoConfigFixture current_store general/store_information/country_id GR
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ * @magentoConfigFixture current_store customer/create_account/viv_intra_union_group 2
+ * @return void
+ */
+ public function testAddressCreatedWithGroupAssignByIntraUnionVatId(): void
+ {
+ $this->createVatMock(true, true);
+ $addressData = array_merge(
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT']
+ );
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->createAddress((int)$customer->getId(), $addressData, false, true);
+ $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com'));
+ }
+
+ /**
+ * Assert that after address creation customer group is Group for Invalid VAT ID.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ * @magentoConfigFixture current_store customer/create_account/viv_invalid_group 2
+ * @return void
+ */
+ public function testAddressCreatedWithGroupAssignByInvalidVatId(): void
+ {
+ $this->createVatMock(false, true);
+ $addressData = array_merge(
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT']
+ );
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->createAddress((int)$customer->getId(), $addressData, false, true);
+ $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com'));
+ }
+
+ /**
+ * Assert that after address creation customer group is Validation Error Group.
+ *
+ * @magentoAppIsolation enabled
+ * @magentoDataFixture Magento/Customer/_files/customer_no_address.php
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ * @magentoConfigFixture current_store customer/create_account/viv_error_group 2
+ * @return void
+ */
+ public function testAddressCreatedWithGroupAssignByVatIdWithError(): void
+ {
+ $this->createVatMock(false, false);
+ $addressData = array_merge(
+ self::STATIC_CUSTOMER_ADDRESS_DATA,
+ [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT']
+ );
+ $customer = $this->customerRepository->get('customer5@example.com');
+ $this->createAddress((int)$customer->getId(), $addressData, false, true);
+ $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com'));
+ }
+
/**
* Create customer address with provided address data.
*
@@ -361,4 +459,49 @@ protected function createAddress(
return $address;
}
+
+ /**
+ * Creates mock for vat id validation.
+ *
+ * @param bool $isValid
+ * @param bool $isRequestSuccess
+ * @return void
+ */
+ private function createVatMock(bool $isValid = false, bool $isRequestSuccess = false): void
+ {
+ $gatewayResponse = $this->dataObjectFactory->create(
+ [
+ 'data' => [
+ 'is_valid' => $isValid,
+ 'request_date' => '',
+ 'request_identifier' => '123123123',
+ 'request_success' => $isRequestSuccess,
+ 'request_message' => __(''),
+ ],
+ ]
+ );
+ $customerVat = $this->getMockBuilder(Vat::class)
+ ->setConstructorArgs(
+ [
+ $this->objectManager->get(ScopeConfigInterface::class),
+ $this->objectManager->get(PsrLogger::class)
+ ]
+ )
+ ->setMethods(['checkVatNumber'])
+ ->getMock();
+ $customerVat->method('checkVatNumber')->willReturn($gatewayResponse);
+ $this->objectManager->removeSharedInstance(Vat::class);
+ $this->objectManager->addSharedInstance($customerVat, Vat::class);
+ }
+
+ /**
+ * Returns customer group id by email.
+ *
+ * @param string $email
+ * @return int
+ */
+ private function getCustomerGroupId(string $email): int
+ {
+ return (int)$this->customerRepository->get($email)->getGroupId();
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ForgotPasswordToken/GetCustomerByTokenTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ForgotPasswordToken/GetCustomerByTokenTest.php
new file mode 100644
index 0000000000000..55e9a9572d229
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ForgotPasswordToken/GetCustomerByTokenTest.php
@@ -0,0 +1,42 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->customerByToken = $this->objectManager->get(GetCustomerByToken::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ */
+ public function testExecuteWithNoSuchEntityException(): void
+ {
+ self::expectException(NoSuchEntityException::class);
+ self::expectExceptionMessage('No such entity with rp_token = ' . self::RESET_PASSWORD);
+ $this->customerByToken->execute(self::RESET_PASSWORD);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php
new file mode 100644
index 0000000000000..38b607230cbaf
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php
@@ -0,0 +1,30 @@
+get(TemplateResource::class);
+/** @var TemplateInterfaceFactory $templateFactory */
+$templateFactory = $objectManager->get(TemplateInterfaceFactory::class);
+/** @var TemplateInterface $template */
+$template = $templateFactory->create();
+
+$content = <<{{trans "Customer create account email confirmation template"}}
+{{template config_path="design/email/footer_template"}}
+HTML;
+
+$template->setTemplateCode('customer_create_account_email_confirmation_template')
+ ->setTemplateText($content)
+ ->setTemplateType(TemplateInterface::TYPE_HTML);
+$templateResource->save($template);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php
new file mode 100644
index 0000000000000..07fee6e81fe47
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php
@@ -0,0 +1,25 @@
+get(TemplateResource::class);
+/** @var Collection $collection */
+$collection = $objectManager->get(CollectionFactory::class)->create();
+/** @var TemplateInterface $template */
+$template = $collection
+ ->addFieldToFilter('template_code', 'customer_create_account_email_confirmation_template')
+ ->getFirstItem();
+if ($template->getId()) {
+ $templateResource->delete($template);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php
new file mode 100644
index 0000000000000..859cae92dbd27
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php
@@ -0,0 +1,30 @@
+get(TemplateResource::class);
+/** @var TemplateInterfaceFactory $templateFactory */
+$templateFactory = $objectManager->get(TemplateInterfaceFactory::class);
+/** @var TemplateInterface $template */
+$template = $templateFactory->create();
+
+$content = <<{{trans "Customer create account email confirmed template"}}
+{{template config_path="design/email/footer_template"}}
+HTML;
+
+$template->setTemplateCode('customer_create_account_email_confirmed_template')
+ ->setTemplateText($content)
+ ->setTemplateType(TemplateInterface::TYPE_HTML);
+$templateResource->save($template);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php
new file mode 100644
index 0000000000000..a4e03038d45bd
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php
@@ -0,0 +1,25 @@
+get(TemplateResource::class);
+/** @var Collection $collection */
+$collection = $objectManager->get(CollectionFactory::class)->create();
+/** @var TemplateInterface $template */
+$template = $collection
+ ->addFieldToFilter('template_code', 'customer_create_account_email_confirmed_template')
+ ->getFirstItem();
+if ($template->getId()) {
+ $templateResource->delete($template);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php
new file mode 100644
index 0000000000000..6cc273dbe235a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php
@@ -0,0 +1,30 @@
+get(TemplateResource::class);
+/** @var TemplateInterfaceFactory $templateFactory */
+$templateFactory = $objectManager->get(TemplateInterfaceFactory::class);
+/** @var TemplateInterface $template */
+$template = $templateFactory->create();
+
+$content = <<{{trans "Customer create account email template"}}
+{{template config_path="design/email/footer_template"}}
+HTML;
+
+$template->setTemplateCode('customer_create_account_email_template')
+ ->setTemplateText($content)
+ ->setTemplateType(TemplateInterface::TYPE_HTML);
+$templateResource->save($template);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php
new file mode 100644
index 0000000000000..6bef9822d3e9a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php
@@ -0,0 +1,23 @@
+get(TemplateResource::class);
+/** @var Collection $collection */
+$collection = $objectManager->get(CollectionFactory::class)->create();
+/** @var TemplateInterface $template */
+$template = $collection->addFieldToFilter('template_code', 'customer_create_account_email_template')->getFirstItem();
+if ($template->getId()) {
+ $templateResource->delete($template);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php
new file mode 100644
index 0000000000000..a936bb9a4eb02
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php
@@ -0,0 +1,30 @@
+get(TemplateResource::class);
+/** @var TemplateInterfaceFactory $templateFactory */
+$templateFactory = $objectManager->get(TemplateInterfaceFactory::class);
+/** @var TemplateInterface $template */
+$template = $templateFactory->create();
+
+$content = <<{{trans "Customer create account email no password template"}}
+{{template config_path="design/email/footer_template"}}
+HTML;
+
+$template->setTemplateCode('customer_create_account_email_no_password_template')
+ ->setTemplateText($content)
+ ->setTemplateType(TemplateInterface::TYPE_HTML);
+$templateResource->save($template);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php
new file mode 100644
index 0000000000000..4e14b4293cbb5
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php
@@ -0,0 +1,25 @@
+get(TemplateResource::class);
+/** @var Collection $collection */
+$collection = $objectManager->get(CollectionFactory::class)->create();
+/** @var TemplateInterface $template */
+$template = $collection
+ ->addFieldToFilter('template_code', 'customer_create_account_email_no_password_template')
+ ->getFirstItem();
+if ($template->getId()) {
+ $templateResource->delete($template);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer_rollback.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer_rollback.php
index 9ca4f78660b3a..a97faa29a1588 100644
--- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer_rollback.php
@@ -4,6 +4,10 @@
* See COPYING.txt for license details.
*/
+use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
+
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_boolean_attribute_rollback.php');
+
/** @var $objectManager \Magento\Framework\ObjectManagerInterface */
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php b/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php
index 8d4ebc40128d1..aad47165a470a 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Filter/Template/Tokenizer/ParameterTest.php
@@ -3,20 +3,33 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Framework\Filter\Template\Tokenizer;
-class ParameterTest extends \PHPUnit\Framework\TestCase
+use Magento\Catalog\Block\Product\Widget\NewWidget;
+use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Test for \Magento\Framework\Filter\Template\Tokenizer\Parameter.
+ */
+class ParameterTest extends TestCase
{
/**
+ * Test for getValue
+ *
+ * @dataProvider getValueDataProvider
+ *
* @param string $string
* @param array $values
- * @dataProvider getValueDataProvider
+ * @return void
*/
- public function testGetValue($string, $values)
+ public function testGetValue($string, $values): void
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- /** @var \Magento\Framework\Filter\Template\Tokenizer\Parameter $parameter */
- $parameter = $objectManager->create(\Magento\Framework\Filter\Template\Tokenizer\Parameter::class);
+ $objectManager = Bootstrap::getObjectManager();
+ /** @var Parameter $parameter */
+ $parameter = $objectManager->create(Parameter::class);
$parameter->setString($string);
foreach ($values as $value) {
@@ -25,30 +38,36 @@ public function testGetValue($string, $values)
}
/**
+ * Test for tokenize
+ *
* @dataProvider tokenizeDataProvider
+ *
* @param string $string
* @param array $params
+ * @return void
*/
- public function testTokenize($string, $params)
+ public function testTokenize(string $string, array $params): void
{
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- /** @var \Magento\Framework\Filter\Template\Tokenizer\Parameter $parameter */
- $parameter = $objectManager->create(\Magento\Framework\Filter\Template\Tokenizer\Parameter::class);
+ $objectManager = Bootstrap::getObjectManager();
+ $parameter = $objectManager->create(Parameter::class);
$parameter->setString($string);
+
$this->assertEquals($params, $parameter->tokenize());
}
/**
+ * DataProvider for testTokenize
+ *
* @return array
*/
- public function tokenizeDataProvider()
+ public function tokenizeDataProvider(): array
{
return [
[
' type="Magento\\Catalog\\Block\\Product\\Widget\\NewWidget" display_type="all_products"'
. ' products_count="10" template="product/widget/new/content/new_grid.phtml"',
[
- 'type' => \Magento\Catalog\Block\Product\Widget\NewWidget::class,
+ 'type' => NewWidget::class,
'display_type' => 'all_products',
'products_count' => 10,
'template' => 'product/widget/new/content/new_grid.phtml'
@@ -58,12 +77,24 @@ public function tokenizeDataProvider()
' type="Magento\Catalog\Block\Product\Widget\NewWidget" display_type="all_products"'
. ' products_count="10" template="product/widget/new/content/new_grid.phtml"',
[
- 'type' => \Magento\Catalog\Block\Product\Widget\NewWidget::class,
+ 'type' => NewWidget::class,
'display_type' => 'all_products',
'products_count' => 10,
'template' => 'product/widget/new/content/new_grid.phtml'
]
- ]
+ ],
+ [
+ sprintf(
+ 'type="%s" display_type="all_products" products_count="1" template="content/new_grid.phtml"',
+ NewWidget::class
+ ),
+ [
+ 'type' => NewWidget::class,
+ 'display_type' => 'all_products',
+ 'products_count' => 1,
+ 'template' => 'content/new_grid.phtml'
+ ],
+ ],
];
}
diff --git a/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/Model/SynchronizeFilesTest.php b/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/Model/SynchronizeFilesTest.php
new file mode 100644
index 0000000000000..52e7191a97226
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/Model/SynchronizeFilesTest.php
@@ -0,0 +1,151 @@
+driver = Bootstrap::getObjectManager()->get(DriverInterface::class);
+ $this->synchronizeFiles = Bootstrap::getObjectManager()->get(SynchronizeFilesInterface::class);
+ $this->getAssetsByPath = Bootstrap::getObjectManager()->get(GetAssetsByPathsInterface::class);
+ $this->getAssetKeywords = Bootstrap::getObjectManager()->get(GetAssetsKeywordsInterface::class);
+ $this->mediaDirectory = Bootstrap::getObjectManager()->get(Filesystem::class)
+ ->getDirectoryWrite(DirectoryList::MEDIA);
+ }
+
+ /**
+ * Test for SynchronizeFiles::execute
+ *
+ * @dataProvider filesProvider
+ * @param null|string $file
+ * @param null|string $title
+ * @param null|string $description
+ * @param null|array $keywords
+ * @throws FileSystemException
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ public function testExecute(
+ ?string $file,
+ ?string $title,
+ ?string $description,
+ ?array $keywords
+ ): void {
+ $path = realpath(__DIR__ . '/../_files/' . $file);
+ $modifiableFilePath = $this->mediaDirectory->getAbsolutePath($file);
+ $this->driver->copy(
+ $path,
+ $modifiableFilePath
+ );
+
+ $this->synchronizeFiles->execute([$file]);
+
+ $loadedAssets = $this->getAssetsByPath->execute([$file])[0];
+ $loadedKeywords = $this->getKeywords($loadedAssets) ?: null;
+
+ $this->assertEquals($title, $loadedAssets->getTitle());
+ $this->assertEquals($description, $loadedAssets->getDescription());
+ $this->assertEquals($keywords, $loadedKeywords);
+
+ $this->driver->deleteFile($modifiableFilePath);
+ }
+
+ /**
+ * Data provider for testExecute
+ *
+ * @return array[]
+ */
+ public function filesProvider(): array
+ {
+ return [
+ [
+ '/magento.jpg',
+ 'magento',
+ null,
+ null
+ ],
+ [
+ '/magento_metadata.jpg',
+ 'Title of the magento image',
+ 'Description of the magento image',
+ [
+ 'magento',
+ 'mediagallerymetadata'
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Key asset keywords
+ *
+ * @param AssetInterface $asset
+ * @return string[]
+ */
+ private function getKeywords(AssetInterface $asset): array
+ {
+ $assetKeywords = $this->getAssetKeywords->execute([$asset->getId()]);
+
+ if (empty($assetKeywords)) {
+ return [];
+ }
+
+ $keywords = current($assetKeywords)->getKeywords();
+
+ return array_map(
+ function (KeywordInterface $keyword) {
+ return $keyword->getKeyword();
+ },
+ $keywords
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/_files/magento.jpg b/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/_files/magento.jpg
new file mode 100644
index 0000000000000..c377daf8fb0b3
Binary files /dev/null and b/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/_files/magento.jpg differ
diff --git a/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/_files/magento_metadata.jpg b/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/_files/magento_metadata.jpg
new file mode 100644
index 0000000000000..6dc8cd69e41c1
Binary files /dev/null and b/dev/tests/integration/testsuite/Magento/MediaGallerySynchronizationMetadata/_files/magento_metadata.jpg differ
diff --git a/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php b/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php
index 31b0ac84266e7..3b70659f80a42 100644
--- a/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php
+++ b/dev/tests/integration/testsuite/Magento/Multishipping/Fixtures/quote_with_configurable_product.php
@@ -24,7 +24,7 @@
$objectManager = Bootstrap::getObjectManager();
/** @var ProductRepositoryInterface $productRepository */
$productRepository = $objectManager->create(ProductRepositoryInterface::class);
-$product = $productRepository->getById(10);
+$product = $productRepository->get('simple_10');
$product->setStockData(['use_config_manage_stock' => 1, 'qty' => 4, 'is_qty_decimal' => 0, 'is_in_stock' => 1]);
$productRepository->save($product);
diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php
index 370dc552458b9..dbf8bce795548 100644
--- a/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php
+++ b/dev/tests/integration/testsuite/Magento/Newsletter/Model/SubscriberTest.php
@@ -8,6 +8,7 @@
namespace Magento\Newsletter\Model;
use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Framework\Mail\EmailMessage;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\ObjectManagerInterface;
@@ -22,13 +23,16 @@
*/
class SubscriberTest extends TestCase
{
- /** @var ObjectManagerInterface */
+ private const CONFIRMATION_SUBSCRIBE = 'You have been successfully subscribed to our newsletter.';
+ private const CONFIRMATION_UNSUBSCRIBE = 'You have been unsubscribed from the newsletter.';
+
+ /** @var ObjectManagerInterface */
private $objectManager;
/** @var SubscriberFactory */
private $subscriberFactory;
- /** @var TransportBuilderMock */
+ /** @var TransportBuilderMock */
private $transportBuilder;
/** @var CustomerRepositoryInterface */
@@ -89,27 +93,20 @@ public function testUnsubscribeSubscribe(): void
$subscriber = $this->subscriberFactory->create();
$this->assertSame($subscriber, $subscriber->loadByCustomerId(1));
$this->assertEquals($subscriber, $subscriber->unsubscribe());
- $this->assertStringContainsString(
- 'You have been unsubscribed from the newsletter.',
- $this->getFilteredRawMessage($this->transportBuilder)
+ $this->assertConfirmationParagraphExists(
+ self::CONFIRMATION_UNSUBSCRIBE,
+ $this->transportBuilder->getSentMessage()
);
+
$this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getSubscriberStatus());
// Subscribe and verify
$this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->subscribe('customer@example.com'));
$this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getSubscriberStatus());
- $this->assertStringContainsString(
- 'You have been successfully subscribed to our newsletter.',
- $this->getFilteredRawMessage($this->transportBuilder)
- );
- }
- /**
- * @param TransportBuilderMock $transportBuilderMock
- * @return string
- */
- private function getFilteredRawMessage(TransportBuilderMock $transportBuilderMock): string
- {
- return $transportBuilderMock->getSentMessage()->getBody()->getParts()[0]->getRawContent();
+ $this->assertConfirmationParagraphExists(
+ self::CONFIRMATION_SUBSCRIBE,
+ $this->transportBuilder->getSentMessage()
+ );
}
/**
@@ -125,16 +122,17 @@ public function testUnsubscribeSubscribeByCustomerId(): void
// Unsubscribe and verify
$this->assertSame($subscriber, $subscriber->unsubscribeCustomerById(1));
$this->assertEquals(Subscriber::STATUS_UNSUBSCRIBED, $subscriber->getSubscriberStatus());
- $this->assertStringContainsString(
- 'You have been unsubscribed from the newsletter.',
- $this->getFilteredRawMessage($this->transportBuilder)
+ $this->assertConfirmationParagraphExists(
+ self::CONFIRMATION_UNSUBSCRIBE,
+ $this->transportBuilder->getSentMessage()
);
+
// Subscribe and verify
$this->assertSame($subscriber, $subscriber->subscribeCustomerById(1));
$this->assertEquals(Subscriber::STATUS_SUBSCRIBED, $subscriber->getSubscriberStatus());
- $this->assertStringContainsString(
- 'You have been successfully subscribed to our newsletter.',
- $this->getFilteredRawMessage($this->transportBuilder)
+ $this->assertConfirmationParagraphExists(
+ self::CONFIRMATION_SUBSCRIBE,
+ $this->transportBuilder->getSentMessage()
);
}
@@ -152,9 +150,10 @@ public function testConfirm(): void
$subscriber->subscribe($customerEmail);
$subscriber->loadByEmail($customerEmail);
$subscriber->confirm($subscriber->getSubscriberConfirmCode());
- $this->assertStringContainsString(
- 'You have been successfully subscribed to our newsletter.',
- $this->getFilteredRawMessage($this->transportBuilder)
+
+ $this->assertConfirmationParagraphExists(
+ self::CONFIRMATION_SUBSCRIBE,
+ $this->transportBuilder->getSentMessage()
);
}
@@ -189,4 +188,35 @@ public function testSubscribeUnconfirmedCustomerWithoutSubscription(): void
$subscriber->subscribeCustomerById($customer->getId());
$this->assertEquals(Subscriber::STATUS_UNCONFIRMED, $subscriber->getStatus());
}
+
+ /**
+ * Verifies if Paragraph with specified message is in e-mail
+ *
+ * @param string $expectedMessage
+ * @param EmailMessage $message
+ */
+ private function assertConfirmationParagraphExists(string $expectedMessage, EmailMessage $message): void
+ {
+ $messageContent = $this->getMessageRawContent($message);
+
+ $emailDom = new \DOMDocument();
+ $emailDom->loadHTML($messageContent);
+
+ $emailXpath = new \DOMXPath($emailDom);
+ $greeting = $emailXpath->query("//p[contains(text(), '$expectedMessage')]");
+
+ $this->assertSame(1, $greeting->length, "Cannot find the confirmation paragraph in e-mail contents");
+ }
+
+ /**
+ * Returns raw content of provided message
+ *
+ * @param EmailMessage $message
+ * @return string
+ */
+ private function getMessageRawContent(EmailMessage $message): string
+ {
+ $emailParts = $message->getBody()->getParts();
+ return current($emailParts)->getRawContent();
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
index c1ac7cf1ef723..13fcd53d78186 100644
--- a/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
+++ b/dev/tests/integration/testsuite/Magento/Paypal/Model/Express/CheckoutTest.php
@@ -17,11 +17,13 @@
use Magento\Quote\Model\Quote\Address;
use Magento\Quote\Model\ResourceModel\Quote\Collection;
use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
-class CheckoutTest extends \PHPUnit\Framework\TestCase
+class CheckoutTest extends TestCase
{
/**
* @var ObjectManagerInterface
@@ -29,22 +31,22 @@ class CheckoutTest extends \PHPUnit\Framework\TestCase
private $objectManager;
/**
- * @var Info|\PHPUnit\Framework\MockObject\MockObject
+ * @var Info|MockObject
*/
private $paypalInfo;
/**
- * @var Config|\PHPUnit\Framework\MockObject\MockObject
+ * @var Config|MockObject
*/
private $paypalConfig;
/**
- * @var Factory|\PHPUnit\Framework\MockObject\MockObject
+ * @var Factory|MockObject
*/
private $apiTypeFactory;
/**
- * @var Nvp|\PHPUnit\Framework\MockObject\MockObject
+ * @var Nvp|MockObject
*/
private $api;
@@ -215,6 +217,28 @@ public function testPlaceGuestQuote()
$this->assertNotEmpty($order->getShippingAddress());
}
+ /**
+ * Place the order as guest when `Automatic Assignment to Customer Group` is enabled.
+ *
+ * @magentoDataFixture Magento/Paypal/_files/quote_express.php
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ *
+ * @return void
+ */
+ public function testPlaceGuestQuoteAutomaticAssignmentEnabled(): void
+ {
+ $quote = $this->getFixtureQuote();
+ $quote->setCheckoutMethod(Onepage::METHOD_GUEST);
+ $quote->getShippingAddress()->setSameAsBilling(0);
+ $quote->setReservedOrderId(null);
+
+ $checkout = $this->getCheckout($quote);
+ $checkout->place('token');
+
+ $order = $checkout->getOrder();
+ $this->assertNotEmpty($order->getRealOrderId());
+ }
+
/**
* @param Quote $quote
* @return Checkout
@@ -721,11 +745,11 @@ private function getFixtureQuote(): Quote
/**
* Adds countryFactory to a mock.
*
- * @param \PHPUnit\Framework\MockObject\MockObject $api
+ * @param MockObject $api
* @return void
* @throws \ReflectionException
*/
- private function addCountryFactory(\PHPUnit\Framework\MockObject\MockObject $api): void
+ private function addCountryFactory(MockObject $api): void
{
$reflection = new \ReflectionClass($api);
$property = $reflection->getProperty('_countryFactory');
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php
new file mode 100644
index 0000000000000..ca1f309c5cc9b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php
@@ -0,0 +1,108 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Remember::class)
+ ->setTemplate('Magento_Persistent::remember_me.phtml');
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_default 0
+ *
+ * @return void
+ */
+ public function testRememberMeEnabled(): void
+ {
+ $this->assertFalse($this->block->isRememberMeChecked());
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ '//input[@name="persistent_remember_me"]/following-sibling::label/span[contains(text(), "%s")]',
+ __('Remember Me')
+ ),
+ $this->block->toHtml()
+ ),
+ 'Remember Me checkbox wasn\'t found.'
+ );
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_default 1
+ *
+ * @return void
+ */
+ public function testRememberMeAndRememberDefaultEnabled(): void
+ {
+ $this->assertTrue($this->block->isRememberMeChecked());
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ '//input[@name="persistent_remember_me"]/following-sibling::label/span[contains(text(), "%s")]',
+ __('Remember Me')
+ ),
+ $this->block->toHtml()
+ ),
+ 'Remember Me checkbox wasn\'t found or not checked by default.'
+ );
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 0
+ *
+ * @return void
+ */
+ public function testPersistentDisabled(): void
+ {
+ $this->assertEmpty($this->block->toHtml());
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_enabled 0
+ *
+ * @return void
+ */
+ public function testRememberMeDisabled(): void
+ {
+ $this->assertEmpty($this->block->toHtml());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php
new file mode 100644
index 0000000000000..16ce015d89ecd
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php
@@ -0,0 +1,80 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->helper = $this->objectManager->get(Session::class);
+ $this->sessionFactory = $this->objectManager->get(SessionFactory::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ *
+ * @return void
+ */
+ public function testPersistentEnabled(): void
+ {
+ $this->helper->setSession($this->sessionFactory->create()->loadByCustomerId(1));
+ $this->assertTrue($this->helper->isPersistent());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent.php
+ * @magentoConfigFixture current_store persistent/options/enabled 0
+ *
+ * @return void
+ */
+ public function testPersistentDisabled(): void
+ {
+ $this->helper->setSession($this->sessionFactory->create()->loadByCustomerId(1));
+ $this->assertFalse($this->helper->isPersistent());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ *
+ * @return void
+ */
+ public function testCustomerWithoutPersistent(): void
+ {
+ $this->helper->setSession($this->sessionFactory->create()->loadByCustomerId(1));
+ $this->assertFalse($this->helper->isPersistent());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php
new file mode 100644
index 0000000000000..803e1502e3ad9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php
@@ -0,0 +1,160 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->configProvider = $this->objectManager->get(DefaultConfigProvider::class);
+ $this->customerSession = $this->objectManager->get(CustomerSession::class);
+ $this->checkoutSession = $this->objectManager->get(CheckoutSession::class);
+ $this->quoteIdMask = $this->objectManager->get(QuoteIdMaskFactory::class)->create();
+ $this->persistentSessionHelper = $this->objectManager->get(PersistentSessionHelper::class);
+ $this->persistentSession = $this->objectManager->get(PersistentSessionFactory::class)->create();
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->customerSession->setCustomerId(null);
+ $this->checkoutSession->clearQuote();
+ $this->checkoutSession->setCustomerData(null);
+ $this->persistentSessionHelper->setSession(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @return void
+ */
+ public function testPluginIsRegistered(): void
+ {
+ $pluginInfo = $this->objectManager->get(PluginList::class)->get(DefaultConfigProvider::class);
+ $this->assertSame(ConfigProviderPlugin::class, $pluginInfo['mask_quote_id_substitutor']['instance']);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ *
+ * @return void
+ */
+ public function testWithNotLoggedCustomer(): void
+ {
+ $session = $this->persistentSession->loadByCustomerId(1);
+ $this->persistentSessionHelper->setSession($session);
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($quote->getId());
+ $result = $this->configProvider->getConfig();
+ $this->assertEquals(
+ $this->quoteIdMask->load($quote->getId(), 'quote_id')->getMaskedId(),
+ $result['quoteData']['entity_id']
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ *
+ * @return void
+ */
+ public function testWithLoggedCustomer(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $session = $this->persistentSession->loadByCustomerId(1);
+ $this->persistentSessionHelper->setSession($session);
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($quote->getId());
+ $result = $this->configProvider->getConfig();
+ $this->assertEquals($quote->getId(), $result['quoteData']['entity_id']);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ * @magentoConfigFixture current_store persistent/options/enabled 0
+ *
+ * @return void
+ */
+ public function testPersistentDisabled(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($quote->getId());
+ $result = $this->configProvider->getConfig();
+ $this->assertNull($result['quoteData']['entity_id']);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ *
+ * @return void
+ */
+ public function testWithoutPersistentSession(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($quote->getId());
+ $result = $this->configProvider->getConfig();
+ $this->assertNull($result['quoteData']['entity_id']);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php
new file mode 100644
index 0000000000000..176224bad7a1f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php
@@ -0,0 +1,84 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->get(CheckoutConfigProvider::class);
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_default 1
+ *
+ * @return void
+ */
+ public function testRememberMeEnabled(): void
+ {
+ $expectedConfig = [
+ 'persistenceConfig' => ['isRememberMeCheckboxVisible' => true, 'isRememberMeCheckboxChecked' => true],
+ ];
+ $config = $this->model->getConfig();
+ $this->assertEquals($expectedConfig, $config);
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/remember_enabled 0
+ * @magentoConfigFixture current_store persistent/options/remember_default 0
+ *
+ * @return void
+ */
+ public function testRememberMeDisabled(): void
+ {
+ $expectedConfig = [
+ 'persistenceConfig' => ['isRememberMeCheckboxVisible' => false, 'isRememberMeCheckboxChecked' => false],
+ ];
+ $config = $this->model->getConfig();
+ $this->assertEquals($expectedConfig, $config);
+ }
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 0
+ * @magentoConfigFixture current_store persistent/options/remember_default 0
+ *
+ * @return void
+ */
+ public function testPersistentDisabled(): void
+ {
+ $expectedConfig = [
+ 'persistenceConfig' => ['isRememberMeCheckboxVisible' => false, 'isRememberMeCheckboxChecked' => false],
+ ];
+ $config = $this->model->getConfig();
+ $this->assertEquals($expectedConfig, $config);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php
new file mode 100644
index 0000000000000..e11d47af3e814
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php
@@ -0,0 +1,116 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->model = $this->objectManager->get(QuoteManager::class);
+ $this->checkoutSession = $this->objectManager->get(CheckoutSession::class);
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->persistentSessionHelper = $this->objectManager->get(PersistentSessionHelper::class);
+ $this->quoteRepository = $this->objectManager->get(CartRepositoryInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->checkoutSession->clearQuote();
+ $this->checkoutSession->setCustomerData(null);
+ if ($this->quote instanceof CartInterface) {
+ $this->quoteRepository->delete($this->quote);
+ }
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/shopping_cart 1
+ *
+ * @return void
+ */
+ public function testPersistentShoppingCartEnabled(): void
+ {
+ $customerQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($customerQuote->getId());
+ $this->model->setGuest(true);
+ $this->quote = $this->checkoutSession->getQuote();
+ $this->assertNotEquals($customerQuote->getId(), $this->quote->getId());
+ $this->assertFalse($this->model->isPersistent());
+ $this->assertNull($this->quote->getCustomerId());
+ $this->assertNull($this->quote->getCustomerEmail());
+ $this->assertNull($this->quote->getCustomerFirstname());
+ $this->assertNull($this->quote->getCustomerLastname());
+ $this->assertEquals(GroupInterface::NOT_LOGGED_IN_ID, $this->quote->getCustomerGroupId());
+ $this->assertEmpty($this->quote->getIsPersistent());
+ $this->assertNull($this->persistentSessionHelper->getSession()->getId());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/shopping_cart 0
+ *
+ * @return void
+ */
+ public function testPersistentShoppingCartDisabled(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->checkoutSession->setQuoteId($quote->getId());
+ $this->model->setGuest(true);
+ $this->assertNull($this->checkoutSession->getQuote()->getId());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php
index 35f2283494b1c..bd4d24211f1e3 100644
--- a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php
@@ -10,16 +10,21 @@
use DateTime;
use DateTimeZone;
use Magento\Customer\Api\CustomerRepositoryInterface;
-use Magento\Customer\Api\Data\CustomerInterface;
-use Magento\Framework\Event;
-use Magento\Framework\Event\Observer;
+use Magento\Customer\Model\Session as CustomerSession;
use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Stdlib\CookieManagerInterface;
+use Magento\Persistent\Helper\Session as PersistentSessionHelper;
use Magento\Persistent\Model\Session;
use Magento\Persistent\Model\SessionFactory;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
/**
+ * Test for synchronize persistent session on login observer
+ *
+ * @see \Magento\Persistent\Observer\SynchronizePersistentOnLoginObserver
+ * @magentoAppArea frontend
+ * @magentoDbIsolation enabled
* @magentoDataFixture Magento/Customer/_files/customer.php
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
@@ -28,87 +33,129 @@ class SynchronizePersistentOnLoginObserverTest extends TestCase
/**
* @var SynchronizePersistentOnLoginObserver
*/
- protected $_model;
+ private $model;
/**
* @var ObjectManagerInterface
*/
- protected $_objectManager;
+ private $objectManager;
/**
- * @var \Magento\Persistent\Helper\Session
+ * @var PersistentSessionHelper
*/
- protected $_persistentSession;
+ private $persistentSessionHelper;
/**
- * @var \Magento\Customer\Model\Session
+ * @var CustomerRepositoryInterface
*/
- protected $_customerSession;
+ private $customerRepository;
/**
- * @var CustomerInterface
+ * @var SessionFactory
*/
- private $customer;
+ private $persistentSessionFactory;
+
+ /**
+ * @var CookieManagerInterface
+ */
+ private $cookieManager;
+
+ /**
+ * @var CustomerSession
+ */
+ private $customerSession;
/**
* @inheritDoc
*/
protected function setUp(): void
{
- $this->_objectManager = Bootstrap::getObjectManager();
- $this->_persistentSession = $this->_objectManager->get(\Magento\Persistent\Helper\Session::class);
- $this->_customerSession = $this->_objectManager->get(\Magento\Customer\Model\Session::class);
- $this->_model = $this->_objectManager->create(
- SynchronizePersistentOnLoginObserver::class,
- [
- 'persistentSession' => $this->_persistentSession,
- 'customerSession' => $this->_customerSession
- ]
- );
- /** @var CustomerRepositoryInterface $customerRepository */
- $customerRepository = $this->_objectManager->create(CustomerRepositoryInterface::class);
- $this->customer = $customerRepository->getById(1);
+ parent::setUp();
+
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->persistentSessionHelper = $this->objectManager->get(PersistentSessionHelper::class);
+ $this->model = $this->objectManager->get(SynchronizePersistentOnLoginObserver::class);
+ $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
+ $this->persistentSessionFactory = $this->objectManager->get(SessionFactory::class);
+ $this->cookieManager = $this->objectManager->get(CookieManagerInterface::class);
+ $this->customerSession = $this->objectManager->get(CustomerSession::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->persistentSessionHelper->setRememberMeChecked(null);
+ $this->customerSession->logout();
+
+ parent::tearDown();
}
/**
* Test that persistent session is created on customer login
+ *
+ * @return void
*/
public function testSynchronizePersistentOnLogin(): void
{
- $sessionModel = $this->_objectManager->create(Session::class);
- $sessionModel->loadByCustomerId($this->customer->getId());
+ $customer = $this->customerRepository->get('customer@example.com');
+ $sessionModel = $this->persistentSessionFactory->create();
+ $sessionModel->loadByCustomerId($customer->getId());
$this->assertNull($sessionModel->getCustomerId());
- $event = new Event();
- $observer = new Observer(['event' => $event]);
- $event->setData('customer', $this->customer);
- $this->_persistentSession->setRememberMeChecked(true);
- $this->_model->execute($observer);
- // check that persistent session has been stored for Customer
- /** @var Session $sessionModel */
- $sessionModel = $this->_objectManager->create(Session::class);
- $sessionModel->loadByCustomerId($this->customer->getId());
- $this->assertEquals($this->customer->getId(), $sessionModel->getCustomerId());
+ $this->persistentSessionHelper->setRememberMeChecked(true);
+ $this->customerSession->loginById($customer->getId());
+ $sessionModel = $this->persistentSessionFactory->create();
+ $sessionModel->loadByCustomerId($customer->getId());
+ $this->assertEquals($customer->getId(), $sessionModel->getCustomerId());
}
/**
* Test that expired persistent session is renewed on customer login
+ *
+ * @return void
*/
public function testExpiredPersistentSessionShouldBeRenewedOnLogin(): void
{
+ $customer = $this->customerRepository->get('customer@example.com');
$lastUpdatedAt = (new DateTime('-1day'))->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i:s');
- /** @var Session $sessionModel */
- $sessionModel = $this->_objectManager->create(SessionFactory::class)->create();
- $sessionModel->setCustomerId($this->customer->getId());
+ $sessionModel = $this->persistentSessionFactory->create();
+ $sessionModel->setCustomerId($customer->getId());
$sessionModel->setUpdatedAt($lastUpdatedAt);
$sessionModel->save();
- $event = new Event();
- $observer = new Observer(['event' => $event]);
- $event->setData('customer', $this->customer);
- $this->_persistentSession->setRememberMeChecked(true);
- $this->_model->execute($observer);
- /** @var Session $sessionModel */
- $sessionModel = $this->_objectManager->create(Session::class);
- $sessionModel->loadByCustomerId(1);
+ $this->persistentSessionHelper->setRememberMeChecked(true);
+ $this->customerSession->loginById($customer->getId());
+ $sessionModel = $this->persistentSessionFactory->create();
+ $sessionModel->loadByCustomerId($customer->getId());
$this->assertGreaterThan($lastUpdatedAt, $sessionModel->getUpdatedAt());
}
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
+ * @magentoConfigFixture current_store persistent/options/enabled 0
+ *
+ * @return void
+ */
+ public function testDisabledPersistentSession(): void
+ {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->customerSession->loginById($customer->getId());
+ $this->assertNull($this->cookieManager->getCookie(Session::COOKIE_NAME));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/lifetime 0
+ *
+ * @return void
+ */
+ public function testDisabledPersistentSessionLifetime(): void
+ {
+ $customer = $this->customerRepository->get('customer@example.com');
+ $this->customerSession->loginById($customer->getId());
+ $session = $this->persistentSessionFactory->create()->setLoadExpired()->loadByCustomerId($customer->getId());
+ $this->assertNull($session->getId());
+ $this->assertNull($this->cookieManager->getCookie(Session::COOKIE_NAME));
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php
index 2bf97fdb4953f..293f1d1890d92 100644
--- a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php
@@ -3,54 +3,77 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Persistent\Observer;
+use Magento\Customer\Model\Session as CustomerSession;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Persistent\Model\SessionFactory;
+use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
+
/**
+ * Test for synchronize persistent on logout observer
+ *
+ * @see \Magento\Persistent\Observer\SynchronizePersistentOnLogoutObserver
* @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoAppArea frontend
+ * @magentoDbIsolation enabled
*/
-class SynchronizePersistentOnLogoutObserverTest extends \PHPUnit\Framework\TestCase
+class SynchronizePersistentOnLogoutObserverTest extends TestCase
{
- /**
- * @var \Magento\Framework\ObjectManagerInterface
- */
- protected $_objectManager;
+ /** @var ObjectManagerInterface */
+ private $objectManager;
+
+ /** @var CustomerSession */
+ private $customerSession;
+
+ /** @var SessionFactory */
+ private $sessionFactory;
/**
- * @var \Magento\Customer\Model\Session
+ * @inheritdoc
*/
- protected $_customerSession;
-
protected function setUp(): void
{
- $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $this->_customerSession = $this->_objectManager->get(\Magento\Customer\Model\Session::class);
+ parent::setUp();
+
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->customerSession = $this->objectManager->get(CustomerSession::class);
+ $this->sessionFactory = $this->objectManager->get(SessionFactory::class);
}
/**
* @magentoConfigFixture current_store persistent/options/enabled 1
* @magentoConfigFixture current_store persistent/options/logout_clear 1
- * @magentoAppArea frontend
- * @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testSynchronizePersistentOnLogout()
+ public function testSynchronizePersistentOnLogout(): void
{
- $this->_customerSession->loginById(1);
-
- // check that persistent session has been stored for Customer
- /** @var \Magento\Persistent\Model\Session $sessionModel */
- $sessionModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Persistent\Model\Session::class
- );
+ $this->customerSession->loginById(1);
+ $sessionModel = $this->sessionFactory->create();
$sessionModel->loadByCookieKey();
$this->assertEquals(1, $sessionModel->getCustomerId());
-
- $this->_customerSession->logout();
-
- /** @var \Magento\Persistent\Model\Session $sessionModel */
- $sessionModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Persistent\Model\Session::class
- );
+ $this->customerSession->logout();
+ $sessionModel = $this->sessionFactory->create();
$sessionModel->loadByCookieKey();
$this->assertNull($sessionModel->getCustomerId());
}
+
+ /**
+ * @magentoConfigFixture current_store persistent/options/enabled 1
+ * @magentoConfigFixture current_store persistent/options/logout_clear 0
+ *
+ * @return void
+ */
+ public function testSynchronizePersistentOnLogoutDisabled(): void
+ {
+ $this->customerSession->loginById(1);
+ $this->customerSession->logout();
+ $sessionModel = $this->sessionFactory->create();
+ $sessionModel->loadByCookieKey();
+ $this->assertEquals(1, $sessionModel->getCustomerId());
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php
new file mode 100644
index 0000000000000..581ddb35e3678
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php
@@ -0,0 +1,11 @@
+requireDataFixture('Magento/Customer/_files/customer_address_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
new file mode 100644
index 0000000000000..a2c68ad9b7f2a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php
@@ -0,0 +1,19 @@
+requireDataFixture('Magento/Checkout/_files/quote_with_customer_without_address.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var SessionFactory $persistentSessionFactory */
+$persistentSessionFactory = $objectManager->get(SessionFactory::class);
+$session = $persistentSessionFactory->create();
+$session->setCustomerId(1)->save();
+$session->setPersistentCookie(10000, '');
diff --git a/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php
new file mode 100644
index 0000000000000..252b3f4be7079
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php
@@ -0,0 +1,17 @@
+get(SessionFactory::class);
+$sessionFactory->create()->deleteByCustomerId(1);
+
+Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_customer_without_address_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_with_store_rollback.php b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_with_store_rollback.php
new file mode 100644
index 0000000000000..59aa4bda3872a
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ProductAlert/_files/product_alert_with_store_rollback.php
@@ -0,0 +1,41 @@
+requireDataFixture('Magento/Customer/_files/customer_for_second_store_rollback.php');
+Resolver::getInstance()->requireDataFixture(
+ 'Magento/Catalog/_files/product_simple_out_of_stock_without_categories_rollback.php'
+);
+
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var CustomerRegistry $customerRegistry */
+$customerRegistry = Bootstrap::getObjectManager()->create(CustomerRegistry::class);
+$customer = $customerRegistry->remove(1);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->create(ProductRepositoryInterface::class);
+try {
+ $product = $productRepository->deleteById('simple');
+} catch (\Exception $e) {
+ // product already removed
+}
+/** @var Magento\Store\Model\Store $store */
+$store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class);
+$store->load('fixture_second_store');
+if ($store->getId()) {
+ $store->delete();
+}
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
index f16986a3f2422..f2ae33ee85093 100644
--- a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php
@@ -5,28 +5,43 @@
*/
namespace Magento\Quote\Observer\Frontend\Quote\Address;
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Customer\Model\Customer;
+use Magento\Customer\Model\CustomerRegistry;
+use Magento\Customer\Model\Group;
+use Magento\Framework\Event\Observer;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Quote\Model\Quote;
+use Magento\Quote\Model\Quote\Address\Total;
+use Magento\Quote\Model\Shipping;
+use Magento\Quote\Model\ShippingAssignment;
use Magento\TestFramework\Helper\Bootstrap;
+use PHPUnit\Framework\TestCase;
-class CollectTotalsObserverTest extends \PHPUnit\Framework\TestCase
+/**
+ * Test for \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver.
+ */
+class CollectTotalsObserverTest extends TestCase
{
+ private const STUB_CUSTOMER_EMAIL = 'customer@example.com';
+
/**
- * @var \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver
+ * @var CollectTotalsObserver
*/
- protected $model;
+ private $model;
/**
- * Object Manager
- *
- * @var \Magento\Framework\ObjectManagerInterface
+ * @var ObjectManagerInterface
*/
private $objectManager;
+ /**
+ * @inheridoc
+ */
protected function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
- $this->model = $this->objectManager->create(
- \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver::class
- );
+ $this->model = $this->objectManager->create(CollectTotalsObserver::class);
}
/**
@@ -37,37 +52,37 @@ protected function setUp(): void
*
* @covers \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver::execute
*/
- public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGroupChange()
+ public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGroupChange(): void
{
- /** @var \Magento\Framework\ObjectManagerInterface $objectManager */
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var ObjectManagerInterface $objectManager */
+ $objectManager = Bootstrap::getObjectManager();
- /** @var $customer \Magento\Customer\Model\Customer */
- $customer = $objectManager->create(\Magento\Customer\Model\Customer::class);
+ /** @var $customer Customer */
+ $customer = $objectManager->create(Customer::class);
$customer->load(1);
$customer->setDisableAutoGroupChange(1);
$customer->setGroupId(2);
$customer->save();
- /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */
- $customerRepository = $objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class);
+ /** @var CustomerRepositoryInterface $customerRepository */
+ $customerRepository = $objectManager->create(CustomerRepositoryInterface::class);
$customerData = $customerRepository->getById($customer->getId());
- /** @var $quote \Magento\Quote\Model\Quote */
- $quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var $quote Quote */
+ $quote = $objectManager->create(Quote::class);
$quote->load('test01', 'reserved_order_id');
$quote->setCustomer($customerData);
$quoteAddress = $quote->getBillingAddress();
- $shippingAssignment = $this->objectManager->create(\Magento\Quote\Model\ShippingAssignment::class);
- $shipping = $this->objectManager->create(\Magento\Quote\Model\Shipping::class);
+ $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shipping = $this->objectManager->create(Shipping::class);
$shipping->setAddress($quoteAddress);
$shippingAssignment->setShipping($shipping);
- /** @var \Magento\Quote\Model\Quote\Address\Total $total */
- $total = $this->objectManager->create(\Magento\Quote\Model\Quote\Address\Total::class);
+ /** @var Total $total */
+ $total = $this->objectManager->create(Total::class);
$eventObserver = $objectManager->create(
- \Magento\Framework\Event\Observer::class,
+ Observer::class,
['data' => [
'quote' => $quote,
'shipping_assignment' => $shippingAssignment,
@@ -88,51 +103,80 @@ public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGr
*
* @covers \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver::execute
*/
- public function testChangeQuoteCustomerGroupIdForCustomerWithEnabledAutomaticGroupChange()
+ public function testChangeQuoteCustomerGroupIdForCustomerWithEnabledAutomaticGroupChange(): void
{
- /** @var \Magento\Framework\ObjectManagerInterface $objectManager */
- $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+ /** @var ObjectManagerInterface $objectManager */
+ $objectManager = Bootstrap::getObjectManager();
- /** @var $customer \Magento\Customer\Model\Customer */
- $customer = $objectManager->create(\Magento\Customer\Model\Customer::class);
+ /** @var $customer Customer */
+ $customer = $objectManager->create(Customer::class);
$customer->load(1);
$customer->setDisableAutoGroupChange(0);
$customer->setGroupId(2);
$customer->save();
- /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */
- $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class);
+ /** @var CustomerRegistry $customerRegistry */
+ $customerRegistry = $objectManager->get(CustomerRegistry::class);
$customerRegistry->remove($customer->getId());
- /** @var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */
- $customerRepository = $objectManager->create(\Magento\Customer\Api\CustomerRepositoryInterface::class);
+ /** @var CustomerRepositoryInterface $customerRepository */
+ $customerRepository = $objectManager->create(CustomerRepositoryInterface::class);
$customerData = $customerRepository->getById($customer->getId());
- /** @var $quote \Magento\Quote\Model\Quote */
- $quote = $objectManager->create(\Magento\Quote\Model\Quote::class);
+ /** @var $quote Quote */
+ $quote = $objectManager->create(Quote::class);
$quote->load('test01', 'reserved_order_id');
$quote->setCustomer($customerData);
$quoteAddress = $quote->getBillingAddress();
- $shippingAssignment = $this->objectManager->create(\Magento\Quote\Model\ShippingAssignment::class);
- $shipping = $this->objectManager->create(\Magento\Quote\Model\Shipping::class);
+ $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shipping = $this->objectManager->create(Shipping::class);
$shipping->setAddress($quoteAddress);
$shippingAssignment->setShipping($shipping);
- /** @var \Magento\Quote\Model\Quote\Address\Total $total */
- $total = $this->objectManager->create(\Magento\Quote\Model\Quote\Address\Total::class);
+ /** @var Total $total */
+ $total = $this->objectManager->create(Total::class);
$eventObserver = $objectManager->create(
- \Magento\Framework\Event\Observer::class,
- ['data' => [
- 'quote' => $quote,
- 'shipping_assignment' => $shippingAssignment,
- 'total' => $total
- ]
- ]
+ Observer::class,
+ ['data' => ['quote' => $quote, 'shipping_assignment' => $shippingAssignment, 'total' => $total]]
+ );
+ $this->model->execute($eventObserver);
+
+ $this->assertEquals(2, $quote->getCustomer()->getGroupId());
+ }
+
+ /**
+ * Dispatch event with guest quote and check that email will not be override to null when auto group assign enabled
+ *
+ * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1
+ *
+ * @return void
+ */
+ public function testQuoteCustomerEmailNotChanged(): void
+ {
+ // prepare quote for guest
+ $quote = $this->objectManager->create(Quote::class);
+ $quote->setCustomerId(null)
+ ->setCustomerEmail(self::STUB_CUSTOMER_EMAIL)
+ ->setCustomerIsGuest(true)
+ ->setCustomerGroupId(Group::NOT_LOGGED_IN_ID);
+
+ $quoteAddress = $quote->getBillingAddress();
+
+ $shippingAssignment = $this->objectManager->create(ShippingAssignment::class);
+ $shipping = $this->objectManager->create(Shipping::class);
+ $shipping->setAddress($quoteAddress);
+ $shippingAssignment->setShipping($shipping);
+ /** @var Total $total */
+ $total = $this->objectManager->create(Total::class);
+
+ $eventObserver = $this->objectManager->create(
+ Observer::class,
+ ['data' => ['quote' => $quote, 'shipping_assignment' => $shippingAssignment, 'total' => $total]]
);
$this->model->execute($eventObserver);
- $this->assertEquals(1, $quote->getCustomer()->getGroupId());
+ $this->assertEquals(self::STUB_CUSTOMER_EMAIL, $quote->getCustomerEmail());
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Reports/_files/recently_compared_out_of_stock_product_rollback.php b/dev/tests/integration/testsuite/Magento/Reports/_files/recently_compared_out_of_stock_product_rollback.php
index 677bfb32cd8e9..f3d31bee387f6 100644
--- a/dev/tests/integration/testsuite/Magento/Reports/_files/recently_compared_out_of_stock_product_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Reports/_files/recently_compared_out_of_stock_product_rollback.php
@@ -7,5 +7,5 @@
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
-Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/out_of_stock_product_with_category.php');
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/out_of_stock_product_with_category_rollback.php');
Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Reports/_files/recently_viewed_disabled_product_by_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Reports/_files/recently_viewed_disabled_product_by_customer_rollback.php
index f3dedf0a35d96..5c5ad143ac77f 100644
--- a/dev/tests/integration/testsuite/Magento/Reports/_files/recently_viewed_disabled_product_by_customer_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Reports/_files/recently_viewed_disabled_product_by_customer_rollback.php
@@ -43,3 +43,6 @@
$session->logout();
$config->setValue('reports/options/enabled', $originalValue);
}
+
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/second_product_simple_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php
new file mode 100644
index 0000000000000..df5f5f8336303
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php
@@ -0,0 +1,80 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->page = $this->objectManager->get(PageFactory::class)->create();
+ }
+
+ /**
+ * @return void
+ */
+ public function testMyProductReviewsLink(): void
+ {
+ $this->preparePage();
+ $block = $this->page->getLayout()->getBlock('customer-account-navigation-product-reviews-link');
+ $this->assertNotFalse($block);
+ $html = $block->toHtml();
+ $this->assertStringContainsString('/review/customer/', $html);
+ $this->assertEquals((string)__('My Product Reviews'), strip_tags($html));
+ }
+
+ /**
+ * @magentoConfigFixture current_store catalog/review/active 0
+ *
+ * @return void
+ */
+ public function testMyProductReviewsLinkDisabled(): void
+ {
+ $this->preparePage();
+ $block = $this->page->getLayout()->getBlock('customer-account-navigation-product-reviews-link');
+ $this->assertFalse($block);
+ }
+
+ /**
+ * Prepare page before render
+ *
+ * @return void
+ */
+ private function preparePage(): void
+ {
+ $this->page->addHandle([
+ 'default',
+ 'customer_account',
+ ]);
+ $this->page->getLayout()->generateXml();
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php
new file mode 100644
index 0000000000000..24cb2fe76a6d4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php
@@ -0,0 +1,135 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(ListCustomer::class)
+ ->setTemplate('Magento_Review::customer/list.phtml');
+ $this->collectionFactory = $this->objectManager->get(CollectionFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Review/_files/customer_review_with_rating.php
+ *
+ * @return void
+ */
+ public function testCustomerProductReviewsGrid(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $review = $this->collectionFactory->create()->addCustomerFilter(1)->addReviewSummary()->getFirstItem();
+ $this->assertNotNull($review->getReviewId());
+ $blockHtml = $this->block->toHtml();
+ $createdDate = $this->block->dateFormat($review->getReviewCreatedAt());
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//td[contains(@class, 'date') and contains(text(), '%s')]", $createdDate),
+ $blockHtml
+ ),
+ sprintf('Created date wasn\'t found or not equals to %s.', $createdDate)
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//td[contains(@class, 'item')]//a[contains(text(), '%s')]", $review->getName()),
+ $blockHtml
+ ),
+ 'Product name wasn\'t found.'
+ );
+ $rating = $review->getSum() / $review->getCount();
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//td[contains(@class, 'summary')]//span[contains(text(), '%s%%')]", $rating),
+ $blockHtml
+ ),
+ sprintf('Rating wasn\'t found or not equals to %s%%.', $rating)
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//td[contains(@class, 'description') and contains(text(), '%s')]", $review->getDetail()),
+ $blockHtml
+ ),
+ 'Review description wasn\'t found.'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//td[contains(@class, 'actions')]//a[contains(@href, '%s')]/span[contains(text(), '%s')]",
+ $this->block->getReviewUrl($review),
+ __('See Details')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button wasn\'t found.', __('See Details'))
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testCustomerWithoutReviews(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $this->assertStringContainsString(
+ (string)__('You have submitted no reviews.'),
+ strip_tags($this->block->toHtml())
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php
new file mode 100644
index 0000000000000..31a342ad8ac54
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php
@@ -0,0 +1,138 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->collectionFactory = $this->objectManager->get(CollectionFactory::class);
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(View::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Review/_files/customer_review_with_rating.php
+ *
+ * @return void
+ */
+ public function testCustomerProductReviewBlock(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $review = $this->collectionFactory->create()->addCustomerFilter(1)->getFirstItem();
+ $this->assertNotNull($review->getReviewId());
+ $blockHtml = $this->block->setReviewId($review->getReviewId())->toHtml();
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//div[contains(@class, 'product-info')]/h2[contains(text(), '%s')]", $review->getName()),
+ $blockHtml
+ ),
+ 'Product name wasn\'t found.'
+ );
+ $ratings = $this->block->getRating();
+ $this->assertCount(2, $ratings);
+ foreach ($ratings as $rating) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//div[contains(@class, 'rating-summary')]//span[contains(text(), '%s')]"
+ . "/../..//span[contains(text(), '%s%%')]",
+ $rating->getRatingCode(),
+ $rating->getPercent()
+ ),
+ $blockHtml
+ ),
+ sprintf('Rating %s was not found or not equals to %s.', $rating->getRatingCode(), $rating->getPercent())
+ );
+ }
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//div[contains(@class, 'review-title') and contains(text(), '%s')]", $review->getTitle()),
+ $blockHtml
+ ),
+ 'Review title wasn\'t found.'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf("//div[contains(@class, 'review-content') and contains(text(), '%s')]", $review->getDetail()),
+ $blockHtml
+ ),
+ 'Review description wasn\'t found.'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//div[contains(@class, 'review-date') and contains(text(), '%s')]/time[contains(text(), '%s')]",
+ __('Submitted on'),
+ $this->block->dateFormat($review->getCreatedAt())
+ ),
+ $blockHtml
+ ),
+ 'Created date wasn\'t found.'
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@href, '/review/customer/')]/span[contains(text(), '%s')]",
+ __('Back to My Reviews')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button wasn\'t found.', __('Back to My Reviews'))
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php
new file mode 100644
index 0000000000000..b26b71803848f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php
@@ -0,0 +1,69 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->session = $this->objectManager->get(Quote::class);
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Grid::class);
+ $this->layout->createBlock(Items::class)->setChild('items_grid', $this->block);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ *
+ * @return void
+ */
+ public function testGetItems(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $this->session->setQuoteId($quote->getId());
+ $items = $this->block->getItems();
+ $this->assertCount(1, $items);
+ $this->assertEquals('simple2', reset($items)->getSku());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php
new file mode 100644
index 0000000000000..291fda6e2494f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php
@@ -0,0 +1,80 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Cart::class);
+ $this->session = $this->objectManager->get(Quote::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->session->clearStorage();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ *
+ * @return void
+ */
+ public function testGetItemCollection(): void
+ {
+ $this->session->setCustomerId(1);
+ $items = $this->block->getItemCollection();
+ $this->assertCount(1, $items);
+ $this->assertEquals('simple2', reset($items)->getSku());
+ }
+
+ /**
+ * @return void
+ */
+ public function testClearShoppingCartButton(): void
+ {
+ $confirmation = __('Are you sure you want to delete all items from shopping cart?');
+ $button = $this->block->getChildBlock('empty_customer_cart_button');
+ $this->assertEquals(sprintf("order.clearShoppingCart('%s')", $confirmation), $button->getOnclick());
+ $this->assertEquals(__('Clear Shopping Cart'), $button->getLabel());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php
new file mode 100644
index 0000000000000..1b5772cec66de
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php
@@ -0,0 +1,86 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Items::class);
+ $this->orderFactory = $this->objectManager->get(OrderFactory::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->invoiceCollectionFactory = $this->objectManager->get(CollectionFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('current_invoice');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/invoice.php
+ *
+ * @return void
+ */
+ public function testGetUpdateButtonHtml(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $invoice = $this->invoiceCollectionFactory->create()->setOrderFilter($order)->setPageSize(1)->getFirstItem();
+ $this->registry->unregister('current_invoice');
+ $this->registry->register('current_invoice', $invoice);
+ $this->block->toHtml();
+ $button = $this->block->getChildBlock('update_button');
+ $this->assertEquals((string)__('Update Qty\'s'), (string)$button->getLabel());
+ $this->assertStringContainsString(
+ sprintf('sales/index/updateQty/order_id/%u/', (int)$order->getEntityId()),
+ $button->getOnClick()
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php
new file mode 100644
index 0000000000000..a78c221cb5f84
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php
@@ -0,0 +1,115 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->objectManager->addSharedInstance(
+ $this->objectManager->get(AuthorizationMock::class),
+ Authorization::class
+ );
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->orderFactory = $this->objectManager->get(OrderFactory::class);
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->registry->unregister('sales_order');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testInvoiceButton(): void
+ {
+ $this->registerOrder('100000001');
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ '//button[@id=\'order_invoice\']',
+ $this->layout->createBlock(View::class)->getButtonsHtml()
+ )
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php
+ *
+ * @return void
+ */
+ public function testInvoiceButtonIsNotVisible(): void
+ {
+ $this->registerOrder('100000001');
+ $this->assertEmpty(
+ Xpath::getElementsCountForXpath(
+ '//button[@id=\'order_invoice\']',
+ $this->layout->createBlock(View::class)->getButtonsHtml()
+ )
+ );
+ }
+
+ /**
+ * Register order
+ *
+ * @param OrderInterface $order
+ * @return void
+ */
+ private function registerOrder(string $orderIncrementId): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId($orderIncrementId);
+ $this->registry->unregister('sales_order');
+ $this->registry->register('sales_order', $order);
+ }
+}
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
new file mode 100644
index 0000000000000..b6aa44bac1c4d
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php
@@ -0,0 +1,301 @@
+layout = $this->_objectManager->get(LayoutInterface::class);
+ $this->getQuoteByReservedOrderId = $this->_objectManager->get(GetQuoteByReservedOrderId::class);
+ $this->session = $this->_objectManager->get(Quote::class);
+ $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
+ $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->quoteIdsToRemove[] = $this->session->getQuote()->getId();
+ foreach ($this->quoteIdsToRemove as $quoteId) {
+ try {
+ $this->quoteRepository->delete($this->quoteRepository->get($quoteId));
+ } catch (NoSuchEntityException $e) {
+ //do nothing
+ }
+ }
+
+ $this->session->clearStorage();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @dataProvider responseFlagsProvider
+ *
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ *
+ * @param bool $asJson
+ * @param bool $asJsVarname
+ * @return void
+ */
+ public function testAddProductToOrderFromShoppingCart(bool $asJson, bool $asJsVarname): void
+ {
+ $oldQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $params = $this->hydrateParams([
+ 'json' => $asJson,
+ 'as_js_varname' => $asJsVarname,
+ ]);
+ $post = $this->hydratePost([
+ 'sidebar' => [
+ 'add_cart_item' => [
+ $oldQuote->getItemsCollection()->getFirstItem()->getId() => 1,
+ ],
+ ],
+ ]);
+
+ $this->dispatchWitParams($params, $post);
+
+ $this->checkHandles(explode(',', $params['block']), $asJson);
+ $this->checkQuotes($oldQuote, 'simple2');
+
+ if ($asJsVarname) {
+ $this->assertRedirect($this->stringContains('sales/order_create/showUpdateResult'));
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function responseFlagsProvider(): array
+ {
+ return [
+ 'as_json' => [
+ 'as_json' => true,
+ 'as_js_varname' => false,
+ ],
+ 'as_plain' => [
+ 'as_json' => false,
+ 'as_js_varname' => true,
+ ],
+ ];
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ *
+ * @return void
+ */
+ public function testRemoveProductFromShoppingCart(): void
+ {
+ $oldQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $post = $this->hydratePost([
+ 'sidebar' => [
+ 'remove' => [
+ $oldQuote->getItemsCollection()->getFirstItem()->getId() => 'cart',
+ ],
+ ],
+ ]);
+ $params = $this->hydrateParams();
+
+ $this->dispatchWitParams($params, $post);
+
+ $this->checkHandles(explode(',', $params['block']));
+ $this->checkQuotes($oldQuote);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php
+ *
+ * @return void
+ */
+ public function testClearShoppingCart(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address');
+ $post = $this->hydratePost([
+ 'sidebar' => [
+ 'empty_customer_cart' => '1',
+ ],
+ ]);
+ $params = $this->hydrateParams();
+
+ $this->dispatchWitParams($params, $post);
+
+ $this->checkHandles(explode(',', $params['block']));
+ $this->assertEmpty($quote->getItemsCollection(false)->getItems());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Checkout/_files/inactive_quote_with_customer.php
+ *
+ * @return void
+ */
+ public function testMoveFromOrderToShoppingCart(): void
+ {
+ $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_inactive_quote');
+ $this->session->setQuoteId($quote->getId());
+ $post = $this->hydratePost([
+ 'update_items' => '1',
+ 'item' => [
+ $quote->getItemsCollection()->getFirstItem()->getId() => [
+ 'qty' => '1',
+ 'use_discount' => '1',
+ 'action' => 'cart',
+ ],
+ ],
+ ]);
+ $params = $this->hydrateParams(['blocks' => null]);
+ $this->dispatchWitParams($params, $post);
+ $customerCart = $this->quoteRepository->getForCustomer(1);
+ $cartItems = $customerCart->getItemsCollection();
+ $this->assertCount(1, $cartItems->getItems());
+ $this->assertEquals('taxable_product', $cartItems->getFirstItem()->getSku());
+ $this->quoteIdsToRemove[] = $customerCart->getId();
+ }
+
+ /**
+ * Check customer quotes
+ *
+ * @param CartInterface $oldQuote
+ * @param string|null $expectedSku
+ * @return void
+ */
+ private function checkQuotes(CartInterface $oldQuote, ?string $expectedSku = null): void
+ {
+ $newQuote = $this->session->getQuote();
+ $oldQuoteItemCollection = $oldQuote->getItemsCollection(false);
+ $this->assertEmpty($oldQuoteItemCollection->getItems());
+ $newQuoteItemsCollection = $newQuote->getItemsCollection(false);
+
+ if ($expectedSku !== null) {
+ $this->assertNotNull($newQuoteItemsCollection->getItemByColumnValue('sku', $expectedSku));
+ } else {
+ $this->assertEmpty($newQuoteItemsCollection->getItems());
+ }
+ }
+
+ /**
+ * Check that all required handles were applied
+ *
+ * @param array $blocks
+ * @param bool $asJson
+ * @return void
+ */
+ private function checkHandles(array $blocks, bool $asJson = true): void
+ {
+ $handles = $this->layout->getUpdate()->getHandles();
+
+ if ($asJson) {
+ $this->assertContains('sales_order_create_load_block_message', $handles);
+ $this->assertContains('sales_order_create_load_block_json', $handles);
+ } else {
+ $this->assertContains('sales_order_create_load_block_plain', $handles);
+ }
+
+ foreach ($blocks as $block) {
+ $this->assertContains(
+ 'sales_order_create_load_block_' . $block,
+ $handles
+ );
+ }
+ }
+
+ /**
+ * Fill post params array to proper state
+ *
+ * @param array $inputArray
+ * @return array
+ */
+ private function hydratePost(array $inputArray = []): array
+ {
+ return array_merge(
+ [
+ 'customer_id' => 1,
+ 'store_id' => $this->storeManager->getStore('default')->getId(),
+ 'sidebar' => [],
+ ],
+ $inputArray
+ );
+ }
+
+ /**
+ * Fill params array to proper state
+ *
+ * @param array $inputArray
+ * @return array
+ */
+ private function hydrateParams(array $inputArray = []): array
+ {
+ return array_merge(
+ [
+ 'json' => true,
+ 'block' => 'sidebar,items,shipping_method,billing_method,totals,giftmessage',
+ 'as_js_varname' => true,
+ ],
+ $inputArray
+ );
+ }
+
+ /**
+ * Dispatch request with params
+ *
+ * @param array $params
+ * @param array $postParams
+ * @return void
+ */
+ private function dispatchWitParams(array $params, array $postParams): void
+ {
+ $this->getRequest()->setMethod(Http::METHOD_POST)
+ ->setPostValue($postParams)
+ ->setParams($params);
+ $this->dispatch('backend/sales/order_create/loadBlock');
+ }
+}
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 726ba697beb12..3c26a53424d81 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
@@ -7,39 +7,34 @@
namespace Magento\Sales\Controller\Adminhtml\Order\Invoice;
-use Magento\Framework\Api\SearchCriteria;
use Magento\Framework\Api\SearchCriteriaBuilder;
-use Magento\Framework\Data\Form\FormKey;
+use Magento\Framework\App\Request\Http;
use Magento\Sales\Api\Data\InvoiceInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Model\OrderRepository;
+use Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory;
use Magento\TestFramework\Mail\Template\TransportBuilderMock;
use Magento\TestFramework\TestCase\AbstractBackendController;
/**
* Abstract backend invoice test.
*/
-class AbstractInvoiceControllerTest extends AbstractBackendController
+abstract class AbstractInvoiceControllerTest extends AbstractBackendController
{
- /**
- * @var TransportBuilderMock
- */
+ /** @var TransportBuilderMock */
protected $transportBuilder;
- /**
- * @var OrderRepository
- */
- protected $orderRepository;
+ /** @var string */
+ protected $resource = 'Magento_Sales::sales_invoice';
- /**
- * @var FormKey
- */
- protected $formKey;
+ /** @var OrderRepository */
+ private $orderRepository;
- /**
- * @var string
- */
- protected $resource = 'Magento_Sales::sales_invoice';
+ /** @var SearchCriteriaBuilder */
+ private $searchCriteriaBuilder;
+
+ /** @var CollectionFactory */
+ private $invoiceCollectionFactory;
/**
* @inheritdoc
@@ -47,46 +42,71 @@ class AbstractInvoiceControllerTest extends AbstractBackendController
protected function setUp(): void
{
parent::setUp();
+
$this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class);
$this->orderRepository = $this->_objectManager->get(OrderRepository::class);
- $this->formKey = $this->_objectManager->get(FormKey::class);
+ $this->searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class);
+ $this->invoiceCollectionFactory = $this->_objectManager->get(CollectionFactory::class);
}
/**
+ * Retrieve order
+ *
* @param string $incrementalId
* @return OrderInterface|null
*/
- protected function getOrder(string $incrementalId)
+ protected function getOrder(string $incrementalId): ?OrderInterface
{
- /** @var SearchCriteria $searchCriteria */
- $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class)
- ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId)
+ $searchCriteria = $this->searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $incrementalId)
->create();
-
$orders = $this->orderRepository->getList($searchCriteria)->getItems();
- /** @var OrderInterface $order */
- $order = reset($orders);
- return $order;
+ return reset($orders);
}
/**
- * @param OrderInterface $order
+ * Get firs order invoice
+ *
+ * @param OrderInterface|int $order
* @return InvoiceInterface
*/
- protected function getInvoiceByOrder(OrderInterface $order): InvoiceInterface
+ protected function getInvoiceByOrder($order): InvoiceInterface
{
- /** @var \Magento\Sales\Model\ResourceModel\Order\Invoice\Collection $invoiceCollection */
- $invoiceCollection = $this->_objectManager->create(
- \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory::class
- )->create();
+ $invoiceCollection = $this->invoiceCollectionFactory->create();
- /** @var InvoiceInterface $invoice */
- $invoice = $invoiceCollection
- ->setOrderFilter($order)
- ->setPageSize(1)
- ->getFirstItem();
+ return $invoiceCollection->setOrderFilter($order)->setPageSize(1)->getFirstItem();
+ }
- return $invoice;
+ /**
+ * Prepare request
+ *
+ * @param array $postParams
+ * @param array $params
+ * @return void
+ */
+ protected function prepareRequest(array $postParams = [], array $params = []): void
+ {
+ $this->getRequest()->setMethod(Http::METHOD_POST);
+ $this->getRequest()->setParams($params);
+ $this->getRequest()->setPostValue($postParams);
+ }
+
+ /**
+ * Normalize post parameters
+ *
+ * @param array $items
+ * @param string $commentText
+ * @param bool $doShipment
+ * @return array
+ */
+ protected function hydratePost(array $items, string $commentText = '', $doShipment = false): array
+ {
+ return [
+ 'invoice' => [
+ 'items' => $items,
+ 'comment_text' => $commentText,
+ 'do_shipment' => $doShipment
+ ],
+ ];
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php
index ee59a55acd9b1..c7711e8897696 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php
@@ -30,10 +30,11 @@ class AddCommentTest extends AbstractInvoiceControllerTest
public function testSendEmailOnAddInvoiceComment(): void
{
$comment = 'Test Invoice Comment';
- $order = $this->prepareRequest(
- [
- 'comment' => ['comment' => $comment, 'is_customer_notified' => true],
- ]
+ $order = $this->getOrder('100000001');
+ $invoice = $this->getInvoiceByOrder($order);
+ $this->prepareRequest(
+ ['comment' => ['comment' => $comment, 'is_customer_notified' => true]],
+ ['id' => $invoice->getEntityId()]
);
$this->dispatch('backend/sales/order_invoice/addComment');
@@ -41,6 +42,7 @@ public function testSendEmailOnAddInvoiceComment(): void
$this->assertStringContainsString($comment, $html);
$message = $this->transportBuilder->getSentMessage();
+ $this->assertNotNull($message);
$subject = __('Update to your %1 invoice', $order->getStore()->getFrontendName())->render();
$messageConstraint = $this->logicalAnd(
new StringContains($order->getCustomerName()),
@@ -55,7 +57,8 @@ public function testSendEmailOnAddInvoiceComment(): void
);
$this->assertEquals($message->getSubject(), $subject);
- $this->assertThat($message->getBody()->getParts()[0]->getRawContent(), $messageConstraint);
+ $bodyParts = $message->getBody()->getParts();
+ $this->assertThat(reset($bodyParts)->getRawContent(), $messageConstraint);
}
/**
@@ -63,7 +66,7 @@ public function testSendEmailOnAddInvoiceComment(): void
*/
public function testAclHasAccess()
{
- $this->prepareRequest(['comment' => ['comment' => 'Comment']]);
+ $this->prepareRequest();
parent::testAclHasAccess();
}
@@ -73,31 +76,8 @@ public function testAclHasAccess()
*/
public function testAclNoAccess()
{
- $this->prepareRequest(['comment' => ['comment' => 'Comment']]);
+ $this->prepareRequest();
parent::testAclNoAccess();
}
-
- /**
- * @param array $params
- * @return \Magento\Sales\Api\Data\OrderInterface|null
- */
- private function prepareRequest(array $params = [])
- {
- $order = $this->getOrder('100000001');
- $invoice = $this->getInvoiceByOrder($order);
-
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setParams(
- [
- 'id' => $invoice->getEntityId(),
- 'form_key' => $this->formKey->getFormKey(),
- ]
- );
-
- $data = $params ?? [];
- $this->getRequest()->setPostValue($data);
-
- return $order;
- }
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php
new file mode 100644
index 0000000000000..c8444c827d2e4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php
@@ -0,0 +1,78 @@
+orderFactory = $this->_objectManager->get(OrderFactory::class);
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ }
+
+ /**
+ * @return void
+ */
+ public function testWithNoExistingOrder(): void
+ {
+ $this->dispatchWithOrderId(863521);
+ $expectedMessage = (string)__("The entity that was requested doesn't exist. Verify the entity and try again.");
+ $this->assertSessionMessages($this->containsEqual($this->escaper->escapeHtml($expectedMessage)));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php
+ *
+ * @return void
+ */
+ public function testCanNotInvoice(): void
+ {
+ $expectedMessage = __('The order does not allow an invoice to be created.');
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->dispatchWithOrderId((int)$order->getEntityId());
+ $this->assertSessionMessages($this->containsEqual((string)$expectedMessage), MessageInterface::TYPE_ERROR);
+ }
+
+ /**
+ * Dispatch request with order_id param
+ *
+ * @param int $orderId
+ * @return void
+ */
+ private function dispatchWithOrderId(int $orderId): void
+ {
+ $this->getRequest()->setMethod(Http::METHOD_GET)
+ ->setParams(['order_id' => $orderId]);
+ $this->dispatch('backend/sales/order_invoice/new');
+ }
+}
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 2dc5f5adc86d2..d00b4c784110c 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
@@ -7,38 +7,59 @@
namespace Magento\Sales\Controller\Adminhtml\Order\Invoice;
+use Magento\Framework\Escaper;
+use Magento\Sales\Api\Data\InvoiceInterface;
+use Magento\Sales\Api\Data\OrderItemInterface;
+use Magento\Sales\Model\Order;
+use Magento\Sales\Model\ResourceModel\Order\Item;
use PHPUnit\Framework\Constraint\StringContains;
/**
- * Class tests invoice creation in backend.
+ * Class tests invoice creation in admin panel.
+ *
+ * @see \Magento\Sales\Controller\Adminhtml\Order\Invoice\Save
*
* @magentoDbIsolation enabled
* @magentoAppArea adminhtml
- * @magentoDataFixture Magento/Sales/_files/order.php
*/
class SaveTest extends AbstractInvoiceControllerTest
{
+ /** @var string */
+ protected $uri = 'backend/sales/order_invoice/save';
+
+ /** @var Escaper */
+ private $escaper;
+
+ /** @var Item */
+ private $orderItemResource;
+
/**
- * @var string
+ * @inheritdoc
*/
- protected $uri = 'backend/sales/order_invoice/save';
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ $this->orderItemResource = $this->_objectManager->get(Item::class);
+ }
/**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
* @return void
*/
public function testSendEmailOnInvoiceSave(): void
{
- $order = $this->prepareRequest();
+ $order = $this->getOrder('100000001');
+ $itemId = $order->getItemsCollection()->getFirstItem()->getId();
+ $post = $this->hydratePost([$itemId => 2]);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
$this->dispatch('backend/sales/order_invoice/save');
-
- $this->assertSessionMessages(
- $this->equalTo([(string)__('The invoice has been created.')]),
- \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS
- );
- $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId()));
-
$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();
$messageConstraint = $this->logicalAnd(
new StringContains($invoice->getBillingAddress()->getName()),
@@ -49,9 +70,147 @@ public function testSendEmailOnInvoiceSave(): void
"Your Invoice #{$invoice->getIncrementId()} for Order #{$order->getIncrementId()}"
)
);
-
$this->assertEquals($message->getSubject(), $subject);
- $this->assertThat($message->getBody()->getParts()[0]->getRawContent(), $messageConstraint);
+ $bodyParts = $message->getBody()->getParts();
+ $this->assertThat(reset($bodyParts)->getRawContent(), $messageConstraint);
+ }
+
+ /**
+ * @magentoConfigFixture current_store sales_email/invoice/enabled 0
+ *
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testSendEmailOnInvoiceSaveWithDisabledConfig(): void
+ {
+ $order = $this->getOrder('100000001');
+ $post = $this->hydratePost([$order->getItemsCollection()->getFirstItem()->getId() => 2]);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/save');
+ $this->checkSuccess($this->getInvoiceByOrder($order), 2);
+ $this->assertNull($this->transportBuilder->getSentMessage());
+ }
+
+ /**
+ * @dataProvider invoiceDataProvider
+ *
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @param int $invoicedItemsQty
+ * @param string $commentMessage
+ * @param bool $doShipment
+ * @return void
+ */
+ public function testSuccessfulInvoice(
+ int $invoicedItemsQty,
+ string $commentMessage = '',
+ bool $doShipment = false
+ ): void {
+ $order = $this->getOrder('100000001');
+ $post = $this->hydratePost(
+ [$order->getItemsCollection()->getFirstItem()->getId() => $invoicedItemsQty],
+ $commentMessage,
+ $doShipment
+ );
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/save');
+ $this->checkSuccess($this->getInvoiceByOrder($order), $invoicedItemsQty, $commentMessage, $doShipment);
+ }
+
+ /**
+ * @return array
+ */
+ public function invoiceDataProvider(): array
+ {
+ return [
+ 'with_comment_message' => [
+ 'invoiced_items_qty' => 2,
+ 'comment_message' => 'test comment message',
+ ],
+ 'partial_invoice' => [
+ 'invoiced_items_qty' => 1,
+ ],
+ 'with_do_shipment' => [
+ 'invoiced_items_qty' => 2,
+ 'comment_message' => '',
+ 'do_shipment' => true,
+ ],
+ ];
+ }
+
+ /**
+ * @return void
+ */
+ public function testWitNoExistingOrder(): void
+ {
+ $expectedMessage = (string)__('The order no longer exists.');
+ $this->prepareRequest(['order_id' => 899989]);
+ $this->dispatch('backend/sales/order_invoice/save');
+ $this->assertErrorResponse($expectedMessage);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php
+ *
+ * @return void
+ */
+ public function testCanNotInvoiceOrder(): void
+ {
+ $expectedMessage = (string)__('The order does not allow an invoice to be created.');
+ $order = $this->getOrder('100000001');
+ $this->prepareRequest([], ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/save');
+ $this->assertErrorResponse($expectedMessage);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testInvoiceWithoutQty(): void
+ {
+ $expectedMessage = (string)__('The invoice can\'t be created without products. Add products and try again.');
+ $order = $this->getOrder('100000001');
+ $post = $this->hydratePost([$order->getItemsCollection()->getFirstItem()->getId() => '0']);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/save');
+ $this->assertErrorResponse($this->escaper->escapeHtml($expectedMessage));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_configurable_product.php
+ *
+ * @return void
+ */
+ public function testPartialInvoiceWitConfigurableProduct(): void
+ {
+ $order = $this->getOrder('100000001');
+ $post = $this->hydratePost([$order->getItemsCollection()->getFirstItem()->getId() => '1']);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch($this->uri);
+ $this->assertSessionMessages($this->containsEqual((string)__('The invoice has been created.')));
+ $orderItems = $this->getOrderItemsQtyInvoiced((int)$order->getEntityId());
+ $this->assertCount(2, $orderItems);
+ $this->assertEquals(1, (int)$orderItems[0]);
+ $this->assertEquals($orderItems[0], $orderItems[1]);
+ }
+
+ /**
+ * Get order items qty invoiced
+ *
+ * @param int $orderId
+ * @return array
+ */
+ private function getOrderItemsQtyInvoiced(int $orderId): array
+ {
+ $connection = $this->orderItemResource->getConnection();
+ $select = $connection->select()
+ ->from($this->orderItemResource->getMainTable(), OrderItemInterface::QTY_INVOICED)
+ ->where(OrderItemInterface::ORDER_ID . ' = ?', $orderId);
+
+ return $connection->fetchCol($select);
}
/**
@@ -75,23 +234,64 @@ public function testAclNoAccess()
}
/**
- * @param array $params
- * @return \Magento\Sales\Api\Data\OrderInterface|null
+ * Checks that order protect code is not changing after invoice submitting
+ *
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
*/
- private function prepareRequest(array $params = [])
+ public function testOrderProtectCodePreserveAfterInvoiceSave(): void
{
$order = $this->getOrder('100000001');
- $this->getRequest()->setMethod('POST');
- $this->getRequest()->setParams(
- [
- 'order_id' => $order->getEntityId(),
- 'form_key' => $this->formKey->getFormKey(),
- ]
- );
+ $this->prepareRequest([], ['order_id' => $order->getEntityId()]);
+ $protectCode = $order->getProtectCode();
+ $this->dispatch($this->uri);
+ $invoicedOrder = $this->getOrder('100000001');
- $data = $params ?? [];
- $this->getRequest()->setPostValue($data);
+ $this->assertEquals($protectCode, $invoicedOrder->getProtectCode());
+ }
- return $order;
+ /**
+ * Check error response
+ *
+ * @param string $expectedMessage
+ * @return void
+ */
+ private function assertErrorResponse(string $expectedMessage): void
+ {
+ $this->assertRedirect($this->stringContains('sales/order_invoice/new'));
+ $this->assertSessionMessages($this->containsEqual($expectedMessage));
+ }
+
+ /**
+ * Check that invoice was successfully created
+ *
+ * @param InvoiceInterface $invoice
+ * @param int $invoicedItemsQty
+ * @param string|null $commentMessage
+ * @param bool $doShipment
+ * @return void
+ */
+ private function checkSuccess(
+ InvoiceInterface $invoice,
+ int $invoicedItemsQty,
+ ?string $commentMessage = null,
+ bool $doShipment = false
+ ): void {
+ $message = $doShipment ? 'You created the invoice and shipment.' : 'The invoice has been created.';
+ $expectedState = $doShipment ? Order::STATE_COMPLETE : Order::STATE_PROCESSING;
+ $this->assertNotNull($invoice->getEntityId());
+ $this->assertEquals($invoicedItemsQty, (int)$invoice->getTotalQty());
+ $order = $invoice->getOrder();
+ $this->assertEquals($expectedState, $order->getState());
+
+ if ($commentMessage) {
+ $this->assertEquals($commentMessage, $invoice->getCustomerNote());
+ }
+
+ $this->assertRedirect(
+ $this->stringContains(sprintf('sales/order/view/order_id/%u', (int)$order->getEntityId()))
+ );
+ $this->assertSessionMessages($this->containsEqual((string)__($message)));
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php
new file mode 100644
index 0000000000000..5eb554ef937d5
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php
@@ -0,0 +1,66 @@
+orderFactory = $this->_objectManager->get(OrderFactory::class);
+ $this->session = $this->_objectManager->get(Session::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->session->getInvoiceItemQtys(true);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testExecute(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->session->setInvoiceItemQtys('test');
+ $this->getRequest()->setMethod(Http::METHOD_GET)->setParams(['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/start');
+ $this->assertRedirect($this->stringContains('sales/order_invoice/new'));
+ $this->assertNull($this->session->getInvoiceItemQtys());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php
new file mode 100644
index 0000000000000..2b91c5d04fd6f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php
@@ -0,0 +1,123 @@
+json = $this->_objectManager->get(SerializerInterface::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testSuccess(): void
+ {
+ $order = $this->getOrder('100000001');
+ $itemId = $order->getItemsCollection()->getFirstItem()->getId();
+ $qtyToInvoice = 1;
+ $invoicedItemsXpath = sprintf(
+ "//input[contains(@class, 'qty-input') and @name='invoice[items][%u]' and @value='%u']",
+ $itemId,
+ $qtyToInvoice
+ );
+ $post = $this->hydratePost([$itemId => $qtyToInvoice]);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/updateQty');
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath($invoicedItemsXpath, $this->getResponse()->getContent())
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php
+ *
+ * @return void
+ */
+ public function testCanNotInvoice(): void
+ {
+ $order = $this->getOrder('100000001');
+ $itemId = $order->getItemsCollection()->getFirstItem()->getId();
+ $post = $this->hydratePost([$itemId => '1']);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/updateQty');
+ $this->assertErrorResponse('The order does not allow an invoice to be created.');
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testWithoutQty(): void
+ {
+ $order = $this->getOrder('100000001');
+ $itemId = $order->getItemsCollection()->getFirstItem()->getId();
+ $post = $this->hydratePost([$itemId => '0']);
+ $this->prepareRequest($post, ['order_id' => $order->getEntityId()]);
+ $this->dispatch('backend/sales/order_invoice/updateQty');
+ $this->assertErrorResponse(
+ 'The invoice can\'t be created without products. Add products and try again.'
+ );
+ }
+
+ /**
+ * @return void
+ */
+ public function testWithNoExistingOrderId(): void
+ {
+ $post = $this->hydratePost([
+ 'invoice' => [
+ 'items' => [
+ '1' => '3',
+ ],
+ ],
+ ]);
+ $this->prepareRequest($post, ['order_id' => 6543265]);
+ $this->dispatch('backend/sales/order_invoice/updateQty');
+ $this->assertErrorResponse('The order no longer exists.');
+ }
+
+ /**
+ * Check error response
+ *
+ * @param string $expectedMessage
+ * @return void
+ */
+ private function assertErrorResponse(string $expectedMessage): void
+ {
+ $expectedResponse = [
+ 'error' => true,
+ 'message' => (string)__($expectedMessage),
+ ];
+ $response = $this->getResponse()->getContent();
+ $this->assertNotEmpty($response);
+ $this->assertEquals($expectedResponse, $this->json->unserialize($response));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php
new file mode 100644
index 0000000000000..cffdda80cc897
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php
@@ -0,0 +1,139 @@
+checkoutSession = $this->_objectManager->get(CheckoutSession::class);
+ $this->orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class);
+ $this->cookieManager = $this->_objectManager->get(CookieManagerInterface::class);
+ $this->customerSession = $this->_objectManager->get(Session::class);
+ $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $createdQuoteId = $this->checkoutSession->getQuoteId();
+
+ if ($createdQuoteId !== null) {
+ try {
+ $this->quoteRepository->delete($this->quoteRepository->get($createdQuoteId));
+ } catch (NoSuchEntityException $e) {
+ //already deleted
+ }
+ }
+
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDbIsolation disabled
+ *
+ * @magentoDataFixture Magento/Sales/_files/order_by_guest_with_simple_product.php
+ *
+ * @return void
+ */
+ public function testReorderSimpleProduct(): void
+ {
+ $orderIncrementId = 'test_order_1';
+ $order = $this->orderFactory->create()->loadByIncrementId($orderIncrementId);
+ $cookieValue = base64_encode($order->getProtectCode() . ':' . $orderIncrementId);
+ $this->cookieManager->setPublicCookie(Guest::COOKIE_NAME, $cookieValue);
+ $this->dispatchReorderRequest();
+ $this->assertRedirect($this->stringContains('checkout/cart'));
+ $quoteId = $this->checkoutSession->getQuoteId();
+ $this->assertNotNull($quoteId);
+ $quoteItemsCollection = $this->quoteRepository->get((int)$quoteId)->getItemsCollection();
+ $this->assertCount(1, $quoteItemsCollection);
+ $this->assertEquals(
+ $order->getItemsCollection()->getFirstItem()->getSku(),
+ $quoteItemsCollection->getFirstItem()->getSku()
+ );
+ }
+
+ /**
+ * @return void
+ */
+ public function testReorderWithoutParamsAndCookie(): void
+ {
+ $this->dispatchReorderRequest();
+ $this->assertRedirect($this->stringContains('sales/guest/form'));
+ $this->assertSessionMessages(
+ $this->containsEqual((string)__('You entered incorrect data. Please try again.')),
+ MessageInterface::TYPE_ERROR
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testReorderGuestOrderByCustomer(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $this->dispatchReorderRequest();
+ $this->assertRedirect($this->stringContains('sales/order/history'));
+ }
+
+ /**
+ * Dispatch reorder request.
+ *
+ * @return void
+ */
+ private function dispatchReorderRequest(): void
+ {
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->dispatch('sales/guest/reorder/');
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php
index 5a912c2960ab6..d5f252ffdad53 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ViewTest.php
@@ -7,22 +7,71 @@
namespace Magento\Sales\Controller\Guest;
+use Magento\Framework\Stdlib\CookieManagerInterface;
+use Magento\Sales\Api\Data\OrderInterfaceFactory;
+use Magento\Sales\Api\OrderRepositoryInterface;
+use Magento\Sales\Helper\Guest;
use Magento\TestFramework\Request;
use Magento\TestFramework\TestCase\AbstractController;
/**
- * Test for \Magento\Sales\Controller\Guest\View class.
+ * Test for orders and returns controller.
+ *
+ * @see \Magento\Sales\Controller\Guest\View
*/
class ViewTest extends AbstractController
{
+ /** @var CookieManagerInterface */
+ private $cookieManager;
+
+ /** @var OrderInterfaceFactory */
+ private $orderFactory;
+
+ /** @var OrderRepositoryInterface */
+ private $orderRepository;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->cookieManager = $this->_objectManager->get(CookieManagerInterface::class);
+ $this->orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class);
+ $this->orderRepository = $this->_objectManager->get(OrderRepositoryInterface::class);
+ }
+
/**
* Check that controller applied GET requests.
+ *
+ * @return void
*/
- public function testExecuteWithGetRequest()
+ public function testExecuteWithGetRequest(): void
{
$this->getRequest()->setMethod(Request::METHOD_GET);
$this->dispatch('sales/guest/view/');
$this->assertRedirect($this->stringContains('sales/guest/form'));
}
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testExecuteWithWrongCookie(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $order->setProtectCode('0e6640');
+ $this->orderRepository->save($order);
+ $cookieValue = base64_encode('0' . ':' . $order->getIncrementId());
+ $this->cookieManager->setPublicCookie(Guest::COOKIE_NAME, $cookieValue);
+ $this->getRequest()->setMethod(Request::METHOD_GET);
+ $this->dispatch('sales/guest/view/');
+ $this->assertRedirect($this->stringContains('sales/guest/form/'));
+ $this->assertSessionMessages(
+ $this->containsEqual((string)__('You entered incorrect data. Please try again.'))
+ );
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php
new file mode 100644
index 0000000000000..3b32e7238cc76
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php
@@ -0,0 +1,151 @@
+checkoutSession = $this->_objectManager->get(CheckoutSession::class);
+ $this->orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class);
+ $this->customerSession = $this->_objectManager->get(Session::class);
+ $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
+ $this->escaper = $this->_objectManager->get(Escaper::class);
+ $this->versionChecker = $this->_objectManager->get(View::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ if ($this->quote instanceof CartInterface) {
+ $this->quoteRepository->delete($this->quote);
+ }
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_taxable_product.php
+ *
+ * @return void
+ */
+ public function testReorder(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('test_order_with_taxable_product');
+ $this->customerSession->setCustomerId($order->getCustomerId());
+ $this->dispatchReorderRequest((int)$order->getId());
+ $this->assertRedirect($this->stringContains('checkout/cart'));
+ $this->quote = $this->checkoutSession->getQuote();
+ $quoteItemsCollection = $this->quote->getItemsCollection();
+ $this->assertCount(1, $quoteItemsCollection);
+ $this->assertEquals(
+ $order->getItemsCollection()->getFirstItem()->getSku(),
+ $quoteItemsCollection->getFirstItem()->getSku()
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_simple_product.php
+ *
+ * @return void
+ */
+ public function testReorderProductLowQty(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('55555555');
+ $this->customerSession->setCustomerId($order->getCustomerId());
+ $this->dispatchReorderRequest((int)$order->getId());
+ $origMessage = (string)__('The requested qty is not available');
+ $message = $this->escaper->escapeHtml(
+ __('Could not add the product with SKU "%1" to the shopping cart: %2', 'simple-1', $origMessage)
+ );
+ $constraint = $this->logicalOr($this->containsEqual($origMessage), $this->containsEqual($message));
+ $this->assertThat($this->getMessages(MessageInterface::TYPE_ERROR), $constraint);
+ $this->quote = $this->checkoutSession->getQuote();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php
+ *
+ * @return void
+ */
+ public function testReorderByAnotherCustomer(): void
+ {
+ $this->customerSession->setCustomerId(1);
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $this->dispatchReorderRequest((int)$order->getId());
+
+ if ($this->versionChecker->isVersionUpdated()) {
+ $this->assertRedirect($this->stringContains('noroute'));
+ } else {
+ $this->assertRedirect($this->stringContains('sales/order/history'));
+ }
+ }
+
+ /**
+ * Dispatch reorder request.
+ *
+ * @param null|int $orderId
+ * @return void
+ */
+ private function dispatchReorderRequest(?int $orderId = null): void
+ {
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->getRequest()->setParam('order_id', $orderId);
+ $this->dispatch('sales/order/reorder/');
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php
new file mode 100644
index 0000000000000..5a21f551ff1a7
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php
@@ -0,0 +1,106 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->helper = $this->objectManager->get(Reorder::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ $this->customerSession = $this->objectManager->get(Session::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown(): void
+ {
+ $this->customerSession->setCustomerId(null);
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testCanReorderForGuest(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->assertTrue($this->helper->canReorder($order->getId()));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php
+ *
+ * @return void
+ */
+ public function testCanReorderForLoggedCustomer(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $this->customerSession->setCustomerId($order->getCustomerId());
+ $this->assertTrue($this->helper->canReorder($order->getId()));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Sales/_files/order_state_hold.php
+ *
+ * @return void
+ */
+ public function testCanReorderHoldOrderForLoggedCustomer(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->customerSession->setCustomerId(1);
+ $this->assertFalse($this->helper->canReorder($order->getId()));
+ }
+
+ /**
+ * @magentoConfigFixture current_store sales/reorder/allow 0
+ * @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testCanReorderConfigDisabled(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->assertFalse($this->helper->canReorder($order->getId()));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php
index e1cc942d4ae28..3e6b27a7ca622 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Model/AdminOrder/CreateTest.php
@@ -684,7 +684,6 @@ public function testMoveQuoteItemToCart()
public function testGetCustomerCartNewCart()
{
$customerIdFromFixture = 1;
- $customerEmailFromFixture = 'customer@example.com';
/** Preconditions */
/** @var SessionQuote $session */
@@ -693,12 +692,8 @@ public function testGetCustomerCartNewCart()
/** SUT execution */
$customerQuote = $this->model->getCustomerCart();
- self::assertNotEmpty($customerQuote->getId(), 'Quote ID is invalid.');
- self::assertEquals(
- $customerEmailFromFixture,
- $customerQuote->getCustomerEmail(),
- 'Customer data is preserved incorrectly in a newly quote.'
- );
+ self::assertInstanceOf(Quote::class, $customerQuote);
+ self::assertEmpty($customerQuote->getData());
}
/**
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php
new file mode 100644
index 0000000000000..ca102b0fabf89
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php
@@ -0,0 +1,22 @@
+requireDataFixture('Magento/Checkout/_files/customer_quote_ready_for_order.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var CartRepositoryInterface $quoteRepository */
+$quoteRepository = $objectManager->get(CartRepositoryInterface::class);
+/** @var CartManagementInterface $quoteManagement */
+$quoteManagement = $objectManager->get(CartManagementInterface::class);
+
+$quote = $quoteRepository->getActiveForCustomer(1);
+$quoteManagement->placeOrder($quote->getId());
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php
new file mode 100644
index 0000000000000..46cabc2e3fd9b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php
@@ -0,0 +1,33 @@
+get(Registry::class);
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+/** @var OrderInterfaceFactory $orderFactory */
+$orderFactory = $objectManager->get(OrderInterfaceFactory::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$order = $orderFactory->create()->loadByIncrementId('55555555');
+if ($order->getId()) {
+ $orderRepository->delete($order);
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php
new file mode 100644
index 0000000000000..59ec4182ac870
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php
@@ -0,0 +1,30 @@
+requireDataFixture('Magento/Checkout/_files/quote_with_taxable_product_and_customer.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var CartRepositoryInterface $quoteRepository */
+$quoteRepository = $objectManager->get(CartRepositoryInterface::class);
+/** @var CartManagementInterface $quoteManagement */
+$quoteManagement = $objectManager->get(CartManagementInterface::class);
+/** @var PaymentInterface $payment */
+$payment = $objectManager->get(PaymentInterface::class);
+$payment->setMethod('checkmo');
+
+$quote = $quoteRepository->getActiveForCustomer(1);
+$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate');
+$quote->getShippingAddress()->setCollectShippingRates(true);
+$quote->getShippingAddress()->collectShippingRates();
+$quoteRepository->save($quote);
+$quoteManagement->placeOrder($quote->getId(), $payment);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php
new file mode 100644
index 0000000000000..d42f6a1140286
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php
@@ -0,0 +1,35 @@
+get(Registry::class);
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+/** @var OrderInterfaceFactory $orderFactory */
+$orderFactory = $objectManager->get(OrderInterfaceFactory::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$order = $orderFactory->create()->loadByIncrementId('test_order_with_taxable_product');
+if ($order->getId()) {
+ $orderRepository->delete($order);
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+Resolver::getInstance()->requireDataFixture(
+ 'Magento/Checkout/_files/quote_with_taxable_product_and_customer_rollback.php'
+);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php
new file mode 100644
index 0000000000000..c3bab9acca27b
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php
@@ -0,0 +1,31 @@
+requireDataFixture('Magento/Checkout/_files/quote_with_address_saved.php');
+
+$objectManager = Bootstrap::getObjectManager();
+/** @var CartRepositoryInterface $quoteRepository */
+$quoteRepository = $objectManager->get(CartRepositoryInterface::class);
+/** @var CartManagementInterface $quoteManagement */
+$quoteManagement = $objectManager->get(CartManagementInterface::class);
+/** @var PaymentInterface $payment */
+$payment = $objectManager->get(PaymentInterface::class);
+
+$quote = $objectManager->get(GetQuoteByReservedOrderId::class)->execute('test_order_1');
+$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate');
+$quote->getShippingAddress()->setCollectShippingRates(true);
+$quote->getShippingAddress()->collectShippingRates();
+$quoteRepository->save($quote);
+$payment->setMethod('checkmo');
+$quoteManagement->placeOrder($quote->getId(), $payment);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php
new file mode 100644
index 0000000000000..b4ec514d1311e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php
@@ -0,0 +1,33 @@
+get(Registry::class);
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+/** @var OrderInterfaceFactory $orderFactory */
+$orderFactory = $objectManager->get(OrderInterfaceFactory::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$order = $orderFactory->create()->loadByIncrementId('test_order_1');
+if ($order->getId()) {
+ $orderRepository->delete($order);
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
+
+Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_address_saved_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php
new file mode 100644
index 0000000000000..954c23498ec66
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php
@@ -0,0 +1,108 @@
+resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class);
+ $this->initSalesRule();
+ }
+
+ /**
+ * Prepare request
+ *
+ * @return void
+ */
+ private function prepareRequest(): void
+ {
+ $couponList = $this->getCouponsIdList();
+ if (count($couponList)) {
+ $this->getRequest()->setParams(['internal_ids' => $couponList[0]])->setMethod('POST');
+ }
+ }
+
+ /**
+ * Init current sales rule
+ *
+ * @return void
+ */
+ private function initSalesRule(): void
+ {
+ /** @var RuleCollection $collection */
+ $collection = Bootstrap::getObjectManager()->create(RuleCollection::class);
+ $collection->addFieldToFilter('name', 'Rule with coupon list');
+ $this->salesRule = $collection->getFirstItem();
+ }
+
+ /**
+ * Retrieve id list of coupons
+ *
+ * @return array
+ */
+ private function getCouponsIdList(): array
+ {
+ $select = $this->resourceConnection->getConnection()
+ ->select()
+ ->from($this->resourceConnection->getTableName('salesrule_coupon'))
+ ->columns(['coupon_id'])
+ ->where('rule_id=?', $this->salesRule->getId());
+
+ return $this->resourceConnection->getConnection()->fetchCol($select);
+ }
+
+ /**
+ * Test export csv
+ *
+ * @return void
+ */
+ public function testExportCsv(): void
+ {
+ $this->prepareRequest();
+ $this->dispatch($this->uri);
+ $this->assertStringNotContainsString('404 Error', $this->getResponse()->getBody());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php
new file mode 100644
index 0000000000000..d222b064a0d2c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php
@@ -0,0 +1,108 @@
+resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class);
+ $this->initSalesRule();
+ }
+
+ /**
+ * Prepare request
+ *
+ * @return void
+ */
+ private function prepareRequest(): void
+ {
+ $couponList = $this->getCouponsIdList();
+ if (count($couponList)) {
+ $this->getRequest()->setParams(['internal_ids' => $couponList[0]])->setMethod('POST');
+ }
+ }
+
+ /**
+ * Init current sales rule
+ *
+ * @return void
+ */
+ private function initSalesRule(): void
+ {
+ /** @var RuleCollection $collection */
+ $collection = Bootstrap::getObjectManager()->create(RuleCollection::class);
+ $collection->addFieldToFilter('name', 'Rule with coupon list');
+ $this->salesRule = $collection->getFirstItem();
+ }
+
+ /**
+ * Retrieve id list of coupons
+ *
+ * @return array
+ */
+ private function getCouponsIdList(): array
+ {
+ $select = $this->resourceConnection->getConnection()
+ ->select()
+ ->from($this->resourceConnection->getTableName('salesrule_coupon'))
+ ->columns(['coupon_id'])
+ ->where('rule_id=?', $this->salesRule->getId());
+
+ return $this->resourceConnection->getConnection()->fetchCol($select);
+ }
+
+ /**
+ * Test export xml
+ *
+ * @return void
+ */
+ public function testExportCsv(): void
+ {
+ $this->prepareRequest();
+ $this->dispatch($this->uri);
+ $this->assertStringNotContainsString('404 Error', $this->getResponse()->getBody());
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/GenerateFixturesCommandTest.php b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/GenerateFixturesCommandTest.php
index 3ec185e71a1e5..355239f9dc2e7 100644
--- a/dev/tests/integration/testsuite/Magento/Setup/Console/Command/GenerateFixturesCommandTest.php
+++ b/dev/tests/integration/testsuite/Magento/Setup/Console/Command/GenerateFixturesCommandTest.php
@@ -87,6 +87,9 @@ protected function tearDown(): void
{
$this->setIncrement(1);
+ self::restoreFromDb();
+ self::$dbRestored = true;
+
parent::tearDown();
}
diff --git a/dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixtureModelTest.php b/dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixtureModelTest.php
index 2829cffd8d8a7..10e5cf40bd500 100644
--- a/dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixtureModelTest.php
+++ b/dev/tests/integration/testsuite/Magento/Setup/Fixtures/FixtureModelTest.php
@@ -75,6 +75,8 @@ protected function tearDown(): void
$indexer = $this->indexerRegistry->get($indexerId);
$indexer->setScheduled($state);
}
+ self::restoreFromDb();
+ self::$dbRestored = true;
}
public static function setUpBeforeClass(): void
diff --git a/dev/tests/integration/testsuite/Magento/Store/Ui/Component/Listing/Column/Store/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Store/Ui/Component/Listing/Column/Store/OptionsTest.php
new file mode 100644
index 0000000000000..e13c4a427464f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Store/Ui/Component/Listing/Column/Store/OptionsTest.php
@@ -0,0 +1,114 @@
+modelFactory = $objectManager->get(OptionsFactory::class);
+ $this->storeManager = $objectManager->get(StoreManagerInterface::class);
+
+ $this->websiteResource = $objectManager->get(WebsiteResource::class);
+ $this->groupResource = $objectManager->get(GroupResource::class);
+ $this->storeResource = $objectManager->get(StoreResource::class);
+ }
+
+ /**
+ * To option array test with duplicate website, store group, store view names
+ *
+ * @magentoDataFixture Magento/Store/_files/second_website_with_store_group_and_store.php
+ *
+ * @return void
+ */
+ public function testToOptionArray(): void
+ {
+ $website = $this->storeManager->getWebsite('test');
+ $this->websiteResource->save($website->setName(self::DEFAULT_WEBSITE_NAME));
+
+ $storeGroup = current($website->getGroups());
+ $this->groupResource->save($storeGroup->setName(self::DEFAULT_STORE_GROUP_NAME));
+
+ $store = current($website->getStores());
+ $this->storeResource->save($store->setName(self::DEFAULT_STORE_NAME));
+
+ $model = $this->modelFactory->create();
+ $storeIds = [$this->storeManager->getStore('default')->getId(), $store->getId()];
+
+ $this->assertEquals($this->getExpectedOptions($storeIds), $model->toOptionArray());
+ }
+
+ /**
+ * Returns expected options
+ *
+ * @param array $storeIds
+ * @return array
+ */
+ private function getExpectedOptions(array $storeIds): array
+ {
+ $expectedOptions = [];
+ foreach ($storeIds as $storeId) {
+ $expectedOptions[] = [
+ 'label' => self::DEFAULT_WEBSITE_NAME,
+ 'value' => [[
+ 'label' => str_repeat(' ', 4) . self::DEFAULT_STORE_GROUP_NAME,
+ 'value' => [[
+ 'label' => str_repeat(' ', 8) . self::DEFAULT_STORE_NAME,
+ 'value' => $storeId,
+ ]],
+ ]],
+ ];
+ }
+
+ return $expectedOptions;
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore_rollback.php
index 19d064bb79834..0ccfdf0af5c94 100644
--- a/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore_rollback.php
@@ -3,8 +3,13 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
declare(strict_types=1);
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
+use Magento\UrlRewrite\Model\UrlRewrite;
+
/** @var \Magento\Framework\Registry $registry */
$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
@@ -33,5 +38,21 @@
$store->delete();
}
+$urlRewriteCollectionFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ UrlRewriteCollectionFactory::class
+);
+/** @var UrlRewriteCollection $urlRewriteCollection */
+$urlRewriteCollection = $urlRewriteCollectionFactory->create();
+$urlRewriteCollection->addFieldToFilter('store_id', ['gt' => 1]);
+$urlRewrites = $urlRewriteCollection->getItems();
+/** @var UrlRewrite $urlRewrite */
+foreach ($urlRewrites as $urlRewrite) {
+ try {
+ $urlRewrite->delete();
+ } catch (\Exception $exception) {
+ // already removed
+ }
+}
+
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_rollback.php
index 56ba31fad4ed2..a434321189015 100644
--- a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_rollback.php
@@ -4,6 +4,11 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
+use Magento\UrlRewrite\Model\UrlRewrite;
+
/** @var \Magento\Framework\Registry $registry */
$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
@@ -15,6 +20,24 @@
$store->load('fixture_second_store');
if ($store->getId()) {
+ $storeId = $store->getId();
+
+ $urlRewriteCollectionFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ UrlRewriteCollectionFactory::class
+ );
+ /** @var UrlRewriteCollection $urlRewriteCollection */
+ $urlRewriteCollection = $urlRewriteCollectionFactory->create();
+ $urlRewriteCollection->addFieldToFilter('store_id', ['eq' => $storeId]);
+ $urlRewrites = $urlRewriteCollection->getItems();
+ /** @var UrlRewrite $urlRewrite */
+ foreach ($urlRewrites as $urlRewrite) {
+ try {
+ $urlRewrite->delete();
+ } catch (\Exception $exception) {
+ // already removed
+ }
+ }
+
$store->delete();
}
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php
index 3151a76327397..547ce78500f49 100644
--- a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php
@@ -4,24 +4,35 @@
* See COPYING.txt for license details.
*/
declare(strict_types=1);
+
+use Magento\Config\Model\ResourceModel\Config;
+use Magento\Directory\Model\Currency as ModelCurrency;
+use Magento\Directory\Model\ResourceModel\Currency as ResourceCurrency;
+use Magento\Store\Model\ScopeInterface;
+use Magento\Store\Model\Store;
+use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
-$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-$store = $objectManager->create(\Magento\Store\Model\Store::class);
+$objectManager = Bootstrap::getObjectManager();
+$store = $objectManager->create(Store::class);
$storeId = $store->load('fixture_second_store', 'code')->getId();
if ($storeId) {
- $configResource = $objectManager->get(\Magento\Config\Model\ResourceModel\Config::class);
+ $configResource = $objectManager->get(Config::class);
$configResource->deleteConfig(
- \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_DEFAULT,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORES,
+ ModelCurrency::XML_PATH_CURRENCY_DEFAULT,
+ ScopeInterface::SCOPE_STORES,
$storeId
);
$configResource->deleteConfig(
- \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_ALLOW,
- \Magento\Store\Model\ScopeInterface::SCOPE_STORES,
+ ModelCurrency::XML_PATH_CURRENCY_ALLOW,
+ ScopeInterface::SCOPE_STORES,
$storeId
);
}
Resolver::getInstance()->requireDataFixture('Magento/Store/_files/second_store_rollback.php');
+$reflectionClass = new \ReflectionClass(ResourceCurrency::class);
+$staticProperty = $reflectionClass->getProperty('_rateCache');
+$staticProperty->setAccessible(true);
+$staticProperty->setValue(null);
diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/store_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/store_rollback.php
index 8289244d6581a..fa7d18124fdf1 100644
--- a/dev/tests/integration/testsuite/Magento/Store/_files/store_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Store/_files/store_rollback.php
@@ -7,6 +7,9 @@
use Magento\Framework\Registry;
use Magento\Store\Model\Store;
use Magento\TestFramework\Helper\Bootstrap;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollection;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewriteCollectionFactory;
+use Magento\UrlRewrite\Model\ResourceModel\UrlRewrite;
$objectManager = Bootstrap::getObjectManager();
@@ -29,5 +32,23 @@
$store->delete();
}
+/** @var UrlRewriteCollectionFactory $urlRewriteCollectionFactory */
+$urlRewriteCollectionFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+ UrlRewriteCollectionFactory::class
+);
+/** @var UrlRewriteCollection $urlRewriteCollection */
+$urlRewriteCollection = $urlRewriteCollectionFactory->create();
+$urlRewriteCollection
+ ->addFieldToFilter('store_id', ['nin' => [0, 1]]);
+$urlRewrites = $urlRewriteCollection->getItems();
+/** @var UrlRewrite $urlRewrite */
+foreach ($urlRewrites as $urlRewrite) {
+ try {
+ $urlRewrite->delete();
+ } catch (\Exception $exception) {
+ // already removed
+ }
+}
+
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php
index 5d1758f578836..dd715ecc93b0d 100644
--- a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php
+++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php
@@ -116,6 +116,7 @@ public function childProductsDataProvider(): array
],
'expected_data' => [
[
+ 'baseOldPrice' => ['amount' => 150],
'oldPrice' => ['amount' => 150],
'basePrice' => ['amount' => 50],
'finalPrice' => ['amount' => 50],
@@ -123,6 +124,7 @@ public function childProductsDataProvider(): array
'msrpPrice' => ['amount' => null],
],
[
+ 'baseOldPrice' => ['amount' => 150],
'oldPrice' => ['amount' => 150],
'basePrice' => ['amount' => 58.55],
'finalPrice' => ['amount' => 58.55],
@@ -130,6 +132,7 @@ public function childProductsDataProvider(): array
'msrpPrice' => ['amount' => null],
],
[
+ 'baseOldPrice' => ['amount' => 150],
'oldPrice' => ['amount' => 150],
'basePrice' => ['amount' => 75],
'finalPrice' => ['amount' => 75],
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
index 96fc92c3c0ca5..a119b6259b5f6 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php
@@ -5,8 +5,9 @@
*/
namespace Magento\Tax\Model\Sales\Total\Quote;
+use Magento\Catalog\Api\ProductRepositoryInterface;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Quote\Model\Quote\TotalsCollector;
-use Magento\Tax\Model\Calculation;
use Magento\TestFramework\Helper\Bootstrap;
require_once __DIR__ . '/SetupUtil.php';
@@ -15,6 +16,9 @@
/**
* Class TaxTest
+ *
+ * Tests sales taxes with discounts/price rules during checkout.
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class TaxTest extends \Magento\TestFramework\Indexer\TestCase
@@ -302,6 +306,11 @@ public function testTaxCalculation($configData, $quoteData, $expectedResults)
$quoteAddress = $quote->getShippingAddress();
$this->totalsCollector->collectAddressTotals($quote, $quoteAddress);
$this->verifyResult($quoteAddress, $expectedResults);
+
+ $skus = array_map(function ($item) {
+ return $item['sku'];
+ }, $quoteData['items'] ?? []);
+ $this->removeProducts($skus);
}
/**
@@ -315,4 +324,32 @@ public function taxDataProvider()
global $taxCalculationData;
return $taxCalculationData;
}
+
+ /**
+ * Cleanup test by removing products.
+ *
+ * @param string[] $skus
+ * @return void
+ */
+ private function removeProducts(array $skus): void
+ {
+ $objectManager = Bootstrap::getObjectManager();
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = $objectManager->create(ProductRepositoryInterface::class);
+ $registry = $objectManager->get(\Magento\Framework\Registry::class);
+ /** @var ProductRepositoryInterface $productRepository */
+ $registry->unregister('isSecureArea');
+ $registry->register('isSecureArea', true);
+
+ foreach ($skus as $sku) {
+ try {
+ $productRepository->deleteById($sku);
+ } catch (NoSuchEntityException $e) {
+ // product already deleted
+ }
+ }
+
+ $registry->unregister('isSecureArea');
+ $registry->register('isSecureArea', false);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php
index 4d2c148141943..d8bf0e6bfb731 100644
--- a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_rollback.php
@@ -5,9 +5,13 @@
*/
declare(strict_types=1);
+use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
+
/** @var \Magento\Framework\Registry $registry */
$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
+Resolver::getInstance()->requireDataFixture('Magento/Store/_files/second_store_rollback.php');
+
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', true);
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php
index 9a95ed4fd462d..1cbdf6144d640 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php
@@ -62,6 +62,23 @@ public function testLoadedProductAttributes()
$this->assertEquals('Short description', $productOnWishlist->getData('short_description'));
}
+ /**
+ * Tests collection load.
+ * Tests collection load method when product salable filter flag is setted to true
+ * and few products are present.
+ *
+ * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
+ * @magentoDataFixture Magento/Wishlist/_files/wishlist.php
+ * @magentoDbIsolation disabled
+ */
+ public function testLoadWhenFewProductsPresent()
+ {
+ $this->itemCollection->setSalableFilter(true);
+ $this->itemCollection->addCustomerIdFilter(1);
+ $this->itemCollection->load();
+ $this->assertCount(1, $this->itemCollection->getItems());
+ }
+
/**
* @param array $attributes
*/
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php
index ee47961dec55b..ac1d846563188 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_rollback.php
@@ -7,6 +7,9 @@
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
+
/** @var \Magento\Framework\ObjectManagerInterface $objectManager */
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php
index 22583483ddf69..7fe1983d3192f 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product.php
@@ -5,7 +5,6 @@
*/
declare(strict_types=1);
-
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\TestFramework\Helper\Bootstrap;
@@ -30,6 +29,11 @@
$productRepository->cleanCache();
$product = $productRepository->get('product_disabled');
$wishlist->loadByCustomerId($customer->getId(), true);
+/** @var \Magento\Catalog\Helper\Product $productHelper */
+$productHelper = $objectManager->get(\Magento\Catalog\Helper\Product::class);
+$isSkipSaleableCheck = $productHelper->getSkipSaleableCheck();
+$productHelper->setSkipSaleableCheck(true);
$item = $wishlist->addNewItem($product);
+$productHelper->setSkipSaleableCheck($isSkipSaleableCheck);
$wishlist->setSharingCode('wishlist_disabled_item');
$wishListResource->save($wishlist);
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product_rollback.php
index 665644cd9b6db..9e46bc60b9a79 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_product_rollback.php
@@ -13,6 +13,10 @@
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
$objectManager = Bootstrap::getObjectManager();
+
+Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/simple_product_disabled_rollback.php');
+Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
+
/** @var Registry $registry */
$registry = $objectManager->get(Registry::class);
/** @var WishlistResource $wishListResource */
@@ -28,6 +32,3 @@
$registry->unregister('isSecureArea');
$registry->register('isSecureArea', false);
-
-Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/simple_product_disabled.php');
-Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_simple_product_rollback.php
new file mode 100644
index 0000000000000..8ce2f4b64d851
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_disabled_simple_product_rollback.php
@@ -0,0 +1,9 @@
+requireDataFixture('Magento/Wishlist/_files/wishlist_rollback.php');
diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php
index 4961d2403672c..54c26b73c70ba 100644
--- a/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php
+++ b/dev/tests/integration/testsuite/Magento/Wishlist/_files/wishlist_with_simple_product.php
@@ -25,4 +25,9 @@
$wishlistFactory = $objectManager->get(WishlistFactory::class);
$wishlist = $wishlistFactory->create();
$wishlist->loadByCustomerId($customer->getId(), true);
+/** @var \Magento\Catalog\Helper\Product $productHelper */
+$productHelper = $objectManager->get(\Magento\Catalog\Helper\Product::class);
+$isSkipSaleableCheck = $productHelper->getSkipSaleableCheck();
+$productHelper->setSkipSaleableCheck(true);
$wishlist->addNewItem($product);
+$productHelper->setSkipSaleableCheck($isSkipSaleableCheck);
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/product-gallery.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/product-gallery.test.js
new file mode 100644
index 0000000000000..2d6b6cc88fe78
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/product-gallery.test.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+define([
+ 'jquery',
+ 'Magento_Catalog/js/product-gallery'
+], function ($) {
+ 'use strict';
+
+ var galleryEl,
+ defaultConfig = {
+ images: [
+ {
+ disabled: 0,
+ file: '/e/a/earth.jpg',
+ position: 2,
+ url: 'http://localhost/media/catalog/product/e/a/earth.jpg',
+ size: 2048,
+ 'value_id': 2
+ },
+ {
+ disabled: 0,
+ file: '/m/a/mars.jpg',
+ position: 3,
+ url: 'http://localhost/media/catalog/product/m/a/mars.jpg',
+ size: 3072,
+ 'value_id': 3
+ },
+ {
+ disabled: 0,
+ file: '/j/u/jupiter.jpg',
+ position: 5,
+ size: 5120,
+ url: 'http://localhost/media/catalog/product/j/u/jupiter.jpg',
+ 'value_id': 5
+ }
+ ],
+ types: {
+ 'image': {
+ code: 'image',
+ label: 'Base',
+ name: 'product[image]'
+ },
+ 'small_image': {
+ code: 'small_image',
+ label: 'Small',
+ name: 'product[image]'
+ },
+ 'thumbnail': {
+ code: 'thumbnail',
+ label: 'Thumbnail',
+ name: 'product[image]'
+ }
+ }
+ };
+
+ function init(config) {
+ $(galleryEl).productGallery($.extend({}, defaultConfig, config || {}));
+ }
+
+ beforeEach(function () {
+ $(' '
+ ).appendTo(document.body);
+ galleryEl = document.getElementById('media_gallery_content');
+ });
+
+ afterEach(function () {
+ $(galleryEl).remove();
+ galleryEl = undefined;
+ });
+
+ describe('Magento_Catalog/js/product-gallery', function () {
+ describe('_create()', function () {
+ it('check that existing images are rendered correctly', function () {
+ init();
+ expect($(galleryEl).find('[data-role=image]').length).toBe(3);
+ expect($(galleryEl).find('[data-role=image]:nth-child(1) .position').val()).toBe('2');
+ expect($(galleryEl).find('[data-role=image]:nth-child(2) .position').val()).toBe('3');
+ expect($(galleryEl).find('[data-role=image]:nth-child(3) .position').val()).toBe('5');
+ });
+ });
+ describe('_addItem()', function () {
+ it('check that new image is inserted at the first position if there were no existing images', function () {
+ init({
+ images: []
+ });
+ $(galleryEl).trigger('addItem', {
+ file: '/s/a/saturn.jpg.tmp',
+ name: 'saturn.jpg',
+ size: 1024,
+ type: 'image/jpeg',
+ url: 'http://localhost/media/tmp/catalog/product/s/a/saturn.jpg'
+ });
+ expect($(galleryEl).find('[data-role=image]').length).toBe(1);
+ expect($(galleryEl).find('[data-role=image]:nth-child(1) .position').val()).toBe('1');
+ });
+ it('check that new image is inserted at the last position if there were existing images', function () {
+ init();
+ $(galleryEl).trigger('addItem', {
+ file: '/s/a/saturn.jpg.tmp',
+ name: 'saturn.jpg',
+ size: 1024,
+ type: 'image/jpeg',
+ url: 'http://localhost/media/tmp/catalog/product/s/a/saturn.jpg'
+ });
+ expect($(galleryEl).find('[data-role=image]').length).toBe(4);
+ // check that new image position is the position of previous image in the list plus one
+ expect($(galleryEl).find('[data-role=image]:nth-child(4) .position').val()).toBe('6');
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js
index 546392d35fe84..a555f8e00a916 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.test.js
@@ -41,11 +41,9 @@ define([
})
};
- model.getChildItems = jasmine.createSpy().and.returnValue($(''));
model.source = sourceMock;
model.processingUnionInsertData(mockData);
expect(model.source.get).toHaveBeenCalled();
- expect(model.getChildItems).toHaveBeenCalled();
expect(expectedData[1].sku).toBe('Conf&-sdfs');
});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js
new file mode 100644
index 0000000000000..e07b1fbc69453
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/adminhtml/js/view/form/components/insert-listing.test.js
@@ -0,0 +1,72 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/* eslint-disable max-nested-callbacks */
+/*jscs:disable jsDoc*/
+define(['Magento_Customer/js/form/components/insert-listing'], function (Constr) {
+ 'use strict';
+
+ describe('Magento_Customer/js/form/components/insert-listing', function () {
+ var obj,
+ ids = ['1', '2'],
+ data = {
+ action: 'delete',
+ data: {
+ selected: ids
+ }
+ },
+ selectionsProvider = {
+ selected: jasmine.createSpy().and.returnValue(ids),
+ deselect: jasmine.createSpy()
+ };
+
+ beforeEach(function () {
+ obj = new Constr({
+ name: 'content_name',
+ selections: function () {
+ return selectionsProvider;
+ }
+ });
+ });
+
+ describe('Check delete massaction process', function () {
+ it('Check call to deleteMassaction method', function () {
+ obj.deleteMassaction = {
+ call: jasmine.createSpy()
+ };
+ obj.onMassAction(data);
+
+ expect(obj.deleteMassaction.call).toHaveBeenCalledWith(obj, {
+ selected: ids
+ });
+ });
+
+ it('Check ids are retrieved from selections provider if they are NOT in data', function () {
+ obj._delete = jasmine.createSpy();
+ obj.onMassAction({
+ action: 'delete',
+ data: {}
+ });
+
+ expect(selectionsProvider.selected).toHaveBeenCalled();
+ selectionsProvider.selected.calls.reset();
+ expect(obj._delete).toHaveBeenCalledWith([1, 2]);
+ });
+
+ it('Check removal of default addresses and selections by provided ids', function () {
+ obj.source = {
+ get: jasmine.createSpy().and.returnValues(2, 3),
+ set: jasmine.createSpy()
+ };
+ obj.onMassAction(data);
+
+ expect(selectionsProvider.selected).not.toHaveBeenCalled();
+ expect(obj.source.get.calls.count()).toEqual(2);
+ expect(obj.source.set.calls.count()).toEqual(1);
+ expect(selectionsProvider.deselect.calls.count()).toEqual(2);
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js
new file mode 100644
index 0000000000000..7063b846ed166
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/customer-data.test.js
@@ -0,0 +1,213 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+
+define([
+ 'jquery',
+ 'underscore',
+ 'Magento_Customer/js/section-config',
+ 'Magento_Customer/js/customer-data'
+], function (
+ $,
+ _,
+ sectionConfig,
+ customerData
+) {
+ 'use strict';
+
+ var sectionConfigSettings = {
+ baseUrls: [
+ 'http://localhost/'
+ ],
+ sections: {
+ 'customer/account/loginpost': ['*'],
+ 'checkout/cart/add': ['cart'],
+ 'rest/*/v1/guest-carts/*/selected-payment-method': ['cart','checkout-data'],
+ '*': ['messages']
+ },
+ clientSideSections: [
+ 'checkout-data',
+ 'cart-data'
+ ],
+ sectionNames: [
+ 'customer',
+ 'product_data_storage',
+ 'cart',
+ 'messages'
+ ]
+ },
+ cookieLifeTime = 3600,
+ jQueryGetJSON;
+
+ function init(config) {
+ var defaultConfig = {
+ sectionLoadUrl: 'http://localhost/customer/section/load/',
+ expirableSectionLifetime: 60, // minutes
+ expirableSectionNames: ['cart'],
+ cookieLifeTime: cookieLifeTime,
+ updateSessionUrl: 'http://localhost/customer/account/updateSession/'
+ };
+
+ customerData['Magento_Customer/js/customer-data']($.extend({}, defaultConfig, config || {}));
+ }
+
+ function setupLocalStorage(sections) {
+ var mageCacheStorage = {},
+ sectionDataIds = {};
+
+ _.each(sections, function (sectionData, sectionName) {
+ sectionDataIds[sectionName] = sectionData['data_id'];
+
+ if (typeof sectionData.content !== 'undefined') {
+ mageCacheStorage[sectionName] = sectionData;
+ }
+ });
+
+ $.localStorage.set(
+ 'mage-cache-storage',
+ mageCacheStorage
+ );
+ $.cookieStorage.set(
+ 'section_data_ids',
+ sectionDataIds
+ );
+
+ $.localStorage.set(
+ 'mage-cache-timeout',
+ new Date(Date.now() + cookieLifeTime * 1000)
+ );
+ $.cookieStorage.set(
+ 'mage-cache-sessid',
+ true
+ );
+ }
+
+ function clearLocalStorage() {
+ $.cookieStorage.set('section_data_ids', {});
+
+ if (window.localStorage) {
+ window.localStorage.clear();
+ }
+ }
+
+ describe('Magento_Customer/js/customer-data', function () {
+ beforeAll(function () {
+ clearLocalStorage();
+ });
+
+ beforeEach(function () {
+ jQueryGetJSON = $.getJSON;
+ sectionConfig['Magento_Customer/js/section-config'](sectionConfigSettings);
+ });
+
+ afterEach(function () {
+ $.getJSON = jQueryGetJSON;
+ clearLocalStorage();
+ });
+
+ describe('getExpiredSectionNames()', function () {
+ it('check that result contains expired section names', function () {
+ setupLocalStorage({
+ 'cart': {
+ 'data_id': Math.floor(Date.now() / 1000) - 61 * 60, // 61 minutes ago
+ 'content': {}
+ }
+ });
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual(['cart']);
+ });
+
+ it('check that result doest not contain unexpired section names', function () {
+ setupLocalStorage({
+ 'cart': {
+ 'data_id': Math.floor(Date.now() / 1000) + 60, // in 1 minute
+ 'content': {}
+ }
+ });
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual([]);
+ });
+
+ it('check that result contains invalidated section names', function () {
+ setupLocalStorage({
+ 'cart': { // without storage content
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
+ }
+ });
+
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual(['cart']);
+ });
+
+ it('check that result does not contain unsupported section names', function () {
+ setupLocalStorage({
+ 'catalog': { // without storage content
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
+ }
+ });
+
+ init();
+ expect(customerData.getExpiredSectionNames()).toEqual([]);
+ });
+ });
+
+ describe('init()', function () {
+ it('check that sections are not requested from server, if there are no expired sections', function () {
+ setupLocalStorage({
+ 'catalog': { // without storage content
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // in 1 minute
+ }
+ });
+
+ $.getJSON = jasmine.createSpy('$.getJSON').and.callFake(function () {
+ var deferred = $.Deferred();
+
+ return deferred.promise();
+ });
+
+ init();
+ expect($.getJSON).not.toHaveBeenCalled();
+ });
+ it('check that sections are requested from server, if there are expired sections', function () {
+ setupLocalStorage({
+ 'customer': {
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // invalidated,
+ },
+ 'cart': {
+ 'data_id': Math.floor(Date.now() / 1000) - 61 * 60, // 61 minutes ago
+ 'content': {}
+ },
+ 'product_data_storage': {
+ 'data_id': Math.floor(Date.now() / 1000) + 60, // in 1 minute
+ 'content': {}
+ },
+ 'catalog': {
+ 'data_id': Math.floor(Date.now() / 1000) + 60 // invalid section,
+ },
+ 'checkout': {
+ 'data_id': Math.floor(Date.now() / 1000) - 61 * 60, // invalid section,
+ 'content': {}
+ }
+ });
+
+ $.getJSON = jasmine.createSpy('$.getJSON').and.callFake(function () {
+ var deferred = $.Deferred();
+
+ return deferred.promise();
+ });
+
+ init();
+ expect($.getJSON).toHaveBeenCalledWith(
+ 'http://localhost/customer/section/load/',
+ jasmine.objectContaining({
+ sections: 'cart,customer'
+ })
+ );
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/validation.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/validation.test.js
new file mode 100644
index 0000000000000..c830632ed0e87
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Customer/frontend/js/validation.test.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/* eslint-disable max-nested-callbacks */
+define([
+ 'jquery',
+ 'Magento_Customer/js/validation'
+], function ($) {
+ 'use strict';
+
+ describe('Testing customer DOB validation to tolerate zeroes in the single digit dates', function () {
+ var params,
+ dataProvider;
+
+ dataProvider = [
+ {
+ format: 'M/d/Y',
+ date: '09/2/18',
+ expects: true
+ },
+ {
+ format: 'M/DD/Y',
+ date: '09/2/18',
+ expects: false
+ }
+ ];
+
+ dataProvider.forEach(function (data) {
+ it('Test date validation for format ' + data.format, function () {
+ params = {
+ 'dateFormat': data.format
+ };
+ expect($.validator.methods['validate-date']
+ .call($.validator.prototype, data.date, null, params)).toEqual(data.expects);
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Payment/adminhtml/web/js/transparent.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Payment/adminhtml/web/js/transparent.test.js
new file mode 100644
index 0000000000000..4902fbad26ff3
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Payment/adminhtml/web/js/transparent.test.js
@@ -0,0 +1,84 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+define([
+ 'jquery',
+ 'jquery/validate',
+ 'Magento_Payment/js/transparent'
+], function ($) {
+ 'use strict';
+
+ var containerEl,
+ formEl,
+ jQueryAjax;
+
+ function init(config) {
+ var defaultConfig = {
+ orderSaveUrl: '/',
+ gateway: 'payflowpro',
+ editFormSelector: '#' + formEl.id
+ };
+
+ $(formEl).find(':radio[value="payflowpro"]').prop('checked', 'checked');
+ $(formEl).transparent($.extend({}, defaultConfig, config || {}));
+ }
+
+ beforeEach(function () {
+ if (!window.FORM_KEY) {
+ window.FORM_KEY = '61d0c9da0aa473d214f61913967cc0ea';
+ }
+ $(' ' +
+ '' +
+ ' '
+ ).appendTo(document.body);
+ containerEl = document.getElementById('admin_edit_order_form_container');
+ formEl = document.getElementById('admin_edit_order_form');
+ jQueryAjax = $.ajax;
+ });
+
+ afterEach(function () {
+ $(containerEl).remove();
+ formEl = undefined;
+ containerEl = undefined;
+ $.ajax = jQueryAjax;
+ jQueryAjax = undefined;
+ });
+
+ describe('Magento_Payment/js/transparent', function () {
+ describe('beforeSubmitOrder handler', function () {
+ it('is registered when selected payment method requires transparent', function () {
+ init();
+ expect(($._data(formEl, 'events') || {}).beforeSubmitOrder[0].type).toBe('beforeSubmitOrder');
+ expect(($._data(formEl, 'events') || {}).beforeSubmitOrder[0].namespace).toBe('payflowpro');
+ });
+ it('is not registered when selected payment method does not require transparent', function () {
+ init();
+ $(formEl).find(':radio[value="money_order"]').prop('checked', 'checked');
+ $(formEl).trigger('changePaymentMethod', ['money_order']);
+ expect(($._data(formEl, 'events') || {}).beforeSubmitOrder).toBeUndefined();
+ });
+ it('returns false to prevent normal order creation', function () {
+ var beforeSubmitOrderEvent;
+
+ $.ajax = jasmine.createSpy();
+ init({
+ orderSaveUrl: '/admin/paypal/transparent/requestSecureToken'
+ });
+ beforeSubmitOrderEvent = $.Event('beforeSubmitOrder');
+ $(formEl).trigger(beforeSubmitOrderEvent);
+ expect($.ajax).toHaveBeenCalledWith(jasmine.objectContaining({
+ url: '/admin/paypal/transparent/requestSecureToken',
+ type: 'post'
+ }));
+ expect(beforeSubmitOrderEvent.result).toBe(false);
+ expect(beforeSubmitOrderEvent.isImmediatePropagationStopped()).toBe(true);
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js
new file mode 100644
index 0000000000000..7e33a7ad3c1fa
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/grid/tree-massactions.test.js
@@ -0,0 +1,117 @@
+/**
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/*eslint max-nested-callbacks: 0*/
+define([
+ 'jquery',
+ 'squire',
+ 'underscore',
+ 'Magento_Sales/js/grid/tree-massactions'
+], function ($, Squire, _, TreeMassaction) {
+ 'use strict';
+
+ var injector = new Squire(),
+ mocks = {
+ 'Magento_Ui/js/grid/massactions': {
+ defaultCallback: jasmine.createSpy().and.returnValue({}),
+ applyAction: jasmine.createSpy().and.returnValue({})
+ }
+ },
+ obj,
+ utils;
+
+ describe('Magento_Sales/js/grid/tree-massactions', function () {
+ var model;
+
+ beforeEach(function (done) {
+ injector.mock(mocks);
+ injector.require([
+ 'Magento_Ui/js/grid/massactions',
+ 'mageUtils'
+ ], function (instance, mageUtils) {
+ obj = _.extend({}, instance);
+ utils = mageUtils;
+ done();
+ });
+ model = new TreeMassaction({
+ actions: [
+ {
+ type: 'availability',
+ actions: [{
+ type: 'enable'
+ }, {
+ type: 'disable'
+ }]
+ },
+ {
+ type: 'hold_order',
+ component: 'uiComponent',
+ label: 'hold',
+ url: 'http://local.magento/hold_order',
+ modules: {
+ selections: ['1','2','3']
+ },
+ actions: [{
+ callback: 'defaultCallback'
+ }]
+ }]
+ });
+ });
+
+ afterEach(function () {
+ try {
+ injector.clean();
+ injector.remove();
+ } catch (e) {}
+ });
+
+ describe('check applyAction', function () {
+ it('change visibility of submenu', function () {
+ expect(model.actions()[0].visible()).toBeFalsy();
+ expect(model.applyAction('availability')).toBe(model);
+ expect(model.actions()[0].visible()).toBeTruthy();
+ });
+ });
+ describe('check defaultCallback', function () {
+ it('check model called with action and selected data', function () {
+ expect(model.applyAction('hold_order')).toBe(model);
+ expect(model.actions()[1].visible()).toBeTruthy();
+ expect(model.actions()[1].modules.selections).toBeTruthy();
+ expect(model.actions()[1].modules.selections.total).toBeFalsy();
+ });
+
+ it('check defaultCallback submitted the data', function () {
+ var action = {
+ component: 'uiComponent',
+ label: 'Hold',
+ type: 'hold_order',
+ url: 'http://local.magento/hold_order/'
+ },
+ data = {
+ excludeMode: true,
+ excluded: [],
+ params: {},
+ selected: ['7', '6', '5', '4', '3', '2', '1'],
+ total: 7
+ },
+ result;
+
+ obj.getAction = jasmine.createSpy().and.returnValue('hold_order');
+
+ obj.applyAction(action);
+
+ result = obj.defaultCallback(action, data);
+
+ expect(typeof result).toBe('object');
+ spyOn(utils, 'submit').and.callThrough();
+ utils.submit({
+ url: action.url,
+ data: data.selected
+ });
+ expect(utils.submit).toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/order/create/scripts.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/order/create/scripts.test.js
new file mode 100644
index 0000000000000..0071d5af7df4e
--- /dev/null
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Sales/adminhtml/js/order/create/scripts.test.js
@@ -0,0 +1,260 @@
+/*
+ * Copyright © Magento, Inc. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+/*eslint-disable max-nested-callbacks*/
+/*jscs:disable jsDoc*/
+define([
+ 'jquery',
+ 'squire',
+ 'jquery/validate'
+], function ($, Squire) {
+ 'use strict';
+
+ var formEl,
+ jQueryAjax,
+ order,
+ tmpl = ' ';
+
+ $.widget('magetest.testPaymentMethodA', {
+ options: {
+ code: null,
+ orderSaveUrl: null,
+ orderFormSelector: null
+ },
+
+ _create: function () {
+ var $editForm = $(this.options.orderFormSelector);
+
+ $editForm.off('changePaymentMethod.' + this.options.code)
+ .on('changePaymentMethod.' + this.options.code, this._onChangePaymentMethod.bind(this));
+ },
+
+ _onChangePaymentMethod: function (event, method) {
+ var $editForm = $(this.options.orderFormSelector);
+
+ $editForm.off('beforeSubmitOrder.' + this.options.code);
+
+ if (method === this.options.code) {
+ $editForm.on('beforeSubmitOrder.' + this.options.code, this._submitOrder.bind(this));
+ }
+ },
+
+ _submitOrder: function (event) {
+ $.ajax({
+ url: this.options.orderSaveUrl,
+ type: 'POST',
+ context: this,
+ data: {
+ code: this.options.code
+ },
+ dataType: 'JSON'
+ });
+ event.stopImmediatePropagation();
+
+ return false;
+ }
+
+ });
+
+ $.widget('magetest.testPaymentMethodB', $.magetest.testPaymentMethodA, {
+ isActive: false,
+ _onChangePaymentMethod: function (event, method) {
+ var $editForm = $(this.options.orderFormSelector),
+ isActive = method === this.options.code;
+
+ if (this.isActive !== isActive) {
+ this.isActive = isActive;
+
+ if (!isActive) {
+ $editForm.off('submitOrder.' + this.options.code);
+ } else {
+ $editForm.off('submitOrder')
+ .on('submitOrder.' + this.options.code, this._submitOrder.bind(this));
+ }
+ }
+ }
+ });
+
+ function init(config) {
+ config = config || {};
+ order = new window.AdminOrder({});
+ $(formEl).validate({});
+ $(formEl).find(':radio[value="payment1"]').testPaymentMethodA({
+ code: 'payment1',
+ orderSaveUrl: '/admin/sales/order/create/payment_method/payment1',
+ orderFormSelector: '#' + formEl.id
+ });
+ $(formEl).find(':radio[value="payment2"]').testPaymentMethodB({
+ code: 'payment2',
+ orderSaveUrl: '/admin/sales/order/create/payment_method/payment2',
+ orderFormSelector: '#' + formEl.id
+ });
+ $(formEl).off('realOrder').on('realOrder', function () {
+ $.ajax({
+ url: '/admin/sales/order/create',
+ type: 'POST',
+ context: this,
+ data: $(this).serializeArray(),
+ dataType: 'JSON'
+ });
+ });
+
+ if (config.method) {
+ $(formEl).find(':radio[value="' + config.method + '"]').prop('checked', true);
+ order.switchPaymentMethod(config.method);
+ }
+ }
+
+ describe('Magento_Sales/order/create/scripts', function () {
+ var injector = new Squire(),
+ mocks = {
+ 'jquery': $,
+ 'Magento_Catalog/catalog/product/composite/configure': jasmine.createSpy(),
+ 'Magento_Ui/js/modal/confirm': jasmine.createSpy(),
+ 'Magento_Ui/js/modal/alert': jasmine.createSpy(),
+ 'Magento_Ui/js/lib/view/utils/async': jasmine.createSpy()
+ };
+
+ beforeEach(function (done) {
+ jQueryAjax = $.ajax;
+ injector.mock(mocks);
+ injector.require(['Magento_Sales/order/create/scripts'], function () {
+ window.FORM_KEY = window.FORM_KEY || '61d0c9da0aa473d214f61913967cc0ea';
+ $(tmpl).appendTo(document.body);
+ formEl = document.getElementById('edit_form');
+ $(formEl).off();
+ done();
+ });
+ });
+
+ afterEach(function () {
+ try {
+ injector.clean();
+ injector.remove();
+ } catch (e) {
+ }
+ $(formEl).off().remove();
+ formEl = undefined;
+ order = undefined;
+ $.ajax = jQueryAjax;
+ jQueryAjax = undefined;
+ });
+
+ describe('submit()', function () {
+ function testSubmit(currentPaymentMethod, paymentMethod, ajaxParams) {
+ $.ajax = jasmine.createSpy('$.ajax');
+ init({
+ method: currentPaymentMethod
+ });
+ $(formEl).find(':radio[value="' + paymentMethod + '"]').prop('checked', true);
+ order.switchPaymentMethod(paymentMethod);
+ order.submit();
+ expect($.ajax).toHaveBeenCalledTimes(1);
+ expect($.ajax).toHaveBeenCalledWith(jasmine.objectContaining(ajaxParams));
+ }
+
+ it('Check that payment custom handler is executed #1', function () {
+ testSubmit(
+ null,
+ 'payment1',
+ {
+ url: '/admin/sales/order/create/payment_method/payment1',
+ data: {
+ code: 'payment1'
+ }
+ }
+ );
+ });
+
+ it('Check that payment custom handler is executed #2', function () {
+ testSubmit(
+ 'payment1',
+ 'payment1',
+ {
+ url: '/admin/sales/order/create/payment_method/payment1',
+ data: {
+ code: 'payment1'
+ }
+ }
+ );
+ });
+
+ it('Check that payment custom handler is executed #3', function () {
+ testSubmit(
+ null,
+ 'payment2',
+ {
+ url: '/admin/sales/order/create/payment_method/payment2',
+ data: {
+ code: 'payment2'
+ }
+ }
+ );
+ });
+
+ it('Check that payment custom handler is executed #4', function () {
+ testSubmit(
+ 'payment2',
+ 'payment2',
+ {
+ url: '/admin/sales/order/create/payment_method/payment2',
+ data: {
+ code: 'payment2'
+ }
+ }
+ );
+ });
+
+ it('Check that native handler is executed for payment without custom handler #1', function () {
+ testSubmit(
+ 'payment1',
+ 'free',
+ {
+ url: '/admin/sales/order/create',
+ data: [
+ {
+ name: 'payment[method]',
+ value: 'free'
+ }
+ ]
+ }
+ );
+ });
+
+ it('Check that native handler is executed for payment without custom handler #2', function () {
+ testSubmit(
+ 'payment2',
+ 'free',
+ {
+ url: '/admin/sales/order/create',
+ data: [
+ {
+ name: 'payment[method]',
+ value: 'free'
+ }
+ ]
+ }
+ );
+ });
+ });
+ });
+});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js
index f46ff6b30abbe..c0ecec40516fa 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js
@@ -246,6 +246,12 @@ define([
expect(type).toEqual('object');
});
+ it('Must be false if "disabled" is true', function () {
+ obj.listVisible(false);
+ obj.disabled(true);
+ obj.toggleListVisible();
+ expect(obj.listVisible()).toEqual(false);
+ });
it('Must be false if "listVisible" is true', function () {
obj.listVisible(true);
obj.toggleListVisible();
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js
index 6a466f0c37872..a5b434d956097 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js
@@ -74,6 +74,7 @@ define([
originMock = $.fn.get;
spyOn($.fn, 'get').and.returnValue(imageMock);
+ imagePreview.lastOpenedImage = jasmine.createSpy().and.returnValue(2);
imagePreview.visibleRecord = jasmine.createSpy().and.returnValue(2);
imagePreview.displayedRecord = ko.observable();
imagePreview.displayedRecord(recordMock);
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js
index de3387e31af88..5975f21e08070 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/multiselect.test.js
@@ -135,6 +135,19 @@ define([
expect(multiSelect.selected().toString()).toEqual('3,4,1,2');
});
+ it('Select all rows all over the Grid and deselects all records', function () {
+ multiSelect.rows([{
+ id: 1
+ }, {
+ id: 2
+ }]);
+
+ multiSelect.selectAll();
+ multiSelect.deselectAll();
+ multiSelect.indetermine(2);
+ expect(multiSelect.togglePage().selected()).toEqual([1, 2]);
+ });
+
it('Select all rows all over the Grid without all rows on current page but with specific rows on another page',
function () {
multiSelect.rows([{
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/masonry.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/masonry.test.js
index 2c2cdab2d46da..7f7d0c5f9dd2a 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/masonry.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/masonry.test.js
@@ -6,79 +6,78 @@
/*eslint max-nested-callbacks: 0*/
define([
'jquery',
- 'ko',
'Magento_Ui/js/grid/masonry'
-], function ($, ko, Masonry) {
+], function ($, Masonry) {
'use strict';
- var Component,
- rows,
- container = ' ';
+ describe('Magento_Ui/js/grid/masonry', function () {
+ var Component,
+ rows,
+ container = ' ';
- beforeEach(function () {
- rows = [
- {
- _rowIndex: 0,
- category: {},
- 'category_id': 695,
- 'category_name': 'People',
- 'comp_url': 'https://stock.adobe.com/Rest/Libraries/Watermarked/Download/327515738/2',
- 'content_type': 'image/jpeg',
- 'country_name': 'Malaysia',
- 'creation_date': '2020-03-02 10:41:51',
- 'creator_id': 208217780,
- 'creator_name': 'NajmiArif',
- height: 3264,
- id: 327515738,
- 'id_field_name': 'id',
- 'is_downloaded': 0,
- 'is_licensed_locally': 0,
- keywords: [],
- 'media_type_id': 1,
- overlay: '',
- path: '',
- 'premium_level_id': 0,
- 'thumbnail_240_url': 'https://t4.ftcdn.net/jpg/03/27/51/57/240_F_327515738_n.jpg',
- 'thumbnail_500_ur': 'https://as2.ftcdn.net/jpg/03/27/51/57/500_F_327515738_n.jpg',
- title: 'Neon effect picture of man wearing medical mask for viral or pandemic disease',
- width: 4896
- }
+ beforeEach(function () {
+ rows = [
+ {
+ _rowIndex: 0,
+ category: {},
+ 'category_id': 695,
+ 'category_name': 'People',
+ 'comp_url': 'url',
+ 'content_type': 'image/jpeg',
+ 'country_name': 'Malaysia',
+ 'creation_date': '2020-03-02 10:41:51',
+ 'creator_id': 208217780,
+ 'creator_name': 'NajmiArif',
+ height: 3264,
+ id: 327515738,
+ 'id_field_name': 'id',
+ 'is_downloaded': 0,
+ 'is_licensed_locally': 0,
+ keywords: [],
+ 'media_type_id': 1,
+ overlay: '',
+ path: '',
+ 'premium_level_id': 0,
+ 'thumbnail_240_url': 'url',
+ 'thumbnail_500_ur': 'url',
+ title: 'Neon effect picture of man wearing medical mask for viral or pandemic disease',
+ width: 4896
+ }
+ ];
- ];
-
- $(container).appendTo('body');
-
- Component = new Masonry({
- defaults: {
- rows: ko.observable()
- }
+ $(document.body).append(container);
+ Component = new Masonry({
+ defaults: {
+ containerId: '#masonry_grid'
+ }
+ });
});
- });
-
- afterEach(function () {
- $('#masonry_grid').remove();
- });
+ afterEach(function () {
+ Component.clear();
+ $('#masonry_grid').remove();
+ });
- describe('check initComponent', function () {
- it('verify setLayoutstyles called and grid iniztilized', function () {
- var setlayoutStyles = spyOn(Component, 'setLayoutStyles');
+ describe('check initComponent', function () {
+ it('verify setLayoutstyles called and grid iniztilized', function () {
+ var setlayoutStyles = spyOn(Component, 'setLayoutStyles');
- expect(Component).toBeDefined();
- Component.containerId = 'masonry_grid';
- Component.initComponent(rows);
- Component.rows().forEach(function (image) {
- expect(image.styles).toBeDefined();
- expect(image.css).toBeDefined();
+ expect(Component).toBeDefined();
+ Component.containerId = 'masonry_grid';
+ Component.initComponent(rows);
+ Component.rows().forEach(function (image) {
+ expect(image.styles).toBeDefined();
+ expect(image.css).toBeDefined();
+ });
+ expect(setlayoutStyles).toHaveBeenCalled();
});
- expect(setlayoutStyles).toHaveBeenCalled();
- });
- it('verify events triggered', function () {
- var setLayoutStyles = spyOn(Component, 'setLayoutStyles');
+ it('verify events triggered', function () {
+ var setLayoutStyles = spyOn(Component, 'setLayoutStyles');
- Component.initComponent(rows);
- window.dispatchEvent(new Event('resize'));
- expect(setLayoutStyles).toHaveBeenCalled();
+ Component.initComponent(rows);
+ window.dispatchEvent(new Event('resize'));
+ expect(setLayoutStyles).toHaveBeenCalled();
+ });
});
});
});
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js
index a3d49e382de51..1e63f9f61f6d1 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/url-filter-applier.test.js
@@ -12,7 +12,8 @@ define([
describe('Magento_Ui/js/grid/url-filter-applier', function () {
var urlFilterApplierObj,
filterComponentMock = {
- setData: jasmine.createSpy(),
+ set: jasmine.createSpy(),
+ get: jasmine.createSpy(),
apply: jasmine.createSpy()
};
@@ -64,11 +65,14 @@ define([
it('applies url filter on filter component', function () {
urlFilterApplierObj.searchString = '?filters[name]=test&filters[qty]=1';
urlFilterApplierObj.apply();
- expect(urlFilterApplierObj.filterComponent().setData).toHaveBeenCalledWith({
- 'name': 'test',
- 'qty': '1'
- }, false);
- expect(urlFilterApplierObj.filterComponent().apply).toHaveBeenCalled();
+ expect(urlFilterApplierObj.filterComponent().get).toHaveBeenCalled();
+ expect(urlFilterApplierObj.filterComponent().set).toHaveBeenCalledWith(
+ 'applied',
+ {
+ 'name': 'test',
+ 'qty': '1'
+ }
+ );
});
});
});
diff --git a/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js b/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js
index 7700c3d64a1b7..5db506b00a883 100644
--- a/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js
+++ b/dev/tests/js/jasmine/tests/lib/mage/gallery/gallery.test.js
@@ -98,8 +98,53 @@ define([
expect(gallery.settings.fotoramaApi).toBeDefined();
expect(gallery.settings.data).toBeDefined();
expect(gallery.settings.api).toBeDefined();
+ expect(gallery.settings.activeBreakpoint).toEqual({});
$.fn.data = originSpy;
});
+
+ it('Verify gallery navigation is set properly as dots if specified in options', function () {
+ // added
+ options.breakpoints = {
+ mobile: {
+ conditions: {
+ 'max-width': '767px'
+ },
+ options: {
+ options: {
+ nav: 'dots'
+ }
+ }
+ },
+ desktop: {
+ conditions: {
+ 'min-width': '1024px'
+ },
+ options: {
+ options: {
+ nav: 'thumbs'
+ }
+ }
+ }
+ };
+
+ originSpy = $.fn.data;
+ jqueryDataMock = {
+ setOptions: jasmine.createSpy().and.returnValue(true),
+ updateOptions: jasmine.createSpy().and.returnValue(true)
+ };
+ spyOn($.fn, 'data').and.callFake(function () {
+ return jqueryDataMock;
+ });
+
+ gallery = new Gallery(options, element);
+
+ options.breakpoints.mobile.options.options.arrows = false;
+ expect(JSON.stringify(gallery.settings.activeBreakpoint))
+ .toEqual(JSON.stringify(options.breakpoints.mobile.options));
+
+ $.fn.data = originSpy;
+ });
+
});
});
diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/AddedFiles.php b/dev/tests/static/framework/Magento/TestFramework/Utility/AddedFiles.php
new file mode 100644
index 0000000000000..4afeda3a035eb
--- /dev/null
+++ b/dev/tests/static/framework/Magento/TestFramework/Utility/AddedFiles.php
@@ -0,0 +1,35 @@
+classNameExtractor = new ClassNameExtractor();
+ }
+
+ /**
+ * Get list of classes name which are subclasses of mentioned class.
+ *
+ * @param array $fileList
+ * @param string $parent
+ * @param bool $asDataSet
+ *
+ * @return array
+ * @throws \ReflectionException
+ */
+ public function getClassesWhichAreChildrenOf(array $fileList, string $parent, bool $asDataSet = true): array
+ {
+ $found = [];
+
+ foreach ($fileList as $file) {
+ $name = $asDataSet ? $file[0] : $file;
+ $class = $this->classNameExtractor->getNameWithNamespace(file_get_contents($name));
+
+ if ($class) {
+ $classReflection = new \ReflectionClass($class);
+ if ($classReflection->isSubclassOf($parent)) {
+ $found[] = $class;
+ }
+ }
+ }
+
+ return $found;
+ }
+}
diff --git a/dev/tests/static/framework/Magento/TestFramework/Utility/FilesSearch.php b/dev/tests/static/framework/Magento/TestFramework/Utility/FilesSearch.php
new file mode 100644
index 0000000000000..0ec8124496d1d
--- /dev/null
+++ b/dev/tests/static/framework/Magento/TestFramework/Utility/FilesSearch.php
@@ -0,0 +1,48 @@
+childrenClassesSearch = new ChildrenClassesSearch();
+ }
+
+ public function testChildrenSearch(): void
+ {
+ $files = [
+ __DIR__ . '/ChildrenClassesSearch/A.php',
+ __DIR__ . '/ChildrenClassesSearch/B.php',
+ __DIR__ . '/ChildrenClassesSearch/C.php',
+ __DIR__ . '/ChildrenClassesSearch/D.php',
+ __DIR__ . '/ChildrenClassesSearch/E.php',
+ __DIR__ . '/ChildrenClassesSearch/F.php',
+ __DIR__ . '/ChildrenClassesSearch/ZInterface.php',
+ ];
+
+ $found = $this->childrenClassesSearch->getClassesWhichAreChildrenOf(
+ $files,
+ A::class,
+ false
+ );
+
+ $expected = [
+ B::class,
+ E::class,
+ F::class
+ ];
+
+ $this->assertSame($expected, $found);
+ }
+}
diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FilesSearchTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FilesSearchTest.php
new file mode 100644
index 0000000000000..7b27dde3b0bf4
--- /dev/null
+++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/FilesSearchTest.php
@@ -0,0 +1,53 @@
+assertSame($files, $expected);
+ }
+
+ /**
+ * Test callblack function in case when files with lists did not found.
+ */
+ public function testGetEmptyList(): void
+ {
+ $pattern = 'zzz.txt';
+
+ $files = FilesSearch::getFilesFromListFile(__DIR__, $pattern, function () {
+ return ['1', '2', '3'];
+ });
+
+ $expected = [
+ BP . '/1',
+ BP . '/2',
+ BP . '/3'
+ ];
+
+ $this->assertSame($files, $expected);
+ }
+}
diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/changed_files_some_name_test.txt b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/changed_files_some_name_test.txt
new file mode 100644
index 0000000000000..816f4b32c9361
--- /dev/null
+++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Utility/_files/changed_files_some_name_test.txt
@@ -0,0 +1,3 @@
+app/code/Magento/Cms/Block/Block.php
+app/code/Magento/Cms/Api/BlockRepositoryInterface.php
+app/code/Magento/Cms/Observer/NoCookiesObserver.php
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/Magento/Framework/App/Action/AbstractActionTest.php b/dev/tests/static/testsuite/Magento/Test/Legacy/Magento/Framework/App/Action/AbstractActionTest.php
new file mode 100644
index 0000000000000..69050c3c51895
--- /dev/null
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/Magento/Framework/App/Action/AbstractActionTest.php
@@ -0,0 +1,85 @@
+childrenClassesSearch = new ChildrenClassesSearch();
+ $this->fileUtilities = Files::init();
+ }
+
+ /**
+ * Test newly created controllers do not extend deprecated AbstractAction.
+ *
+ * @throws \ReflectionException
+ */
+ public function testNewControllersDoNotExtendAbstractAction(): void
+ {
+ $files = $this->getTestFiles();
+
+ $found = $this->childrenClassesSearch->getClassesWhichAreChildrenOf($files, AbstractAction::class);
+
+ $this->assertEmpty(
+ $found,
+ "The following new controller(s) extend " . AbstractAction::class . "\r\n"
+ . "All new controller classes must implement " . ActionInterface::class . " instead.\r\n"
+ . print_r($found, true)
+ );
+ }
+
+ /**
+ * Provide files for test.
+ *
+ * @return array
+ */
+ private function getTestFiles(): array
+ {
+ $phpFiles = AddedFiles::getAddedFilesList($this->getChangedFilesBaseDir());
+
+ $phpFiles = Files::composeDataSets($phpFiles);
+ $fileTypes = Files::INCLUDE_APP_CODE | Files::INCLUDE_LIBS | Files::AS_DATA_SET;
+ return array_intersect_key($phpFiles, $this->fileUtilities->getPhpFiles($fileTypes));
+ }
+
+ /**
+ * Returns base directory for generated lists.
+ *
+ * @return string
+ */
+ private function getChangedFilesBaseDir(): string
+ {
+ return BP . DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR . 'static' .
+ DIRECTORY_SEPARATOR . 'testsuite' . DIRECTORY_SEPARATOR . 'Magento' . DIRECTORY_SEPARATOR . 'Test';
+ }
+}
diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
index 324753b4bd4ec..ad91025448579 100644
--- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php
@@ -14,6 +14,8 @@
use Magento\TestFramework\CodingStandard\Tool\CopyPasteDetector;
use Magento\TestFramework\CodingStandard\Tool\PhpCompatibility;
use Magento\TestFramework\CodingStandard\Tool\PhpStan;
+use Magento\TestFramework\Utility\AddedFiles;
+use Magento\TestFramework\Utility\FilesSearch;
use PHPMD\TextUI\Command;
/**
@@ -113,8 +115,8 @@ public static function getWhitelist(
*/
private static function getChangedFilesList($changedFilesBaseDir)
{
- return self::getFilesFromListFile(
- $changedFilesBaseDir,
+ return FilesSearch::getFilesFromListFile(
+ $changedFilesBaseDir ?: self::getChangedFilesBaseDir(),
'changed_files*',
function () {
// if no list files, probably, this is the dev environment
@@ -128,65 +130,6 @@ function () {
);
}
- /**
- * This method loads list of added files.
- *
- * @param string $changedFilesBaseDir
- * @return string[]
- */
- private static function getAddedFilesList($changedFilesBaseDir)
- {
- return self::getFilesFromListFile(
- $changedFilesBaseDir,
- 'changed_files*.added.*',
- function () {
- // if no list files, probably, this is the dev environment
- // phpcs:ignore Generic.PHP.NoSilencedErrors,Magento2.Security.InsecureFunction
- @exec('git diff --cached --name-only --diff-filter=A', $addedFiles);
- return $addedFiles;
- }
- );
- }
-
- /**
- * Read files from generated lists.
- *
- * @param string $listsBaseDir
- * @param string $listFilePattern
- * @param callable $noListCallback
- * @return string[]
- */
- private static function getFilesFromListFile($listsBaseDir, $listFilePattern, $noListCallback)
- {
- $filesDefinedInList = [];
-
- $globFilesListPattern = ($listsBaseDir ?: self::getChangedFilesBaseDir())
- . '/_files/' . $listFilePattern;
- $listFiles = glob($globFilesListPattern);
- if (!empty($listFiles)) {
- foreach ($listFiles as $listFile) {
- // phpcs:ignore Magento2.Performance.ForeachArrayMerge.ForeachArrayMerge
- $filesDefinedInList = array_merge(
- $filesDefinedInList,
- file($listFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
- );
- }
- } else {
- $filesDefinedInList = call_user_func($noListCallback);
- }
-
- array_walk(
- $filesDefinedInList,
- function (&$file) {
- $file = BP . '/' . $file;
- }
- );
-
- $filesDefinedInList = array_values(array_unique($filesDefinedInList));
-
- return $filesDefinedInList;
- }
-
/**
* Filter list of files.
*
@@ -427,7 +370,7 @@ public function testCopyPaste()
*/
public function testStrictTypes()
{
- $changedFiles = self::getAddedFilesList('');
+ $changedFiles = AddedFiles::getAddedFilesList(self::getChangedFilesBaseDir());
try {
$blackList = Files::init()->readLists(
diff --git a/lib/internal/Magento/Framework/App/ResourceConnection.php b/lib/internal/Magento/Framework/App/ResourceConnection.php
index 00dc88dcd7b17..3ba50fb396a4c 100644
--- a/lib/internal/Magento/Framework/App/ResourceConnection.php
+++ b/lib/internal/Magento/Framework/App/ResourceConnection.php
@@ -178,7 +178,7 @@ public function getTableName($modelEntity, $connectionName = self::DEFAULT_CONNE
list($modelEntity, $tableSuffix) = $modelEntity;
}
- $tableName = $modelEntity;
+ $tableName = (string)$modelEntity;
$mappedTableName = $this->getMappedTableName($tableName);
if ($mappedTableName) {
diff --git a/lib/internal/Magento/Framework/DB/Select.php b/lib/internal/Magento/Framework/DB/Select.php
index 7d2799cf50679..0aaf29aeb332e 100644
--- a/lib/internal/Magento/Framework/DB/Select.php
+++ b/lib/internal/Magento/Framework/DB/Select.php
@@ -116,11 +116,11 @@ public function where($cond, $value = null, $type = null)
{
if ($value === null && $type === null) {
$value = '';
- } elseif ($type == self::TYPE_CONDITION) {
+ } elseif ((string)$type === self::TYPE_CONDITION) {
$type = null;
}
if (is_array($value)) {
- $cond = $this->getConnection()->quoteInto($cond, $value);
+ $cond = $this->getConnection()->quoteInto($cond, $value, $type);
$value = null;
}
return parent::where($cond, $value, $type);
diff --git a/lib/internal/Magento/Framework/Data/Form/Element/Time.php b/lib/internal/Magento/Framework/Data/Form/Element/Time.php
index 53d72d704483c..5f67ac4414e99 100644
--- a/lib/internal/Magento/Framework/Data/Form/Element/Time.php
+++ b/lib/internal/Magento/Framework/Data/Form/Element/Time.php
@@ -114,7 +114,7 @@ public function getElementHtml()
'style',
[],
<< |