= /* @noEscape */ '' ?>
@@ -48,57 +49,57 @@
= $block->escapeHtml($_item->getName()) ?>
- getProductPriceHtml($_item, $type);
- ?>
-
= $block->getReviewsSummaryHtml($_item, $templateType) ?>
+ = $block->getProductPriceHtml($_item, $type) ?>
+
+ = $block->getProductDetailsHtml($_item) ?>
+
-
-
-
- isSaleable()): ?>
- getTypeInstance()->isPossibleBuyFromList($_item)): ?>
-
+
+
+
+
+ isSaleable()): ?>
+ getAddToCartPostParams($_item); ?>
+
- helper('Magento\Framework\Data\Helper\PostHelper');
- $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_item), ['product' => $_item->getEntityId()])
- ?>
-
+ getIsSalable()): ?>
+
= $block->escapeHtml(__('In stock')) ?>
+
+
= $block->escapeHtml(__('Out of stock')) ?>
+
-
- getIsSalable()): ?>
-
= $block->escapeHtml(__('In stock')) ?>
-
-
= $block->escapeHtml(__('Out of stock')) ?>
+
+
+
+
-
-
-
-
+
+
+
diff --git a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
index de996bed02439..a20c146d68d92 100644
--- a/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
+++ b/app/code/Magento/Checkout/Block/Checkout/AttributeMerger.php
@@ -6,10 +6,18 @@
namespace Magento\Checkout\Block\Checkout;
use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository;
+use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Customer\Helper\Address as AddressHelper;
use Magento\Customer\Model\Session;
use Magento\Directory\Helper\Data as DirectoryHelper;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\NoSuchEntityException;
+/**
+ * Fields attribute merger.
+ *
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
+ */
class AttributeMerger
{
/**
@@ -46,6 +54,7 @@ class AttributeMerger
'alpha' => 'validate-alpha',
'numeric' => 'validate-number',
'alphanumeric' => 'validate-alphanum',
+ 'alphanum-with-spaces' => 'validate-alphanum-with-spaces',
'url' => 'validate-url',
'email' => 'email2',
'length' => 'validate-length',
@@ -67,7 +76,7 @@ class AttributeMerger
private $customerRepository;
/**
- * @var \Magento\Customer\Api\Data\CustomerInterface
+ * @var CustomerInterface
*/
private $customer;
@@ -309,10 +318,14 @@ protected function getMultilineFieldConfig($attributeCode, array $attributeConfi
}
/**
+ * Returns default attribute value.
+ *
* @param string $attributeCode
+ * @throws NoSuchEntityException
+ * @throws LocalizedException
* @return null|string
*/
- protected function getDefaultValue($attributeCode)
+ protected function getDefaultValue($attributeCode): ?string
{
if ($attributeCode === 'country_id') {
return $this->directoryHelper->getDefaultCountry();
@@ -346,9 +359,13 @@ protected function getDefaultValue($attributeCode)
}
/**
- * @return \Magento\Customer\Api\Data\CustomerInterface|null
+ * Returns logged customer.
+ *
+ * @throws NoSuchEntityException
+ * @throws LocalizedException
+ * @return CustomerInterface|null
*/
- protected function getCustomer()
+ protected function getCustomer(): ?CustomerInterface
{
if (!$this->customer) {
if ($this->customerSession->isLoggedIn()) {
diff --git a/app/code/Magento/Checkout/Block/Onepage.php b/app/code/Magento/Checkout/Block/Onepage.php
index ca6b045ddbb5d..e01d5835b4cf0 100644
--- a/app/code/Magento/Checkout/Block/Onepage.php
+++ b/app/code/Magento/Checkout/Block/Onepage.php
@@ -38,7 +38,7 @@ class Onepage extends \Magento\Framework\View\Element\Template
protected $layoutProcessors;
/**
- * @var \Magento\Framework\Serialize\Serializer\Json
+ * @var \Magento\Framework\Serialize\SerializerInterface
*/
private $serializer;
@@ -48,8 +48,9 @@ class Onepage extends \Magento\Framework\View\Element\Template
* @param \Magento\Checkout\Model\CompositeConfigProvider $configProvider
* @param array $layoutProcessors
* @param array $data
- * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
- * @throws \RuntimeException
+ * @param \Magento\Framework\Serialize\Serializer\Json $serializer
+ * @param \Magento\Framework\Serialize\SerializerInterface $serializerInterface
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
@@ -57,7 +58,8 @@ public function __construct(
\Magento\Checkout\Model\CompositeConfigProvider $configProvider,
array $layoutProcessors = [],
array $data = [],
- \Magento\Framework\Serialize\Serializer\Json $serializer = null
+ \Magento\Framework\Serialize\Serializer\Json $serializer = null,
+ \Magento\Framework\Serialize\SerializerInterface $serializerInterface = null
) {
parent::__construct($context, $data);
$this->formKey = $formKey;
@@ -65,12 +67,12 @@ public function __construct(
$this->jsLayout = isset($data['jsLayout']) && is_array($data['jsLayout']) ? $data['jsLayout'] : [];
$this->configProvider = $configProvider;
$this->layoutProcessors = $layoutProcessors;
- $this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
- ->get(\Magento\Framework\Serialize\Serializer\Json::class);
+ $this->serializer = $serializerInterface ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
}
/**
- * @return string
+ * @inheritdoc
*/
public function getJsLayout()
{
@@ -78,7 +80,7 @@ public function getJsLayout()
$this->jsLayout = $processor->process($this->jsLayout);
}
- return json_encode($this->jsLayout, JSON_HEX_TAG);
+ return $this->serializer->serialize($this->jsLayout);
}
/**
@@ -115,11 +117,13 @@ public function getBaseUrl()
}
/**
+ * Retrieve serialized checkout config.
+ *
* @return bool|string
* @since 100.2.0
*/
public function getSerializedCheckoutConfig()
{
- return json_encode($this->getCheckoutConfig(), JSON_HEX_TAG);
+ return $this->serializer->serialize($this->getCheckoutConfig());
}
}
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
index 464ccc1913335..2c5444da6d974 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/CheckoutActionGroup.xml
@@ -187,16 +187,16 @@
-
-
-
-
+
+
+
+
-
-
+
+
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml
index 6e5f127eefc18..15c157a982643 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/IdentityOfDefaultBillingAndShippingAddressActionGroup.xml
@@ -5,9 +5,9 @@
* See COPYING.txt for license details.
*/
-->
-
+
@@ -18,5 +18,4 @@
-
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml
index 72c5648991ef5..24ed05583b6fb 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontProductCartActionGroup.xml
@@ -78,20 +78,20 @@
-
-
-
-
+
+
+
+
-
+
-
-
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontShippmentFromActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontShippmentFromActionGroup.xml
index 354ad6d2b44ba..d3d96cb9c743c 100644
--- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontShippmentFromActionGroup.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontShippmentFromActionGroup.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
@@ -26,4 +26,4 @@
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml b/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml
index dc82932ec5ca7..7fc349bf9f05c 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Data/CountryData.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
- Bahamas
@@ -35,4 +35,4 @@
- United Kingdom
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml
index ab82d9fdd93b5..0d3c6e419cc07 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml
@@ -31,5 +31,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml
index d84df3401bab0..8d14a9a561900 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml
@@ -23,6 +23,6 @@
-
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml
index 34819f641cbc9..bc65f8a2c0816 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml
@@ -16,6 +16,7 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml
index d0ef8d347efb5..0d692e4ab143e 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml
@@ -12,5 +12,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml
index 89b3a25b45e3c..2039128ac2de3 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Section/IdentityOfDefaultBillingAndShippingAddressSection.xml
@@ -7,8 +7,7 @@
-->
-
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml
index 269ca94b3f772..f3807388399b8 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckoutSpecificDestinationsTest.xml
@@ -1,86 +1,86 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml
index 35e0058440f6e..5335ec2ad775d 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml
@@ -96,14 +96,10 @@
-
-
-
-
-
-
-
-
+
+
+
+
@@ -157,14 +153,10 @@
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
index 371826c9e7841..65627787e2a05 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml
@@ -96,14 +96,10 @@
-
-
-
-
-
-
-
-
+
+
+
+
@@ -157,14 +153,10 @@
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml
index 9664ec47420cc..89028e146c358 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/IdentityOfDefaultBillingAndShippingAddressTest.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml
new file mode 100644
index 0000000000000..693c05684f292
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontCheckCustomerInfoCreatedByGuestTest.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml
new file mode 100644
index 0000000000000..fb80b4880a6f4
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontUpdateShoppingCartWhileUpdateMinicartTest.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml
index cc5e723c72ea0..8537e10ce5a03 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerPlaceOrderWithNewAddressesThatWasEditedTest.xml
@@ -79,8 +79,12 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml
new file mode 100644
index 0000000000000..913eb34b34d07
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontOnePageCheckoutDataWhenChangeQtyTest.xml
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml
index 70f950f6f6c35..b0e1dead1fff9 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontShoppingCartCheckCustomerDefaultShippingAddressForVirtualQuoteTest.xml
@@ -19,18 +19,12 @@
-
-
-
-
-
- 1
-
+
+
-
@@ -48,8 +42,8 @@
-
-
+
+
diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml
index 2cc21df85ab67..4b3e18fb31877 100644
--- a/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml
+++ b/app/code/Magento/Checkout/Test/Mftf/Test/ZeroSubtotalOrdersWithProcessingStatusTest.xml
@@ -36,7 +36,6 @@
-
@@ -99,5 +98,6 @@
+
diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php
new file mode 100644
index 0000000000000..23840da97bd47
--- /dev/null
+++ b/app/code/Magento/Checkout/Test/Unit/Block/Checkout/AttributeMergerTest.php
@@ -0,0 +1,121 @@
+customerRepository = $this->createMock(CustomerRepository::class);
+ $this->customerSession = $this->createMock(CustomerSession::class);
+ $this->addressHelper = $this->createMock(AddressHelper::class);
+ $this->directoryHelper = $this->createMock(DirectoryHelper::class);
+
+ $this->attributeMerger = new AttributeMerger(
+ $this->addressHelper,
+ $this->customerSession,
+ $this->customerRepository,
+ $this->directoryHelper
+ );
+ }
+
+ /**
+ * Tests of element attributes merging.
+ *
+ * @param String $validationRule - validation rule.
+ * @param String $expectedValidation - expected mapped validation.
+ * @dataProvider validationRulesDataProvider
+ */
+ public function testMerge(String $validationRule, String $expectedValidation): void
+ {
+ $elements = [
+ 'field' => [
+ 'visible' => true,
+ 'formElement' => 'input',
+ 'label' => __('City'),
+ 'value' => null,
+ 'sortOrder' => 1,
+ 'validation' => [
+ 'input_validation' => $validationRule
+ ],
+ ]
+ ];
+
+ $actualResult = $this->attributeMerger->merge(
+ $elements,
+ 'provider',
+ 'dataScope',
+ ['field' =>
+ [
+ 'validation' => ['length' => true]
+ ]
+ ]
+ );
+
+ $expectedResult = [
+ $expectedValidation => true,
+ 'length' => true
+ ];
+
+ self::assertEquals($expectedResult, $actualResult['field']['validation']);
+ }
+
+ /**
+ * Provides possible validation types.
+ *
+ * @return array
+ */
+ public function validationRulesDataProvider(): array
+ {
+ return [
+ ['alpha', 'validate-alpha'],
+ ['numeric', 'validate-number'],
+ ['alphanumeric', 'validate-alphanum'],
+ ['alphanum-with-spaces', 'validate-alphanum-with-spaces'],
+ ['url', 'validate-url'],
+ ['email', 'email2'],
+ ['length', 'validate-length']
+ ];
+ }
+}
diff --git a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
index 54f77c95148ac..b54339aa2c1d8 100644
--- a/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
+++ b/app/code/Magento/Checkout/Test/Unit/Block/OnepageTest.php
@@ -35,7 +35,7 @@ class OnepageTest extends \PHPUnit\Framework\TestCase
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
- private $serializer;
+ private $serializerMock;
protected function setUp()
{
@@ -49,7 +49,7 @@ protected function setUp()
\Magento\Checkout\Block\Checkout\LayoutProcessorInterface::class
);
- $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class);
+ $this->serializerMock = $this->createMock(\Magento\Framework\Serialize\Serializer\JsonHexTag::class);
$this->model = new \Magento\Checkout\Block\Onepage(
$contextMock,
@@ -57,7 +57,8 @@ protected function setUp()
$this->configProviderMock,
[$this->layoutProcessorMock],
[],
- $this->serializer
+ $this->serializerMock,
+ $this->serializerMock
);
}
@@ -93,6 +94,7 @@ public function testGetJsLayout()
$processedLayout = ['layout' => ['processed' => true]];
$jsonLayout = '{"layout":{"processed":true}}';
$this->layoutProcessorMock->expects($this->once())->method('process')->with([])->willReturn($processedLayout);
+ $this->serializerMock->expects($this->once())->method('serialize')->willReturn($jsonLayout);
$this->assertEquals($jsonLayout, $this->model->getJsLayout());
}
@@ -101,6 +103,7 @@ public function testGetSerializedCheckoutConfig()
{
$checkoutConfig = ['checkout', 'config'];
$this->configProviderMock->expects($this->once())->method('getConfig')->willReturn($checkoutConfig);
+ $this->serializerMock->expects($this->once())->method('serialize')->willReturn(json_encode($checkoutConfig));
$this->assertEquals(json_encode($checkoutConfig), $this->model->getSerializedCheckoutConfig());
}
diff --git a/app/code/Magento/Checkout/etc/frontend/di.xml b/app/code/Magento/Checkout/etc/frontend/di.xml
index d80f88786c87b..00bcd2a27005a 100644
--- a/app/code/Magento/Checkout/etc/frontend/di.xml
+++ b/app/code/Magento/Checkout/etc/frontend/di.xml
@@ -59,6 +59,7 @@
- Magento\Checkout\Block\Checkout\TotalsProcessor
- Magento\Checkout\Block\Checkout\DirectoryDataProcessor
+ Magento\Framework\Serialize\Serializer\JsonHexTag
diff --git a/app/code/Magento/Checkout/etc/frontend/sections.xml b/app/code/Magento/Checkout/etc/frontend/sections.xml
index 35733a6119a25..90c2878f501cf 100644
--- a/app/code/Magento/Checkout/etc/frontend/sections.xml
+++ b/app/code/Magento/Checkout/etc/frontend/sections.xml
@@ -46,7 +46,6 @@
-
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js b/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
index 22b37b2da0b2f..1858ce946fb07 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/checkout-data.js
@@ -10,7 +10,8 @@
*/
define([
'jquery',
- 'Magento_Customer/js/customer-data'
+ 'Magento_Customer/js/customer-data',
+ 'jquery/jquery-storageapi'
], function ($, storage) {
'use strict';
@@ -23,6 +24,22 @@ define([
storage.set(cacheKey, data);
},
+ /**
+ * @return {*}
+ */
+ initData = function () {
+ return {
+ 'selectedShippingAddress': null, //Selected shipping address pulled from persistence storage
+ 'shippingAddressFromData': null, //Shipping address pulled from persistence storage
+ 'newCustomerShippingAddress': null, //Shipping address pulled from persistence storage for customer
+ 'selectedShippingRate': null, //Shipping rate pulled from persistence storage
+ 'selectedPaymentMethod': null, //Payment method pulled from persistence storage
+ 'selectedBillingAddress': null, //Selected billing address pulled from persistence storage
+ 'billingAddressFromData': null, //Billing address pulled from persistence storage
+ 'newCustomerBillingAddress': null //Billing address pulled from persistence storage for new customer
+ };
+ },
+
/**
* @return {*}
*/
@@ -30,17 +47,12 @@ define([
var data = storage.get(cacheKey)();
if ($.isEmptyObject(data)) {
- data = {
- 'selectedShippingAddress': null, //Selected shipping address pulled from persistence storage
- 'shippingAddressFromData': null, //Shipping address pulled from persistence storage
- 'newCustomerShippingAddress': null, //Shipping address pulled from persistence storage for customer
- 'selectedShippingRate': null, //Shipping rate pulled from persistence storage
- 'selectedPaymentMethod': null, //Payment method pulled from persistence storage
- 'selectedBillingAddress': null, //Selected billing address pulled from persistence storage
- 'billingAddressFromData': null, //Billing address pulled from persistence storage
- 'newCustomerBillingAddress': null //Billing address pulled from persistence storage for new customer
- };
- saveData(data);
+ data = $.initNamespaceStorage('mage-cache-storage').localStorage.get(cacheKey);
+
+ if ($.isEmptyObject(data)) {
+ data = initData();
+ saveData(data);
+ }
}
return data;
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js b/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js
index c3c5b9d68cec0..c07878fcaea92 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/place-order.js
@@ -9,9 +9,10 @@ define(
[
'mage/storage',
'Magento_Checkout/js/model/error-processor',
- 'Magento_Checkout/js/model/full-screen-loader'
+ 'Magento_Checkout/js/model/full-screen-loader',
+ 'Magento_Customer/js/customer-data'
],
- function (storage, errorProcessor, fullScreenLoader) {
+ function (storage, errorProcessor, fullScreenLoader, customerData) {
'use strict';
return function (serviceUrl, payload, messageContainer) {
@@ -23,6 +24,23 @@ define(
function (response) {
errorProcessor.process(response, messageContainer);
}
+ ).success(
+ function (response) {
+ var clearData = {
+ 'selectedShippingAddress': null,
+ 'shippingAddressFromData': null,
+ 'newCustomerShippingAddress': null,
+ 'selectedShippingRate': null,
+ 'selectedPaymentMethod': null,
+ 'selectedBillingAddress': null,
+ 'billingAddressFromData': null,
+ 'newCustomerBillingAddress': null
+ };
+
+ if (response.responseType !== 'error') {
+ customerData.set('checkout-data', clearData);
+ }
+ }
).always(
function () {
fullScreenLoader.stopLoader();
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js
index a95471d90dab8..0a5334a42c7e5 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/postcode-validator.js
@@ -14,11 +14,13 @@ define([
/**
* @param {*} postCode
* @param {*} countryId
+ * @param {Array} postCodesPatterns
* @return {Boolean}
*/
- validate: function (postCode, countryId) {
- var patterns = window.checkoutConfig.postCodes[countryId],
- pattern, regex;
+ validate: function (postCode, countryId, postCodesPatterns) {
+ var pattern, regex,
+ patterns = postCodesPatterns ? postCodesPatterns[countryId] :
+ window.checkoutConfig.postCodes[countryId];
this.validatedPostCodeExample = [];
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js
index fde88ebadb393..8b07c02e4d380 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/model/shipping-rates-validator.js
@@ -42,6 +42,7 @@ define([
return {
validateAddressTimeout: 0,
+ validateZipCodeTimeout: 0,
validateDelay: 2000,
/**
@@ -133,16 +134,20 @@ define([
});
} else {
element.on('value', function () {
+ clearTimeout(self.validateZipCodeTimeout);
+ self.validateZipCodeTimeout = setTimeout(function () {
+ if (element.index === postcodeElementName) {
+ self.postcodeValidation(element);
+ } else {
+ $.each(postcodeElements, function (index, elem) {
+ self.postcodeValidation(elem);
+ });
+ }
+ }, delay);
+
if (!formPopUpState.isVisible()) {
clearTimeout(self.validateAddressTimeout);
self.validateAddressTimeout = setTimeout(function () {
- if (element.index === postcodeElementName) {
- self.postcodeValidation(element);
- } else {
- $.each(postcodeElements, function (index, elem) {
- self.postcodeValidation(elem);
- });
- }
self.validateFields();
}, delay);
}
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
index dde1ad72ba15e..e66c66006246c 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/sidebar.js
@@ -25,6 +25,7 @@ define([
}
},
scrollHeight: 0,
+ shoppingCartUrl: window.checkout.shoppingCartUrl,
/**
* Create sidebar.
@@ -227,6 +228,10 @@ define([
if (!_.isUndefined(productData)) {
$(document).trigger('ajax:updateCartItemQty');
+
+ if (window.location.href === this.shoppingCartUrl) {
+ window.location.reload(false);
+ }
}
this._hideItemButton(elem);
},
diff --git a/app/code/Magento/CheckoutAgreements/etc/di.xml b/app/code/Magento/CheckoutAgreements/etc/di.xml
index 081e3daa781ff..a8ff8f5941f96 100644
--- a/app/code/Magento/CheckoutAgreements/etc/di.xml
+++ b/app/code/Magento/CheckoutAgreements/etc/di.xml
@@ -23,7 +23,7 @@
-
+
Magento\CheckoutAgreements\Model\Api\SearchCriteria\CollectionProcessor
diff --git a/app/code/Magento/Cms/Model/Page/Source/PageLayout.php b/app/code/Magento/Cms/Model/Page/Source/PageLayout.php
index fb759348759b2..23a452c0fe58c 100644
--- a/app/code/Magento/Cms/Model/Page/Source/PageLayout.php
+++ b/app/code/Magento/Cms/Model/Page/Source/PageLayout.php
@@ -20,6 +20,7 @@ class PageLayout implements OptionSourceInterface
/**
* @var array
+ * @deprecated since the cache is now handled by \Magento\Theme\Model\PageLayout\Config\Builder::$configFiles
*/
protected $options;
@@ -34,16 +35,10 @@ public function __construct(BuilderInterface $pageLayoutBuilder)
}
/**
- * Get options
- *
- * @return array
+ * @inheritdoc
*/
public function toOptionArray()
{
- if ($this->options !== null) {
- return $this->options;
- }
-
$configOptions = $this->pageLayoutBuilder->getPageLayoutsConfig()->getOptions();
$options = [];
foreach ($configOptions as $key => $value) {
@@ -54,6 +49,6 @@ public function toOptionArray()
}
$this->options = $options;
- return $this->options;
+ return $options;
}
}
diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php
index 9aab54b02bc14..30e817713755c 100644
--- a/app/code/Magento/Cms/Model/ResourceModel/Block.php
+++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php
@@ -95,9 +95,11 @@ protected function _beforeSave(AbstractModel $object)
}
/**
+ * Get block id.
+ *
* @param AbstractModel $object
* @param mixed $value
- * @param null $field
+ * @param string $field
* @return bool|int|string
* @throws LocalizedException
* @throws \Exception
@@ -183,10 +185,12 @@ public function getIsUniqueBlockToStores(AbstractModel $object)
$entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class);
$linkField = $entityMetadata->getLinkField();
- if ($this->_storeManager->isSingleStoreMode()) {
- $stores = [Store::DEFAULT_STORE_ID];
- } else {
- $stores = (array)$object->getData('store_id');
+ $stores = (array)$object->getData('store_id');
+ $isDefaultStore = $this->_storeManager->isSingleStoreMode()
+ || array_search(Store::DEFAULT_STORE_ID, $stores) !== false;
+
+ if (!$isDefaultStore) {
+ $stores[] = Store::DEFAULT_STORE_ID;
}
$select = $this->getConnection()->select()
@@ -196,8 +200,11 @@ public function getIsUniqueBlockToStores(AbstractModel $object)
'cb.' . $linkField . ' = cbs.' . $linkField,
[]
)
- ->where('cb.identifier = ?', $object->getData('identifier'))
- ->where('cbs.store_id IN (?)', $stores);
+ ->where('cb.identifier = ? ', $object->getData('identifier'));
+
+ if (!$isDefaultStore) {
+ $select->where('cbs.store_id IN (?)', $stores);
+ }
if ($object->getId()) {
$select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId());
@@ -236,6 +243,8 @@ public function lookupStoreIds($id)
}
/**
+ * Save an object.
+ *
* @param AbstractModel $object
* @return $this
* @throws \Exception
diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml
index 75e059f620c2d..e7379d3c7336f 100644
--- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml
+++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CMSActionGroup.xml
@@ -44,4 +44,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml
new file mode 100644
index 0000000000000..a4b88c544de88
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/CreateNewPageWithWidgetActionGroup.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Data/BlockData.xml b/app/code/Magento/Cms/Test/Mftf/Data/BlockData.xml
new file mode 100644
index 0000000000000..dea047ec43568
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Data/BlockData.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Sales25off
+ Sales25off
+ All Store Views
+ sales25off everything!
+ 0
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Data/NewCMSPageData.xml b/app/code/Magento/Cms/Test/Mftf/Data/NewCMSPageData.xml
new file mode 100644
index 0000000000000..61dfb051d101e
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Data/NewCMSPageData.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ CMSpage
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsEditBlockPage.xml b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsEditBlockPage.xml
new file mode 100644
index 0000000000000..3fd100ee02aa2
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Page/AdminCmsEditBlockPage.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/AdminBlockGridSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/AdminBlockGridSection.xml
new file mode 100644
index 0000000000000..ab15570a01f40
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Section/AdminBlockGridSection.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml
index 4c6014af51264..2efa7f62fc4ec 100644
--- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewBlockBlockActionsSection.xml
@@ -17,6 +17,7 @@
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml
index 42f8f4d00ee9f..a340d0af1e7a1 100644
--- a/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsNewPagePageActionsSection.xml
@@ -22,5 +22,6 @@
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml
index ded94eab92042..1adb781a67536 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCMSPageLinkTypeTest.xml
@@ -36,7 +36,7 @@
-
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml
index 2586ffc11d086..394d79bda1ab3 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductListTypeTest.xml
@@ -42,7 +42,7 @@
-
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml
index 691a99a73b90b..862f51ea72fad 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyComparedProductsTypeTest.xml
@@ -41,7 +41,7 @@
-
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml
index 9cdbccd1f8c32..298aed917fc18 100644
--- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml
+++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithRecentlyViewedProductsTypeTest.xml
@@ -40,7 +40,7 @@
-
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml
new file mode 100644
index 0000000000000..c3c92dc59c288
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml
new file mode 100644
index 0000000000000..65fabfe25e817
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreViewLanguageCorrectSwitchTest.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php
index 54e0e17ab7ad6..a624823d02c13 100644
--- a/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Ui/Component/Listing/DataProviderTest.php
@@ -118,7 +118,8 @@ public function testPrepareMetadata()
'config' => [
'editorConfig' => [
'enabled' => false
- ]
+ ],
+ 'componentType' => \Magento\Ui\Component\Container::NAME
]
]
]
diff --git a/app/code/Magento/Cms/Ui/Component/DataProvider.php b/app/code/Magento/Cms/Ui/Component/DataProvider.php
index 5fc9c5a896037..b02dd6ba98ed0 100644
--- a/app/code/Magento/Cms/Ui/Component/DataProvider.php
+++ b/app/code/Magento/Cms/Ui/Component/DataProvider.php
@@ -13,6 +13,9 @@
use Magento\Framework\AuthorizationInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\Reporting;
+/**
+ * DataProvider for cms ui.
+ */
class DataProvider extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
{
/**
@@ -67,6 +70,8 @@ public function __construct(
}
/**
+ * Get authorization info.
+ *
* @deprecated 101.0.7
* @return AuthorizationInterface|mixed
*/
@@ -95,7 +100,8 @@ public function prepareMetadata()
'config' => [
'editorConfig' => [
'enabled' => false
- ]
+ ],
+ 'componentType' => \Magento\Ui\Component\Container::NAME
]
]
]
diff --git a/app/code/Magento/Config/Block/System/Config/Form.php b/app/code/Magento/Config/Block/System/Config/Form.php
index 81e39a83296d7..2a29fa33feb74 100644
--- a/app/code/Magento/Config/Block/System/Config/Form.php
+++ b/app/code/Magento/Config/Block/System/Config/Form.php
@@ -134,6 +134,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
* @param \Magento\Config\Block\System\Config\Form\Fieldset\Factory $fieldsetFactory
* @param \Magento\Config\Block\System\Config\Form\Field\Factory $fieldFactory
* @param array $data
+ * @param SettingChecker|null $settingChecker
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
@@ -143,13 +144,15 @@ public function __construct(
\Magento\Config\Model\Config\Structure $configStructure,
\Magento\Config\Block\System\Config\Form\Fieldset\Factory $fieldsetFactory,
\Magento\Config\Block\System\Config\Form\Field\Factory $fieldFactory,
- array $data = []
+ array $data = [],
+ SettingChecker $settingChecker = null
) {
parent::__construct($context, $registry, $formFactory, $data);
$this->_configFactory = $configFactory;
$this->_configStructure = $configStructure;
$this->_fieldsetFactory = $fieldsetFactory;
$this->_fieldFactory = $fieldFactory;
+ $this->settingChecker = $settingChecker ?: ObjectManager::getInstance()->get(SettingChecker::class);
$this->_scopeLabels = [
self::SCOPE_DEFAULT => __('[GLOBAL]'),
@@ -158,18 +161,6 @@ public function __construct(
];
}
- /**
- * @deprecated 100.1.2
- * @return SettingChecker
- */
- private function getSettingChecker()
- {
- if ($this->settingChecker === null) {
- $this->settingChecker = ObjectManager::getInstance()->get(SettingChecker::class);
- }
- return $this->settingChecker;
- }
-
/**
* Initialize objects required to render config form
*
@@ -366,9 +357,8 @@ protected function _initElement(
$sharedClass = $this->_getSharedCssClass($field);
$requiresClass = $this->_getRequiresCssClass($field, $fieldPrefix);
+ $isReadOnly = $this->isReadOnly($field, $path);
- $isReadOnly = $this->getElementVisibility()->isDisabled($field->getPath())
- ?: $this->getSettingChecker()->isReadOnly($path, $this->getScope(), $this->getStringScopeCode());
$formField = $fieldset->addField(
$elementId,
$field->getType(),
@@ -417,7 +407,7 @@ private function getFieldData(\Magento\Config\Model\Config\Structure\Element\Fie
{
$data = $this->getAppConfigDataValue($path);
- $placeholderValue = $this->getSettingChecker()->getPlaceholderValue(
+ $placeholderValue = $this->settingChecker->getPlaceholderValue(
$path,
$this->getScope(),
$this->getStringScopeCode()
@@ -541,7 +531,7 @@ public function getConfigValue($path)
}
/**
- * @return \Magento\Backend\Block\Widget\Form|\Magento\Framework\View\Element\AbstractBlock
+ * @inheritdoc
*/
protected function _beforeToHtml()
{
@@ -718,6 +708,7 @@ protected function _getAdditionalElementTypes()
*
* @TODO delete this methods when {^see above^} is done
* @return string
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function getSectionCode()
{
@@ -729,6 +720,7 @@ public function getSectionCode()
*
* @TODO delete this methods when {^see above^} is done
* @return string
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function getWebsiteCode()
{
@@ -740,6 +732,7 @@ public function getWebsiteCode()
*
* @TODO delete this methods when {^see above^} is done
* @return string
+ * @SuppressWarnings(PHPMD.RequestAwareBlockMethod)
*/
public function getStoreCode()
{
@@ -797,6 +790,26 @@ private function getAppConfig()
return $this->appConfig;
}
+ /**
+ * Check Path is Readonly
+ *
+ * @param \Magento\Config\Model\Config\Structure\Element\Field $field
+ * @param string $path
+ * @return boolean
+ */
+ private function isReadOnly(\Magento\Config\Model\Config\Structure\Element\Field $field, $path)
+ {
+ $isReadOnly = $this->settingChecker->isReadOnly(
+ $path,
+ ScopeConfigInterface::SCOPE_TYPE_DEFAULT
+ );
+ if (!$isReadOnly) {
+ $isReadOnly = $this->getElementVisibility()->isDisabled($field->getPath())
+ ?: $this->settingChecker->isReadOnly($path, $this->getScope(), $this->getStringScopeCode());
+ }
+ return $isReadOnly;
+ }
+
/**
* Retrieve deployment config data value by path
*
diff --git a/app/code/Magento/Config/Test/Mftf/Data/AllowGuestCheckoutData.xml b/app/code/Magento/Config/Test/Mftf/Data/AllowGuestCheckoutData.xml
new file mode 100644
index 0000000000000..f89cdf1a87b31
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Data/AllowGuestCheckoutData.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ AllowGuestCheckoutYes
+
+
+ 1
+
+
+
+ AllowGuestCheckoutNo
+
+
+ 0
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Data/CountryOptionConfigData.xml b/app/code/Magento/Config/Test/Mftf/Data/CountryOptionConfigData.xml
new file mode 100644
index 0000000000000..53ca46e746206
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Data/CountryOptionConfigData.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ AdminAccountAllowCountryUS
+
+
+ US
+
+
+
+ DefaultAdminAccountAllowCountry
+
+
+ 0
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml b/app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml
new file mode 100644
index 0000000000000..5647283fae181
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Data/LocaleOptionsData.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ setLocaleOptionsFrance
+
+
+ fr_FR
+
+
+
+ setLocaleOptionsUSA
+
+
+ en_US
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml b/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml
index 75dc19dc99c8e..85188eb6e04cb 100644
--- a/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml
+++ b/app/code/Magento/Config/Test/Mftf/Data/SystemConfigData.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
Yes
diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml
new file mode 100644
index 0000000000000..052d9b6574774
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Metadata/allow_guest_checkout-meta.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml
new file mode 100644
index 0000000000000..055a9896cd2d2
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Metadata/locale_options_config-meta.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+ string
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml
new file mode 100644
index 0000000000000..bd16c225af51d
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Metadata/system_config-countries-meta.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+ string
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml b/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml
index 37b8414d1f396..e7544c4e8ae28 100644
--- a/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml
+++ b/app/code/Magento/Config/Test/Mftf/Metadata/system_config-meta.xml
@@ -5,8 +5,9 @@
* See COPYING.txt for license details.
*/
-->
+
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd">
diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml
index c48f33ba06b3b..e999dbc42a6af 100644
--- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml
+++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml
@@ -17,5 +17,9 @@
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml
new file mode 100644
index 0000000000000..b0a7ee07ddad0
--- /dev/null
+++ b/app/code/Magento/Config/Test/Mftf/Test/CheckingCountryDropDownWithOneAllowedCountryTest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php b/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php
index 93650dd62657c..4e260b0fb2bb1 100644
--- a/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php
+++ b/app/code/Magento/Config/Test/Unit/Block/System/Config/FormTest.php
@@ -102,6 +102,9 @@ protected function setUp()
\Magento\Config\Block\System\Config\Form\Fieldset\Factory::class
);
$this->_fieldFactoryMock = $this->createMock(\Magento\Config\Block\System\Config\Form\Field\Factory::class);
+ $settingCheckerMock = $this->getMockBuilder(SettingChecker::class)
+ ->disableOriginalConstructor()
+ ->getMock();
$this->_coreConfigMock = $this->createMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
$this->_backendConfigMock = $this->createMock(\Magento\Config\Model\Config::class);
@@ -153,6 +156,7 @@ protected function setUp()
'fieldsetFactory' => $this->_fieldsetFactoryMock,
'fieldFactory' => $this->_fieldFactoryMock,
'context' => $context,
+ 'settingChecker' => $settingCheckerMock,
];
$objectArguments = $helper->getConstructArguments(\Magento\Config\Block\System\Config\Form::class, $data);
@@ -532,7 +536,7 @@ public function testInitFields(
$elementVisibilityMock = $this->getMockBuilder(ElementVisibilityInterface::class)
->getMockForAbstractClass();
- $elementVisibilityMock->expects($this->once())
+ $elementVisibilityMock->expects($this->any())
->method('isDisabled')
->willReturn($isDisabled);
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/ProductIdentitiesExtender.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/ProductIdentitiesExtender.php
new file mode 100644
index 0000000000000..92b7ab0d88ea8
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/Frontend/ProductIdentitiesExtender.php
@@ -0,0 +1,48 @@
+configurableType = $configurableType;
+ }
+
+ /**
+ * Add child identities to product identities
+ *
+ * @param Product $subject
+ * @param array $identities
+ * @return array
+ */
+ public function afterGetIdentities(Product $subject, array $identities): array
+ {
+ foreach ($this->configurableType->getChildrenIds($subject->getId()) as $childIds) {
+ foreach ($childIds as $childId) {
+ $identities[] = Product::CACHE_TAG . '_' . $childId;
+ }
+ }
+
+ return array_unique($identities);
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php
new file mode 100644
index 0000000000000..1ed4432347b7a
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Plugin/SalesRule/Model/Rule/Condition/Product.php
@@ -0,0 +1,69 @@
+getProductToValidate($subject, $model);
+ if ($model->getProduct() !== $product) {
+ // We need to replace product only for validation and keep original product for all other cases.
+ $clone = clone $model;
+ $clone->setProduct($product);
+ $model = $clone;
+ }
+
+ return [$model];
+ }
+
+ /**
+ * Select proper product for validation.
+ *
+ * @param \Magento\SalesRule\Model\Rule\Condition\Product $subject
+ * @param \Magento\Framework\Model\AbstractModel $model
+ *
+ * @return \Magento\Catalog\Api\Data\ProductInterface|\Magento\Catalog\Model\Product
+ */
+ private function getProductToValidate(
+ \Magento\SalesRule\Model\Rule\Condition\Product $subject,
+ \Magento\Framework\Model\AbstractModel $model
+ ) {
+ /** @var \Magento\Catalog\Model\Product $product */
+ $product = $model->getProduct();
+
+ $attrCode = $subject->getAttribute();
+
+ /* Check for attributes which are not available for configurable products */
+ if ($product->getTypeId() == Configurable::TYPE_CODE && !$product->hasData($attrCode)) {
+ /** @var \Magento\Catalog\Model\AbstractModel $childProduct */
+ $childProduct = current($model->getChildren())->getProduct();
+ if ($childProduct->hasData($attrCode)) {
+ $product = $childProduct;
+ }
+ }
+
+ return $product;
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php
index 611523a60b06d..447ba16d72710 100644
--- a/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php
+++ b/app/code/Magento/ConfigurableProduct/Pricing/Render/TierPriceBox.php
@@ -5,6 +5,8 @@
*/
namespace Magento\ConfigurableProduct\Pricing\Render;
+use Magento\Catalog\Pricing\Price\TierPrice;
+
/**
* Responsible for displaying tier price box on configurable product page.
*
@@ -17,9 +19,27 @@ class TierPriceBox extends FinalPriceBox
*/
public function toHtml()
{
- // Hide tier price block in case of MSRP.
- if (!$this->isMsrpPriceApplicable()) {
+ // Hide tier price block in case of MSRP or in case when no options with tier price.
+ if (!$this->isMsrpPriceApplicable() && $this->isTierPriceApplicable()) {
return parent::toHtml();
}
}
+
+ /**
+ * Check if at least one of simple products has tier price.
+ *
+ * @return bool
+ */
+ private function isTierPriceApplicable()
+ {
+ $product = $this->getSaleableItem();
+ foreach ($product->getTypeInstance()->getUsedProducts($product) as $simpleProduct) {
+ if ($simpleProduct->isSalable() &&
+ !empty($simpleProduct->getPriceInfo()->getPrice(TierPrice::PRICE_CODE)->getTierPriceList())
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml
new file mode 100644
index 0000000000000..4328159d6e930
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminAddOptionsToAttributeActionGroup.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml
index 0ac1914040d25..d2abfc7977519 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminConfigurableProductActionGroup.xml
@@ -130,4 +130,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml
index 95533057608f2..c4ad02ee14134 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/ConfigurableProductAttributeNameDesignActionGroup.xml
@@ -7,8 +7,7 @@
-->
-
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
@@ -168,5 +167,4 @@
-
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml
index 73a668fd2fefd..0018f5996c9bc 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Data/ConfigurableProductAttributeNameDesignData.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
Shoes
60
@@ -31,5 +31,4 @@
configurable
element.disabled is not a function
-
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml
index 4289638352990..6e8303e6baead 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminChooseAffectedAttributeSetSection.xml
@@ -10,5 +10,6 @@
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
-
\ No newline at end of file
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml
index 7901b6f2290c9..9b4798c95ec72 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminCreateProductConfigurationsPanelSection.xml
@@ -16,6 +16,8 @@
+
+
@@ -35,6 +37,7 @@
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml
index 44077888f8bc0..658e7a5fec9b3 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml
@@ -9,6 +9,15 @@
+
+
+
+
+
+
+
+
+
@@ -20,5 +29,6 @@
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml
index f2caef1717e84..c5d6abd89edbf 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminProductFormConfigurationsSection.xml
@@ -18,10 +18,13 @@
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml
index b3077d9d5d566..ea5638f6816c9 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/ConfigurableProductAttributeNameDesignSection.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
-
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml
new file mode 100644
index 0000000000000..dd641fd370ba7
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml
index 48f46a1205ec3..24af7d44e8261 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductCreateTest.xml
@@ -68,4 +68,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml
index 454f9f5f29a7a..c303e4d19db81 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml
new file mode 100644
index 0000000000000..b8dfec102fdf4
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ProductsQtyReturnAfterOrderCancelTest.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml
index 72fce95ade68d..57c45ee1e8997 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml
+++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml
@@ -153,9 +153,9 @@
-
-
-
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/Frontend/ProductIdentitiesExtenderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/Frontend/ProductIdentitiesExtenderTest.php
new file mode 100644
index 0000000000000..b4fb5ccfaa558
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/Frontend/ProductIdentitiesExtenderTest.php
@@ -0,0 +1,77 @@
+product = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getId'])
+ ->getMock();
+
+ $this->configurableTypeMock = $this->getMockBuilder(Configurable::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->plugin = new ProductIdentitiesExtender($this->configurableTypeMock);
+ }
+
+ public function testAfterGetIdentities()
+ {
+ $identities = [
+ 'SomeCacheId',
+ 'AnotherCacheId',
+ ];
+ $productId = 12345;
+ $childIdentities = [
+ 0 => [1, 2, 5, 100500]
+ ];
+ $expectedIdentities = [
+ 'SomeCacheId',
+ 'AnotherCacheId',
+ Product::CACHE_TAG . '_' . 1,
+ Product::CACHE_TAG . '_' . 2,
+ Product::CACHE_TAG . '_' . 5,
+ Product::CACHE_TAG . '_' . 100500,
+ ];
+
+ $this->product->expects($this->once())
+ ->method('getId')
+ ->willReturn($productId);
+
+ $this->configurableTypeMock->expects($this->once())
+ ->method('getChildrenIds')
+ ->with($productId)
+ ->willReturn($childIdentities);
+
+ $productIdentities = $this->plugin->afterGetIdentities($this->product, $identities);
+ $this->assertEquals($expectedIdentities, $productIdentities);
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php
new file mode 100644
index 0000000000000..80979148c4959
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Plugin/SalesRule/Model/Rule/Condition/ProductTest.php
@@ -0,0 +1,226 @@
+objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+ $this->validator = $this->createValidator();
+ $this->validatorPlugin = $this->objectManager->getObject(ValidatorPlugin::class);
+ }
+
+ /**
+ * @return \Magento\SalesRule\Model\Rule\Condition\Product
+ */
+ private function createValidator(): SalesRuleProduct
+ {
+ /** @var Context|\PHPUnit_Framework_MockObject_MockObject $contextMock */
+ $contextMock = $this->getMockBuilder(Context::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var Data|\PHPUnit_Framework_MockObject_MockObject $backendHelperMock */
+ $backendHelperMock = $this->getMockBuilder(Data::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var Config|\PHPUnit_Framework_MockObject_MockObject $configMock */
+ $configMock = $this->getMockBuilder(Config::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var ProductFactory|\PHPUnit_Framework_MockObject_MockObject $productFactoryMock */
+ $productFactoryMock = $this->getMockBuilder(ProductFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject $productRepositoryMock */
+ $productRepositoryMock = $this->getMockBuilder(ProductRepositoryInterface::class)
+ ->getMockForAbstractClass();
+ $attributeLoaderInterfaceMock = $this->getMockBuilder(AbstractEntity::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAttributesByCode'])
+ ->getMock();
+ $attributeLoaderInterfaceMock
+ ->expects($this->any())
+ ->method('getAttributesByCode')
+ ->willReturn([]);
+ /** @var Product|\PHPUnit_Framework_MockObject_MockObject $productMock */
+ $productMock = $this->getMockBuilder(Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['loadAllAttributes', 'getConnection', 'getTable'])
+ ->getMock();
+ $productMock->expects($this->any())
+ ->method('loadAllAttributes')
+ ->willReturn($attributeLoaderInterfaceMock);
+ /** @var Collection|\PHPUnit_Framework_MockObject_MockObject $collectionMock */
+ $collectionMock = $this->getMockBuilder(Collection::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var FormatInterface|\PHPUnit_Framework_MockObject_MockObject $formatMock */
+ $formatMock = new Format(
+ $this->getMockBuilder(ScopeResolverInterface::class)->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder(ResolverInterface::class)->disableOriginalConstructor()->getMock(),
+ $this->getMockBuilder(CurrencyFactory::class)->disableOriginalConstructor()->getMock()
+ );
+
+ return new SalesRuleProduct(
+ $contextMock,
+ $backendHelperMock,
+ $configMock,
+ $productFactoryMock,
+ $productRepositoryMock,
+ $productMock,
+ $collectionMock,
+ $formatMock
+ );
+ }
+
+ public function testChildIsUsedForValidation()
+ {
+ $configurableProductMock = $this->createProductMock();
+ $configurableProductMock
+ ->expects($this->any())
+ ->method('getTypeId')
+ ->willReturn(Configurable::TYPE_CODE);
+ $configurableProductMock
+ ->expects($this->any())
+ ->method('hasData')
+ ->with($this->equalTo('special_price'))
+ ->willReturn(false);
+
+ /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */
+ $item = $this->getMockBuilder(AbstractItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setProduct', 'getProduct', 'getChildren'])
+ ->getMockForAbstractClass();
+ $item->expects($this->any())
+ ->method('getProduct')
+ ->willReturn($configurableProductMock);
+
+ $simpleProductMock = $this->createProductMock();
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('getTypeId')
+ ->willReturn(Type::TYPE_SIMPLE);
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('hasData')
+ ->with($this->equalTo('special_price'))
+ ->willReturn(true);
+
+ $childItem = $this->getMockBuilder(AbstractItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getProduct'])
+ ->getMockForAbstractClass();
+ $childItem->expects($this->any())
+ ->method('getProduct')
+ ->willReturn($simpleProductMock);
+
+ $item->expects($this->any())
+ ->method('getChildren')
+ ->willReturn([$childItem]);
+ $item->expects($this->once())
+ ->method('setProduct')
+ ->with($simpleProductMock);
+
+ $this->validator->setAttribute('special_price');
+
+ $this->validatorPlugin->beforeValidate($this->validator, $item);
+ }
+
+ /**
+ * @return Product|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private function createProductMock(): \PHPUnit_Framework_MockObject_MockObject
+ {
+ $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'getAttribute',
+ 'getId',
+ 'setQuoteItemQty',
+ 'setQuoteItemPrice',
+ 'getTypeId',
+ 'hasData',
+ ])
+ ->getMock();
+ $productMock
+ ->expects($this->any())
+ ->method('setQuoteItemQty')
+ ->willReturnSelf();
+ $productMock
+ ->expects($this->any())
+ ->method('setQuoteItemPrice')
+ ->willReturnSelf();
+
+ return $productMock;
+ }
+
+ public function testChildIsNotUsedForValidation()
+ {
+ $simpleProductMock = $this->createProductMock();
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('getTypeId')
+ ->willReturn(Type::TYPE_SIMPLE);
+ $simpleProductMock
+ ->expects($this->any())
+ ->method('hasData')
+ ->with($this->equalTo('special_price'))
+ ->willReturn(true);
+
+ /* @var AbstractItem|\PHPUnit_Framework_MockObject_MockObject $item */
+ $item = $this->getMockBuilder(AbstractItem::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setProduct', 'getProduct'])
+ ->getMockForAbstractClass();
+ $item->expects($this->any())
+ ->method('getProduct')
+ ->willReturn($simpleProductMock);
+
+ $this->validator->setAttribute('special_price');
+
+ $this->validatorPlugin->beforeValidate($this->validator, $item);
+ }
+}
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index 102ed1314f864..0ae9ffde66f43 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -242,4 +242,7 @@
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml
index bb830c36b929d..df96829b354c8 100644
--- a/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/frontend/di.xml
@@ -10,4 +10,7 @@
+
+
+
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js
index 28e775b984b05..6bbab77a3a0ab 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/price-configurable.js
@@ -53,6 +53,8 @@ define([
if (isConfigurable) {
this.disable();
this.clear();
+ } else {
+ this.enable();
}
}
});
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js
index 8d27e3dc58a4a..6e82fd42692fc 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/variations/variations.js
@@ -383,12 +383,19 @@ define([
* Chose action for the form save button
*/
saveFormHandler: function () {
- this.serializeData();
+ this.formElement().validate();
+
+ if (this.formElement().source.get('params.invalid') === false) {
+ this.serializeData();
+ }
if (this.checkForNewAttributes()) {
this.formSaveParams = arguments;
this.attributeSetHandlerModal().openModal();
} else {
+ if (this.validateForm(this.formElement())) {
+ this.clearOutdatedData();
+ }
this.formElement().save(arguments[0], arguments[1]);
if (this.formElement().source.get('params.invalid')) {
@@ -397,6 +404,17 @@ define([
}
},
+ /**
+ * @param {Object} formElement
+ *
+ * Validates each form element and returns true, if all elements are valid.
+ */
+ validateForm: function (formElement) {
+ formElement.validate();
+
+ return !formElement.additionalInvalid && !formElement.source.get('params.invalid');
+ },
+
/**
* Serialize data for specific form fields
*
@@ -414,12 +432,27 @@ define([
if (this.source.data['configurable-matrix']) {
this.source.data['configurable-matrix-serialized'] =
JSON.stringify(this.source.data['configurable-matrix']);
- delete this.source.data['configurable-matrix'];
}
if (this.source.data['associated_product_ids']) {
this.source.data['associated_product_ids_serialized'] =
JSON.stringify(this.source.data['associated_product_ids']);
+ }
+ },
+
+ /**
+ * Clear outdated data for specific form fields
+ *
+ * Outdated fields:
+ * - configurable-matrix;
+ * - associated_product_ids.
+ */
+ clearOutdatedData: function () {
+ if (this.source.data['configurable-matrix']) {
+ delete this.source.data['configurable-matrix'];
+ }
+
+ if (this.source.data['associated_product_ids']) {
delete this.source.data['associated_product_ids'];
}
},
diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js
index 6357bbd6c7c0c..1df84d27a5c30 100644
--- a/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js
+++ b/app/code/Magento/ConfigurableProduct/view/frontend/web/js/configurable.js
@@ -376,7 +376,8 @@ define([
basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount),
optionFinalPrice,
optionPriceDiff,
- optionPrices = this.options.spConfig.optionPrices;
+ optionPrices = this.options.spConfig.optionPrices,
+ allowedProductMinPrice;
this._clearSelect(element);
element.options[0] = new Option('', '');
@@ -407,8 +408,8 @@ define([
if (typeof allowedProducts[0] !== 'undefined' &&
typeof optionPrices[allowedProducts[0]] !== 'undefined') {
-
- optionFinalPrice = parseFloat(optionPrices[allowedProducts[0]].finalPrice.amount);
+ allowedProductMinPrice = this._getAllowedProductWithMinPrice(allowedProducts);
+ optionFinalPrice = parseFloat(optionPrices[allowedProductMinPrice].finalPrice.amount);
optionPriceDiff = optionFinalPrice - basePrice;
if (optionPriceDiff !== 0) {
@@ -489,36 +490,27 @@ define([
_getPrices: function () {
var prices = {},
elements = _.toArray(this.options.settings),
- hasProductPrice = false,
- optionPriceDiff = 0,
- allowedProduct, optionPrices, basePrice, optionFinalPrice;
+ allowedProduct;
_.each(elements, function (element) {
var selected = element.options[element.selectedIndex],
config = selected && selected.config,
priceValue = {};
- if (config && config.allowedProducts.length === 1 && !hasProductPrice) {
- prices = {};
+ if (config && config.allowedProducts.length === 1) {
priceValue = this._calculatePrice(config);
- hasProductPrice = true;
} else if (element.value) {
allowedProduct = this._getAllowedProductWithMinPrice(config.allowedProducts);
- optionPrices = this.options.spConfig.optionPrices;
- basePrice = parseFloat(this.options.spConfig.prices.basePrice.amount);
-
- if (!_.isEmpty(allowedProduct)) {
- optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount);
- optionPriceDiff = optionFinalPrice - basePrice;
- }
-
- if (optionPriceDiff !== 0) {
- prices = {};
- priceValue = this._calculatePriceDifference(allowedProduct);
- }
+ priceValue = this._calculatePrice({
+ 'allowedProducts': [
+ allowedProduct
+ ]
+ });
}
- prices[element.attributeId] = priceValue;
+ if (!_.isEmpty(priceValue)) {
+ prices.prices = priceValue;
+ }
}, this);
return prices;
@@ -539,40 +531,15 @@ define([
_.each(allowedProducts, function (allowedProduct) {
optionFinalPrice = parseFloat(optionPrices[allowedProduct].finalPrice.amount);
- if (_.isEmpty(product)) {
+ if (_.isEmpty(product) || optionFinalPrice < optionMinPrice) {
optionMinPrice = optionFinalPrice;
product = allowedProduct;
}
-
- if (optionFinalPrice < optionMinPrice) {
- product = allowedProduct;
- }
}, this);
return product;
},
- /**
- * Calculate price difference for allowed product
- *
- * @param {*} allowedProduct - Product
- * @returns {*}
- * @private
- */
- _calculatePriceDifference: function (allowedProduct) {
- var displayPrices = $(this.options.priceHolderSelector).priceBox('option').prices,
- newPrices = this.options.spConfig.optionPrices[allowedProduct];
-
- _.each(displayPrices, function (price, code) {
-
- if (newPrices[code]) {
- displayPrices[code].amount = newPrices[code].amount - displayPrices[code].amount;
- }
- });
-
- return displayPrices;
- },
-
/**
* Returns prices for configured products
*
diff --git a/app/code/Magento/ConfigurableProductGraphQl/Model/ConfigurableProductTypeResolver.php b/app/code/Magento/ConfigurableProductGraphQl/Model/ConfigurableProductTypeResolver.php
index aae39800cdd30..eda2ce11daaf6 100644
--- a/app/code/Magento/ConfigurableProductGraphQl/Model/ConfigurableProductTypeResolver.php
+++ b/app/code/Magento/ConfigurableProductGraphQl/Model/ConfigurableProductTypeResolver.php
@@ -8,19 +8,25 @@
namespace Magento\ConfigurableProductGraphQl\Model;
use Magento\Framework\GraphQl\Query\Resolver\TypeResolverInterface;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable as Type;
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
class ConfigurableProductTypeResolver implements TypeResolverInterface
{
/**
- * {@inheritdoc}
+ * Configurable product type resolver code
*/
- public function resolveType(array $data) : string
+ const TYPE_RESOLVER = 'ConfigurableProduct';
+
+ /**
+ * @inheritdoc
+ */
+ public function resolveType(array $data): string
{
- if (isset($data['type_id']) && $data['type_id'] == 'configurable') {
- return 'ConfigurableProduct';
+ if (isset($data['type_id']) && $data['type_id'] == Type::TYPE_CODE) {
+ return self::TYPE_RESOLVER;
}
return '';
}
diff --git a/app/code/Magento/Customer/Block/Address/Book.php b/app/code/Magento/Customer/Block/Address/Book.php
index 8b38946a063db..04669446ffee9 100644
--- a/app/code/Magento/Customer/Block/Address/Book.php
+++ b/app/code/Magento/Customer/Block/Address/Book.php
@@ -6,8 +6,8 @@
namespace Magento\Customer\Block\Address;
use Magento\Customer\Api\AddressRepositoryInterface;
-use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\Address\Mapper;
+use Magento\Customer\Block\Address\Grid as AddressesGrid;
/**
* Customer address book block
@@ -24,7 +24,7 @@ class Book extends \Magento\Framework\View\Element\Template
protected $currentCustomer;
/**
- * @var CustomerRepositoryInterface
+ * @var \Magento\Customer\Api\CustomerRepositoryInterface
*/
protected $customerRepository;
@@ -43,33 +43,44 @@ class Book extends \Magento\Framework\View\Element\Template
*/
protected $addressMapper;
+ /**
+ * @var AddressesGrid
+ */
+ private $addressesGrid;
+
/**
* @param \Magento\Framework\View\Element\Template\Context $context
- * @param CustomerRepositoryInterface $customerRepository
+ * @param CustomerRepositoryInterface|null $customerRepository
* @param AddressRepositoryInterface $addressRepository
* @param \Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer
* @param \Magento\Customer\Model\Address\Config $addressConfig
* @param Mapper $addressMapper
* @param array $data
+ * @param AddressesGrid|null $addressesGrid
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
- CustomerRepositoryInterface $customerRepository,
+ \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository = null,
AddressRepositoryInterface $addressRepository,
\Magento\Customer\Helper\Session\CurrentCustomer $currentCustomer,
\Magento\Customer\Model\Address\Config $addressConfig,
Mapper $addressMapper,
- array $data = []
+ array $data = [],
+ Grid $addressesGrid = null
) {
- $this->customerRepository = $customerRepository;
$this->currentCustomer = $currentCustomer;
$this->addressRepository = $addressRepository;
$this->_addressConfig = $addressConfig;
$this->addressMapper = $addressMapper;
+ $this->addressesGrid = $addressesGrid ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(AddressesGrid::class);
parent::__construct($context, $data);
}
/**
+ * Prepare the Address Book section layout
+ *
* @return $this
*/
protected function _prepareLayout()
@@ -79,14 +90,20 @@ protected function _prepareLayout()
}
/**
+ * Generate and return "New Address" URL
+ *
* @return string
+ * @deprecated not used in this block
+ * @see \Magento\Customer\Block\Address\Grid::getAddAddressUrl
*/
public function getAddAddressUrl()
{
- return $this->getUrl('customer/address/new', ['_secure' => true]);
+ return $this->addressesGrid->getAddAddressUrl();
}
/**
+ * Generate and return "Back" URL
+ *
* @return string
*/
public function getBackUrl()
@@ -98,24 +115,37 @@ public function getBackUrl()
}
/**
+ * Generate and return "Delete" URL
+ *
* @return string
+ * @deprecated not used in this block
+ * @see \Magento\Customer\Block\Address\Grid::getDeleteUrl
*/
public function getDeleteUrl()
{
- return $this->getUrl('customer/address/delete');
+ return $this->addressesGrid->getDeleteUrl();
}
/**
+ * Generate and return "Edit Address" URL.
+ *
+ * Address ID passed in parameters
+ *
* @param int $addressId
* @return string
+ * @deprecated not used in this block
+ * @see \Magento\Customer\Block\Address\Grid::getAddressEditUrl
*/
public function getAddressEditUrl($addressId)
{
- return $this->getUrl('customer/address/edit', ['_secure' => true, 'id' => $addressId]);
+ return $this->addressesGrid->getAddressEditUrl($addressId);
}
/**
+ * Determines is the address primary (billing or shipping)
+ *
* @return bool
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function hasPrimaryAddress()
{
@@ -123,22 +153,22 @@ public function hasPrimaryAddress()
}
/**
+ * Get current additional customer addresses
+ *
+ * Will return array of address interfaces if customer have additional addresses and false in other case.
+ *
* @return \Magento\Customer\Api\Data\AddressInterface[]|bool
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @deprecated not used in this block
+ * @see \Magento\Customer\Block\Address\Grid::getAdditionalAddresses
*/
public function getAdditionalAddresses()
{
try {
- $addresses = $this->customerRepository->getById($this->currentCustomer->getCustomerId())->getAddresses();
+ $addresses = $this->addressesGrid->getAdditionalAddresses();
} catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
- return false;
- }
- $primaryAddressIds = [$this->getDefaultBilling(), $this->getDefaultShipping()];
- foreach ($addresses as $address) {
- if (!in_array($address->getId(), $primaryAddressIds)) {
- $additional[] = $address;
- }
}
- return empty($additional) ? false : $additional;
+ return empty($addresses) ? false : $addresses;
}
/**
@@ -158,23 +188,23 @@ public function getAddressHtml(\Magento\Customer\Api\Data\AddressInterface $addr
}
/**
+ * Get current customer
+ *
* @return \Magento\Customer\Api\Data\CustomerInterface|null
*/
public function getCustomer()
{
- $customer = $this->getData('customer');
- if ($customer === null) {
- try {
- $customer = $this->customerRepository->getById($this->currentCustomer->getCustomerId());
- } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
- return null;
- }
- $this->setData('customer', $customer);
+ $customer = null;
+ try {
+ $customer = $this->currentCustomer->getCustomer();
+ } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
}
return $customer;
}
/**
+ * Get customer's default billing address
+ *
* @return int|null
*/
public function getDefaultBilling()
@@ -188,8 +218,11 @@ public function getDefaultBilling()
}
/**
+ * Get customer address by ID
+ *
* @param int $addressId
* @return \Magento\Customer\Api\Data\AddressInterface|null
+ * @throws \Magento\Framework\Exception\LocalizedException
*/
public function getAddressById($addressId)
{
@@ -201,6 +234,8 @@ public function getAddressById($addressId)
}
/**
+ * Get customer's default shipping address
+ *
* @return int|null
*/
public function getDefaultShipping()
diff --git a/app/code/Magento/Customer/Block/Address/Grid.php b/app/code/Magento/Customer/Block/Address/Grid.php
new file mode 100644
index 0000000000000..de6767a0ef92a
--- /dev/null
+++ b/app/code/Magento/Customer/Block/Address/Grid.php
@@ -0,0 +1,245 @@
+currentCustomer = $currentCustomer;
+ $this->addressCollectionFactory = $addressCollectionFactory;
+ $this->countryFactory = $countryFactory;
+
+ parent::__construct($context, $data);
+ }
+
+ /**
+ * Prepare the Address Book section layout
+ *
+ * @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ protected function _prepareLayout(): void
+ {
+ parent::_prepareLayout();
+ $this->preparePager();
+ }
+
+ /**
+ * Generate and return "New Address" URL
+ *
+ * @return string
+ */
+ public function getAddAddressUrl(): string
+ {
+ return $this->getUrl('customer/address/new', ['_secure' => true]);
+ }
+
+ /**
+ * Generate and return "Delete" URL
+ *
+ * @return string
+ */
+ public function getDeleteUrl(): string
+ {
+ return $this->getUrl('customer/address/delete');
+ }
+
+ /**
+ * Generate and return "Edit Address" URL.
+ *
+ * Address ID passed in parameters
+ *
+ * @param int $addressId
+ * @return string
+ */
+ public function getAddressEditUrl($addressId): string
+ {
+ return $this->getUrl('customer/address/edit', ['_secure' => true, 'id' => $addressId]);
+ }
+
+ /**
+ * Get current additional customer addresses
+ *
+ * Return array of address interfaces if customer has additional addresses and false in other cases
+ *
+ * @return \Magento\Customer\Api\Data\AddressInterface[]
+ * @throws \Magento\Framework\Exception\LocalizedException
+ * @throws NoSuchEntityException
+ */
+ public function getAdditionalAddresses(): array
+ {
+ $additional = [];
+ $addresses = $this->getAddressCollection();
+ $primaryAddressIds = [$this->getDefaultBilling(), $this->getDefaultShipping()];
+ foreach ($addresses as $address) {
+ if (!in_array((int)$address->getId(), $primaryAddressIds, true)) {
+ $additional[] = $address->getDataModel();
+ }
+ }
+ return $additional;
+ }
+
+ /**
+ * Get current customer
+ *
+ * Return stored customer or get it from session
+ *
+ * @return \Magento\Customer\Api\Data\CustomerInterface
+ */
+ public function getCustomer(): \Magento\Customer\Api\Data\CustomerInterface
+ {
+ $customer = $this->getData('customer');
+ if ($customer === null) {
+ $customer = $this->currentCustomer->getCustomer();
+ $this->setData('customer', $customer);
+ }
+ return $customer;
+ }
+
+ /**
+ * Get one string street address from the Address DTO passed in parameters
+ *
+ * @param \Magento\Customer\Api\Data\AddressInterface $address
+ * @return string
+ */
+ public function getStreetAddress(\Magento\Customer\Api\Data\AddressInterface $address): string
+ {
+ $street = $address->getStreet();
+ if (is_array($street)) {
+ $street = implode(', ', $street);
+ }
+ return $street;
+ }
+
+ /**
+ * Get country name by $countryCode
+ *
+ * Using \Magento\Directory\Model\Country to get country name by $countryCode
+ *
+ * @param string $countryCode
+ * @return string
+ */
+ public function getCountryByCode(string $countryCode): string
+ {
+ /** @var \Magento\Directory\Model\Country $country */
+ $country = $this->countryFactory->create();
+ return $country->loadByCode($countryCode)->getName();
+ }
+
+ /**
+ * Get default billing address
+ *
+ * Return address string if address found and null if not
+ *
+ * @return int
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function getDefaultBilling(): int
+ {
+ $customer = $this->getCustomer();
+
+ return (int)$customer->getDefaultBilling();
+ }
+
+ /**
+ * Get default shipping address
+ *
+ * Return address string if address found and null if not
+ *
+ * @return int
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function getDefaultShipping(): int
+ {
+ $customer = $this->getCustomer();
+
+ return (int)$customer->getDefaultShipping();
+ }
+
+ /**
+ * Get pager layout
+ *
+ * @return void
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function preparePager(): void
+ {
+ $addressCollection = $this->getAddressCollection();
+ if (null !== $addressCollection) {
+ $pager = $this->getLayout()->createBlock(
+ \Magento\Theme\Block\Html\Pager::class,
+ 'customer.addresses.pager'
+ )->setCollection($addressCollection);
+ $this->setChild('pager', $pager);
+ }
+ }
+
+ /**
+ * Get customer addresses collection.
+ *
+ * Filters collection by customer id
+ *
+ * @return \Magento\Customer\Model\ResourceModel\Address\Collection
+ * @throws NoSuchEntityException
+ */
+ private function getAddressCollection(): \Magento\Customer\Model\ResourceModel\Address\Collection
+ {
+ if (null === $this->addressCollection) {
+ if (null === $this->getCustomer()) {
+ throw new NoSuchEntityException(__('Customer not logged in'));
+ }
+ /** @var \Magento\Customer\Model\ResourceModel\Address\Collection $collection */
+ $collection = $this->addressCollectionFactory->create();
+ $collection->setOrder('entity_id', 'desc')
+ ->setCustomerFilter([$this->getCustomer()->getId()]);
+ $this->addressCollection = $collection;
+ }
+ return $this->addressCollection;
+ }
+}
diff --git a/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php
new file mode 100644
index 0000000000000..280948439e1f8
--- /dev/null
+++ b/app/code/Magento/Customer/Block/DataProviders/PostCodesPatternsAttributeData.php
@@ -0,0 +1,50 @@
+postCodeConfig = $postCodeConfig;
+ $this->serializer = $serializer;
+ }
+
+ /**
+ * Get serialized post codes
+ *
+ * @return string
+ */
+ public function getSerializedPostCodes(): string
+ {
+ return $this->serializer->serialize($this->postCodeConfig->getPostCodes());
+ }
+}
diff --git a/app/code/Magento/Customer/Block/Widget/Dob.php b/app/code/Magento/Customer/Block/Widget/Dob.php
index 936563d519823..55101fb82afd0 100644
--- a/app/code/Magento/Customer/Block/Widget/Dob.php
+++ b/app/code/Magento/Customer/Block/Widget/Dob.php
@@ -61,7 +61,7 @@ public function __construct(
}
/**
- * @return void
+ * @inheritdoc
*/
public function _construct()
{
@@ -70,6 +70,8 @@ public function _construct()
}
/**
+ * Check if dob attribute enabled in system
+ *
* @return bool
*/
public function isEnabled()
@@ -79,6 +81,8 @@ public function isEnabled()
}
/**
+ * Check if dob attribute marked as required
+ *
* @return bool
*/
public function isRequired()
@@ -88,6 +92,8 @@ public function isRequired()
}
/**
+ * Set date
+ *
* @param string $date
* @return $this
*/
@@ -135,6 +141,8 @@ protected function applyOutputFilter($value)
}
/**
+ * Get day
+ *
* @return string|bool
*/
public function getDay()
@@ -143,6 +151,8 @@ public function getDay()
}
/**
+ * Get month
+ *
* @return string|bool
*/
public function getMonth()
@@ -151,6 +161,8 @@ public function getMonth()
}
/**
+ * Get year
+ *
* @return string|bool
*/
public function getYear()
@@ -168,6 +180,19 @@ public function getLabel()
return __('Date of Birth');
}
+ /**
+ * Retrieve store attribute label
+ *
+ * @param string $attributeCode
+ *
+ * @return string
+ */
+ public function getStoreLabel($attributeCode)
+ {
+ $attribute = $this->_getAttribute($attributeCode);
+ return $attribute ? __($attribute->getStoreLabel()) : '';
+ }
+
/**
* Create correct date field
*
diff --git a/app/code/Magento/Customer/Block/Widget/Gender.php b/app/code/Magento/Customer/Block/Widget/Gender.php
index d03c64a54fb94..9df3f1072ce0c 100644
--- a/app/code/Magento/Customer/Block/Widget/Gender.php
+++ b/app/code/Magento/Customer/Block/Widget/Gender.php
@@ -64,6 +64,7 @@ public function _construct()
/**
* Check if gender attribute enabled in system
+ *
* @return bool
*/
public function isEnabled()
@@ -73,6 +74,7 @@ public function isEnabled()
/**
* Check if gender attribute marked as required
+ *
* @return bool
*/
public function isRequired()
@@ -80,6 +82,19 @@ public function isRequired()
return $this->_getAttribute('gender') ? (bool)$this->_getAttribute('gender')->isRequired() : false;
}
+ /**
+ * Retrieve store attribute label
+ *
+ * @param string $attributeCode
+ *
+ * @return string
+ */
+ public function getStoreLabel($attributeCode)
+ {
+ $attribute = $this->_getAttribute($attributeCode);
+ return $attribute ? __($attribute->getStoreLabel()) : '';
+ }
+
/**
* Get current customer from session
*
@@ -92,6 +107,7 @@ public function getCustomer()
/**
* Returns options from gender attribute
+ *
* @return OptionInterface[]
*/
public function getGenderOptions()
diff --git a/app/code/Magento/Customer/Block/Widget/Taxvat.php b/app/code/Magento/Customer/Block/Widget/Taxvat.php
index e5c9c01dc3ac5..e35f04f592a43 100644
--- a/app/code/Magento/Customer/Block/Widget/Taxvat.php
+++ b/app/code/Magento/Customer/Block/Widget/Taxvat.php
@@ -63,4 +63,17 @@ public function isRequired()
{
return $this->_getAttribute('taxvat') ? (bool)$this->_getAttribute('taxvat')->isRequired() : false;
}
+
+ /**
+ * Retrieve store attribute label
+ *
+ * @param string $attributeCode
+ *
+ * @return string
+ */
+ public function getStoreLabel($attributeCode)
+ {
+ $attribute = $this->_getAttribute($attributeCode);
+ return $attribute ? __($attribute->getStoreLabel()) : '';
+ }
}
diff --git a/app/code/Magento/Customer/Controller/Account/EditPost.php b/app/code/Magento/Customer/Controller/Account/EditPost.php
index 38bc52eac4266..4eb41cedea29a 100644
--- a/app/code/Magento/Customer/Controller/Account/EditPost.php
+++ b/app/code/Magento/Customer/Controller/Account/EditPost.php
@@ -7,6 +7,8 @@
namespace Magento\Customer\Controller\Account;
+use Magento\Customer\Api\Data\CustomerInterface;
+use Magento\Customer\Model\AddressRegistry;
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Customer\Model\AuthenticationInterface;
use Magento\Customer\Model\Customer\Mapper;
@@ -25,6 +27,7 @@
use Magento\Framework\Escaper;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\InvalidEmailOrPasswordException;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\State\UserLockedException;
use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\Phrase;
@@ -85,6 +88,11 @@ class EditPost extends AbstractAccount implements CsrfAwareActionInterface, Http
*/
private $escaper;
+ /**
+ * @var AddressRegistry
+ */
+ private $addressRegistry;
+
/**
* @param Context $context
* @param Session $customerSession
@@ -93,6 +101,7 @@ class EditPost extends AbstractAccount implements CsrfAwareActionInterface, Http
* @param Validator $formKeyValidator
* @param CustomerExtractor $customerExtractor
* @param Escaper|null $escaper
+ * @param AddressRegistry|null $addressRegistry
*/
public function __construct(
Context $context,
@@ -101,7 +110,8 @@ public function __construct(
CustomerRepositoryInterface $customerRepository,
Validator $formKeyValidator,
CustomerExtractor $customerExtractor,
- ?Escaper $escaper = null
+ ?Escaper $escaper = null,
+ AddressRegistry $addressRegistry = null
) {
parent::__construct($context);
$this->session = $customerSession;
@@ -110,6 +120,7 @@ public function __construct(
$this->formKeyValidator = $formKeyValidator;
$this->customerExtractor = $customerExtractor;
$this->escaper = $escaper ?: ObjectManager::getInstance()->get(Escaper::class);
+ $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class);
}
/**
@@ -195,6 +206,9 @@ public function execute()
// whether a customer enabled change password option
$isPasswordChanged = $this->changeCustomerPassword($currentCustomerDataObject->getEmail());
+ // No need to validate customer address while editing customer profile
+ $this->disableAddressValidation($customerCandidateDataObject);
+
$this->customerRepository->save($customerCandidateDataObject);
$this->getEmailNotification()->credentialsChanged(
$customerCandidateDataObject,
@@ -352,4 +366,18 @@ private function getCustomerMapper()
}
return $this->customerMapper;
}
+
+ /**
+ * Disable Customer Address Validation
+ *
+ * @param CustomerInterface $customer
+ * @throws NoSuchEntityException
+ */
+ private function disableAddressValidation($customer)
+ {
+ foreach ($customer->getAddresses() as $address) {
+ $addressModel = $this->addressRegistry->retrieve($address->getId());
+ $addressModel->setShouldIgnoreValidation(true);
+ }
+ }
}
diff --git a/app/code/Magento/Customer/Controller/Address/FormPost.php b/app/code/Magento/Customer/Controller/Address/FormPost.php
index 217af0abd7592..25618e3129160 100644
--- a/app/code/Magento/Customer/Controller/Address/FormPost.php
+++ b/app/code/Magento/Customer/Controller/Address/FormPost.php
@@ -26,6 +26,8 @@
use Magento\Framework\View\Result\PageFactory;
/**
+ * Customer Address Form Post Controller
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class FormPost extends \Magento\Customer\Controller\Address implements HttpPostActionInterface
@@ -120,8 +122,18 @@ protected function _extractAddress()
\Magento\Customer\Api\Data\AddressInterface::class
);
$addressDataObject->setCustomerId($this->_getSession()->getCustomerId())
- ->setIsDefaultBilling($this->getRequest()->getParam('default_billing', false))
- ->setIsDefaultShipping($this->getRequest()->getParam('default_shipping', false));
+ ->setIsDefaultBilling(
+ $this->getRequest()->getParam(
+ 'default_billing',
+ isset($existingAddressData['default_billing']) ? $existingAddressData['default_billing'] : false
+ )
+ )
+ ->setIsDefaultShipping(
+ $this->getRequest()->getParam(
+ 'default_shipping',
+ isset($existingAddressData['default_shipping']) ? $existingAddressData['default_shipping'] : false
+ )
+ );
return $addressDataObject;
}
diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php
index 3c3808d0a1ee6..7220de0356817 100644
--- a/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php
+++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/InlineEdit.php
@@ -8,10 +8,13 @@
use Magento\Backend\App\Action;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Api\Data\CustomerInterface;
+use Magento\Customer\Model\AddressRegistry;
use Magento\Customer\Model\EmailNotificationInterface;
use Magento\Customer\Ui\Component\Listing\AttributeRepository;
use Magento\Framework\App\Action\HttpPostActionInterface;
+use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Message\MessageInterface;
+use Magento\Framework\App\ObjectManager;
/**
* Customer inline edit action
@@ -62,6 +65,11 @@ class InlineEdit extends \Magento\Backend\App\Action implements HttpPostActionIn
*/
private $emailNotification;
+ /**
+ * @var AddressRegistry
+ */
+ private $addressRegistry;
+
/**
* @param Action\Context $context
* @param CustomerRepositoryInterface $customerRepository
@@ -69,6 +77,7 @@ class InlineEdit extends \Magento\Backend\App\Action implements HttpPostActionIn
* @param \Magento\Customer\Model\Customer\Mapper $customerMapper
* @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
* @param \Psr\Log\LoggerInterface $logger
+ * @param AddressRegistry|null $addressRegistry
*/
public function __construct(
Action\Context $context,
@@ -76,13 +85,15 @@ public function __construct(
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
\Magento\Customer\Model\Customer\Mapper $customerMapper,
\Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
- \Psr\Log\LoggerInterface $logger
+ \Psr\Log\LoggerInterface $logger,
+ AddressRegistry $addressRegistry = null
) {
$this->customerRepository = $customerRepository;
$this->resultJsonFactory = $resultJsonFactory;
$this->customerMapper = $customerMapper;
$this->dataObjectHelper = $dataObjectHelper;
$this->logger = $logger;
+ $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class);
parent::__construct($context);
}
@@ -219,6 +230,8 @@ protected function updateDefaultBilling(array $data)
protected function saveCustomer(CustomerInterface $customer)
{
try {
+ // No need to validate customer address during inline edit action
+ $this->disableAddressValidation($customer);
$this->customerRepository->save($customer);
} catch (\Magento\Framework\Exception\InputException $e) {
$this->getMessageManager()->addError($this->getErrorWithCustomerId($e->getMessage()));
@@ -304,4 +317,18 @@ protected function getErrorWithCustomerId($errorText)
{
return '[Customer ID: ' . $this->getCustomer()->getId() . '] ' . __($errorText);
}
+
+ /**
+ * Disable Customer Address Validation
+ *
+ * @param CustomerInterface $customer
+ * @throws NoSuchEntityException
+ */
+ private function disableAddressValidation($customer)
+ {
+ foreach ($customer->getAddresses() as $address) {
+ $addressModel = $this->addressRegistry->retrieve($address->getId());
+ $addressModel->setShouldIgnoreValidation(true);
+ }
+ }
}
diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php
index a540ad9d7a70e..5a9c52bf9b1c0 100644
--- a/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php
+++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/MassAssignGroup.php
@@ -5,6 +5,7 @@
*/
namespace Magento\Customer\Controller\Adminhtml\Index;
+use Magento\Customer\Model\Customer;
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Backend\App\Action\Context;
use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory;
@@ -52,6 +53,8 @@ protected function massAction(AbstractCollection $collection)
// Verify customer exists
$customer = $this->customerRepository->getById($customerId);
$customer->setGroupId($this->getRequest()->getParam('group'));
+ // No need to validate customer and customer address during assigning customer to the group
+ $this->setIgnoreValidationFlag($customer);
$this->customerRepository->save($customer);
$customersUpdated++;
}
@@ -65,4 +68,15 @@ protected function massAction(AbstractCollection $collection)
return $resultRedirect;
}
+
+ /**
+ * Set ignore_validation_flag to skip unnecessary address and customer validation
+ *
+ * @param Customer $customer
+ * @return void
+ */
+ private function setIgnoreValidationFlag($customer)
+ {
+ $customer->setData('ignore_validation_flag', true);
+ }
}
diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php
index adb420f983006..cb0343f4ec43b 100644
--- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php
+++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php
@@ -5,6 +5,15 @@
*/
namespace Magento\Customer\Controller\Adminhtml\Index;
+use Magento\Customer\Api\AccountManagementInterface;
+use Magento\Customer\Api\AddressRepositoryInterface;
+use Magento\Customer\Api\CustomerRepositoryInterface;
+use Magento\Customer\Model\Address\Mapper;
+use Magento\Customer\Model\AddressRegistry;
+use Magento\Framework\Api\DataObjectHelper;
+use Magento\Customer\Api\Data\AddressInterfaceFactory;
+use Magento\Customer\Api\Data\CustomerInterfaceFactory;
+use Magento\Framework\DataObjectFactory as ObjectFactory;
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
use Magento\Customer\Api\AddressMetadataInterface;
use Magento\Customer\Api\CustomerMetadataInterface;
@@ -13,6 +22,8 @@
use Magento\Customer\Model\EmailNotificationInterface;
use Magento\Customer\Model\Metadata\Form;
use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\App\ObjectManager;
/**
* Save customer action.
@@ -26,6 +37,100 @@ class Save extends \Magento\Customer\Controller\Adminhtml\Index implements HttpP
*/
private $emailNotification;
+ /**
+ * @var AddressRegistry
+ */
+ private $addressRegistry;
+
+ /**
+ * Constructor
+ *
+ * @param \Magento\Backend\App\Action\Context $context
+ * @param \Magento\Framework\Registry $coreRegistry
+ * @param \Magento\Framework\App\Response\Http\FileFactory $fileFactory
+ * @param \Magento\Customer\Model\CustomerFactory $customerFactory
+ * @param \Magento\Customer\Model\AddressFactory $addressFactory
+ * @param \Magento\Customer\Model\Metadata\FormFactory $formFactory
+ * @param \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory
+ * @param \Magento\Customer\Helper\View $viewHelper
+ * @param \Magento\Framework\Math\Random $random
+ * @param CustomerRepositoryInterface $customerRepository
+ * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter
+ * @param Mapper $addressMapper
+ * @param AccountManagementInterface $customerAccountManagement
+ * @param AddressRepositoryInterface $addressRepository
+ * @param CustomerInterfaceFactory $customerDataFactory
+ * @param AddressInterfaceFactory $addressDataFactory
+ * @param \Magento\Customer\Model\Customer\Mapper $customerMapper
+ * @param \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor
+ * @param DataObjectHelper $dataObjectHelper
+ * @param ObjectFactory $objectFactory
+ * @param \Magento\Framework\View\LayoutFactory $layoutFactory
+ * @param \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory
+ * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
+ * @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
+ * @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
+ * @param AddressRegistry|null $addressRegistry
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
+ */
+ public function __construct(
+ \Magento\Backend\App\Action\Context $context,
+ \Magento\Framework\Registry $coreRegistry,
+ \Magento\Framework\App\Response\Http\FileFactory $fileFactory,
+ \Magento\Customer\Model\CustomerFactory $customerFactory,
+ \Magento\Customer\Model\AddressFactory $addressFactory,
+ \Magento\Customer\Model\Metadata\FormFactory $formFactory,
+ \Magento\Newsletter\Model\SubscriberFactory $subscriberFactory,
+ \Magento\Customer\Helper\View $viewHelper,
+ \Magento\Framework\Math\Random $random,
+ CustomerRepositoryInterface $customerRepository,
+ \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter,
+ Mapper $addressMapper,
+ AccountManagementInterface $customerAccountManagement,
+ AddressRepositoryInterface $addressRepository,
+ CustomerInterfaceFactory $customerDataFactory,
+ AddressInterfaceFactory $addressDataFactory,
+ \Magento\Customer\Model\Customer\Mapper $customerMapper,
+ \Magento\Framework\Reflection\DataObjectProcessor $dataObjectProcessor,
+ DataObjectHelper $dataObjectHelper,
+ ObjectFactory $objectFactory,
+ \Magento\Framework\View\LayoutFactory $layoutFactory,
+ \Magento\Framework\View\Result\LayoutFactory $resultLayoutFactory,
+ \Magento\Framework\View\Result\PageFactory $resultPageFactory,
+ \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory,
+ \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
+ AddressRegistry $addressRegistry = null
+ ) {
+ parent::__construct(
+ $context,
+ $coreRegistry,
+ $fileFactory,
+ $customerFactory,
+ $addressFactory,
+ $formFactory,
+ $subscriberFactory,
+ $viewHelper,
+ $random,
+ $customerRepository,
+ $extensibleDataObjectConverter,
+ $addressMapper,
+ $customerAccountManagement,
+ $addressRepository,
+ $customerDataFactory,
+ $addressDataFactory,
+ $customerMapper,
+ $dataObjectProcessor,
+ $dataObjectHelper,
+ $objectFactory,
+ $layoutFactory,
+ $resultLayoutFactory,
+ $resultPageFactory,
+ $resultForwardFactory,
+ $resultJsonFactory
+ );
+ $this->addressRegistry = $addressRegistry ?: ObjectManager::getInstance()->get(AddressRegistry::class);
+ }
+
/**
* Reformat customer account data to be compatible with customer service interface
*
@@ -195,6 +300,8 @@ public function execute()
if ($customerId) {
$currentCustomer = $this->_customerRepository->getById($customerId);
+ // No need to validate customer address while editing customer profile
+ $this->disableAddressValidation($currentCustomer);
$customerData = array_merge(
$this->customerMapper->toFlatArray($currentCustomer),
$customerData
@@ -368,4 +475,18 @@ private function getCurrentCustomerId()
return $customerId;
}
+
+ /**
+ * Disable Customer Address Validation
+ *
+ * @param CustomerInterface $customer
+ * @throws NoSuchEntityException
+ */
+ private function disableAddressValidation($customer)
+ {
+ foreach ($customer->getAddresses() as $address) {
+ $addressModel = $this->addressRegistry->retrieve($address->getId());
+ $addressModel->setShouldIgnoreValidation(true);
+ }
+ }
}
diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php
index d7c5d7f47a4cf..192b8081254f4 100644
--- a/app/code/Magento/Customer/Model/AccountManagement.php
+++ b/app/code/Magento/Customer/Model/AccountManagement.php
@@ -60,6 +60,7 @@
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
*/
class AccountManagement implements AccountManagementInterface
{
@@ -524,6 +525,8 @@ private function activateCustomer($customer, $confirmationKey)
}
$customer->setConfirmation(null);
+ // No need to validate customer and customer address while activating customer
+ $this->setIgnoreValidationFlag($customer);
$this->customerRepository->save($customer);
$this->getEmailNotification()->newAccount(
$customer,
@@ -683,8 +686,9 @@ public function resetPassword($email, $resetToken, $newPassword)
$customer = $this->customerRepository->get($email);
}
- // No need to validate customer address while saving customer reset password token
+ // No need to validate customer and customer address while saving customer reset password token
$this->disableAddressValidation($customer);
+ $this->setIgnoreValidationFlag($customer);
//Validate Token and new password strength
$this->validateResetPasswordToken($customer->getId(), $resetToken);
@@ -1029,6 +1033,7 @@ private function changePasswordForCustomer($customer, $currentPassword, $newPass
$this->checkPasswordStrength($newPassword);
$customerSecure->setPasswordHash($this->createPasswordHash($newPassword));
$this->destroyCustomerSessions($customer->getId());
+ $this->disableAddressValidation($customer);
$this->customerRepository->save($customer);
return true;
diff --git a/app/code/Magento/Customer/Model/Address.php b/app/code/Magento/Customer/Model/Address.php
index 4976ec546609f..e9aa2839095d5 100644
--- a/app/code/Magento/Customer/Model/Address.php
+++ b/app/code/Magento/Customer/Model/Address.php
@@ -172,12 +172,9 @@ public function updateData(AddressInterface $address)
public function getDataModel($defaultBillingAddressId = null, $defaultShippingAddressId = null)
{
if ($this->getCustomerId() || $this->getParentId()) {
- if ($this->getCustomer()->getDefaultBillingAddress()) {
- $defaultBillingAddressId = $this->getCustomer()->getDefaultBillingAddress()->getId();
- }
- if ($this->getCustomer()->getDefaultShippingAddress()) {
- $defaultShippingAddressId = $this->getCustomer()->getDefaultShippingAddress()->getId();
- }
+ $customer = $this->getCustomer();
+ $defaultBillingAddressId = $customer->getDefaultBilling() ?: $defaultBillingAddressId;
+ $defaultShippingAddressId = $customer->getDefaultShipping() ?: $defaultShippingAddressId;
}
return parent::getDataModel($defaultBillingAddressId, $defaultShippingAddressId);
}
diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php
index 146fec4c79f46..d8d0646b30bb8 100644
--- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php
+++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php
@@ -222,7 +222,7 @@ public function getStreet()
}
/**
- * Get steet line by number
+ * Get street line by number
*
* @param int $number
* @return string
diff --git a/app/code/Magento/Customer/Model/Address/AddressModelInterface.php b/app/code/Magento/Customer/Model/Address/AddressModelInterface.php
index 0af36e877555f..06de3a99a831c 100644
--- a/app/code/Magento/Customer/Model/Address/AddressModelInterface.php
+++ b/app/code/Magento/Customer/Model/Address/AddressModelInterface.php
@@ -15,7 +15,7 @@
interface AddressModelInterface
{
/**
- * Get steet line by number
+ * Get street line by number
*
* @param int $number
* @return string
diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php
index 972cb63ed452e..b00f393f53734 100644
--- a/app/code/Magento/Customer/Model/Customer.php
+++ b/app/code/Magento/Customer/Model/Customer.php
@@ -219,6 +219,13 @@ class Customer extends \Magento\Framework\Model\AbstractModel
*/
private $accountConfirmation;
+ /**
+ * Caching property to store customer address data models by the address ID.
+ *
+ * @var array
+ */
+ private $storedAddress;
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
@@ -314,7 +321,10 @@ public function getDataModel()
$addressesData = [];
/** @var \Magento\Customer\Model\Address $address */
foreach ($this->getAddresses() as $address) {
- $addressesData[] = $address->getDataModel();
+ if (!isset($this->storedAddress[$address->getId()])) {
+ $this->storedAddress[$address->getId()] = $address->getDataModel();
+ }
+ $addressesData[] = $this->storedAddress[$address->getId()];
}
$customerDataObject = $this->customerDataFactory->create();
$this->dataObjectHelper->populateWithArray(
diff --git a/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php b/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php
index d4e0c5cce3401..336e7ab770b02 100644
--- a/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php
+++ b/app/code/Magento/Customer/Model/Indexer/CustomerGroupDimensionProvider.php
@@ -11,6 +11,9 @@
use Magento\Framework\Indexer\DimensionFactory;
use Magento\Framework\Indexer\DimensionProviderInterface;
+/**
+ * Class CustomerGroupDimensionProvider
+ */
class CustomerGroupDimensionProvider implements DimensionProviderInterface
{
/**
@@ -34,12 +37,19 @@ class CustomerGroupDimensionProvider implements DimensionProviderInterface
*/
private $dimensionFactory;
+ /**
+ * @param CustomerGroupCollectionFactory $collectionFactory
+ * @param DimensionFactory $dimensionFactory
+ */
public function __construct(CustomerGroupCollectionFactory $collectionFactory, DimensionFactory $dimensionFactory)
{
$this->dimensionFactory = $dimensionFactory;
$this->collectionFactory = $collectionFactory;
}
+ /**
+ * @inheritdoc
+ */
public function getIterator(): \Traversable
{
foreach ($this->getCustomerGroups() as $customerGroup) {
@@ -48,6 +58,8 @@ public function getIterator(): \Traversable
}
/**
+ * Get Customer Groups
+ *
* @return array
*/
private function getCustomerGroups(): array
diff --git a/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php b/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php
index 5a46fdb9defc4..8e64fba4a9b08 100644
--- a/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php
+++ b/app/code/Magento/Customer/Model/Metadata/AttributeMetadataCache.php
@@ -12,6 +12,7 @@
use Magento\Framework\App\Cache\StateInterface;
use Magento\Framework\App\CacheInterface;
use Magento\Framework\Serialize\SerializerInterface;
+use Magento\Store\Model\StoreManagerInterface;
/**
* Cache for attribute metadata
@@ -53,6 +54,11 @@ class AttributeMetadataCache
*/
private $serializer;
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
/**
* Constructor
*
@@ -60,17 +66,21 @@ class AttributeMetadataCache
* @param StateInterface $state
* @param SerializerInterface $serializer
* @param AttributeMetadataHydrator $attributeMetadataHydrator
+ * @param StoreManagerInterface $storeManager
*/
public function __construct(
CacheInterface $cache,
StateInterface $state,
SerializerInterface $serializer,
- AttributeMetadataHydrator $attributeMetadataHydrator
+ AttributeMetadataHydrator $attributeMetadataHydrator,
+ StoreManagerInterface $storeManager = null
) {
$this->cache = $cache;
$this->state = $state;
$this->serializer = $serializer;
$this->attributeMetadataHydrator = $attributeMetadataHydrator;
+ $this->storeManager = $storeManager ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(StoreManagerInterface::class);
}
/**
@@ -82,11 +92,12 @@ public function __construct(
*/
public function load($entityType, $suffix = '')
{
- if (isset($this->attributes[$entityType . $suffix])) {
- return $this->attributes[$entityType . $suffix];
+ $storeId = $this->storeManager->getStore()->getId();
+ if (isset($this->attributes[$entityType . $suffix . $storeId])) {
+ return $this->attributes[$entityType . $suffix . $storeId];
}
if ($this->isEnabled()) {
- $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix;
+ $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId;
$serializedData = $this->cache->load($cacheKey);
if ($serializedData) {
$attributesData = $this->serializer->unserialize($serializedData);
@@ -94,7 +105,7 @@ public function load($entityType, $suffix = '')
foreach ($attributesData as $key => $attributeData) {
$attributes[$key] = $this->attributeMetadataHydrator->hydrate($attributeData);
}
- $this->attributes[$entityType . $suffix] = $attributes;
+ $this->attributes[$entityType . $suffix . $storeId] = $attributes;
return $attributes;
}
}
@@ -111,9 +122,10 @@ public function load($entityType, $suffix = '')
*/
public function save($entityType, array $attributes, $suffix = '')
{
- $this->attributes[$entityType . $suffix] = $attributes;
+ $storeId = $this->storeManager->getStore()->getId();
+ $this->attributes[$entityType . $suffix . $storeId] = $attributes;
if ($this->isEnabled()) {
- $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix;
+ $cacheKey = self::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId;
$attributesData = [];
foreach ($attributes as $key => $attribute) {
$attributesData[$key] = $this->attributeMetadataHydrator->extract($attribute);
diff --git a/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php b/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php
index 168f00be16e33..8e443e93354b0 100644
--- a/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php
+++ b/app/code/Magento/Customer/Model/Metadata/Form/AbstractData.php
@@ -12,6 +12,8 @@
use Magento\Framework\Validator\EmailAddress;
/**
+ * Form Element Abstract Data Model
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
abstract class AbstractData
@@ -137,6 +139,7 @@ public function setRequestScope($scope)
/**
* Set scope visibility
+ *
* Search value only in scope or search value in scope and global
*
* @param boolean $flag
@@ -281,9 +284,14 @@ protected function _validateInputRule($value)
);
if ($inputValidation !== null) {
+ $allowWhiteSpace = false;
+
switch ($inputValidation) {
+ case 'alphanum-with-spaces':
+ $allowWhiteSpace = true;
+ // Continue to alphanumeric validation
case 'alphanumeric':
- $validator = new \Zend_Validate_Alnum(true);
+ $validator = new \Zend_Validate_Alnum($allowWhiteSpace);
$validator->setMessage(__('"%1" invalid type entered.', $label), \Zend_Validate_Alnum::INVALID);
$validator->setMessage(
__('"%1" contains non-alphabetic or non-numeric characters.', $label),
diff --git a/app/code/Magento/Customer/Model/ResourceModel/Address.php b/app/code/Magento/Customer/Model/ResourceModel/Address.php
index a52c372310843..200eaabe6517d 100644
--- a/app/code/Magento/Customer/Model/ResourceModel/Address.php
+++ b/app/code/Magento/Customer/Model/ResourceModel/Address.php
@@ -14,6 +14,7 @@
/**
* Class Address
+ *
* @package Magento\Customer\Model\ResourceModel
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
@@ -31,8 +32,8 @@ class Address extends \Magento\Eav\Model\Entity\VersionControl\AbstractEntity
/**
* @param \Magento\Eav\Model\Entity\Context $context
- * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot,
- * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite,
+ * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\Snapshot $entitySnapshot
+ * @param \Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite $entityRelationComposite
* @param \Magento\Framework\Validator\Factory $validatorFactory
* @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
* @param array $data
@@ -98,6 +99,9 @@ protected function _beforeSave(\Magento\Framework\DataObject $address)
*/
protected function _validate($address)
{
+ if ($address->getDataByKey('should_ignore_validation')) {
+ return;
+ };
$validator = $this->_validatorFactory->createValidator('customer_address', 'save');
if (!$validator->isValid($address)) {
@@ -110,7 +114,7 @@ protected function _validate($address)
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
public function delete($object)
{
@@ -120,6 +124,8 @@ public function delete($object)
}
/**
+ * Get instance of DeleteRelation class
+ *
* @deprecated 100.2.0
* @return DeleteRelation
*/
@@ -129,6 +135,8 @@ private function getDeleteRelation()
}
/**
+ * Get instance of CustomerRegistry class
+ *
* @deprecated 100.2.0
* @return CustomerRegistry
*/
@@ -138,6 +146,8 @@ private function getCustomerRegistry()
}
/**
+ * After delete entity process
+ *
* @param \Magento\Customer\Model\Address $address
* @return $this
*/
diff --git a/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php b/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php
index eb7e81009c92c..26c4c50009bb1 100644
--- a/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php
+++ b/app/code/Magento/Customer/Observer/UpgradeCustomerPasswordObserver.php
@@ -6,11 +6,15 @@
namespace Magento\Customer\Observer;
+use Magento\Customer\Model\Customer;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Event\ObserverInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Model\CustomerRegistry;
+/**
+ * Class observer UpgradeCustomerPasswordObserver to upgrade customer password hash when customer has logged in
+ */
class UpgradeCustomerPasswordObserver implements ObserverInterface
{
/**
@@ -61,7 +65,20 @@ public function execute(\Magento\Framework\Event\Observer $observer)
if (!$this->encryptor->validateHashVersion($customerSecure->getPasswordHash(), true)) {
$customerSecure->setPasswordHash($this->encryptor->getHash($password, true));
+ // No need to validate customer and customer address while upgrading customer password
+ $this->setIgnoreValidationFlag($customer);
$this->customerRepository->save($customer);
}
}
+
+ /**
+ * Set ignore_validation_flag to skip unnecessary address and customer validation
+ *
+ * @param Customer $customer
+ * @return void
+ */
+ private function setIgnoreValidationFlag($customer)
+ {
+ $customer->setData('ignore_validation_flag', true);
+ }
}
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml
index 4c458c66ca65b..37149e23dc87e 100644
--- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCreateCustomerWithWebsiteAndStoreViewActionGroup.xml
@@ -42,4 +42,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShopingCartActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShopingCartActionGroup.xml
new file mode 100644
index 0000000000000..f5d5682e374f2
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminCustomerShopingCartActionGroup.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminUpdateCustomerGroupActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminUpdateCustomerGroupActionGroup.xml
new file mode 100644
index 0000000000000..b1b82fb9fb74c
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminUpdateCustomerGroupActionGroup.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml
similarity index 93%
rename from app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml
rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml
index a68042127ec48..047f656f5eabe 100644
--- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/CreateCustomerActionGroup.xml
@@ -5,8 +5,9 @@
* See COPYING.txt for license details.
*/
-->
+
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
@@ -39,6 +40,5 @@
-
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml
similarity index 100%
rename from app/code/Magento/Braintree/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml
rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/DeleteCustomerActionGroup.xml
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/EditCustomerAddressesFromAdminActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EditCustomerAddressesFromAdminActionGroup.xml
new file mode 100644
index 0000000000000..617c895bc1201
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/EditCustomerAddressesFromAdminActionGroup.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerAddressActionGroup.xml
new file mode 100644
index 0000000000000..a45fcf31f7b3f
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAddCustomerAddressActionGroup.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml
new file mode 100644
index 0000000000000..fc5c1b881752e
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml
similarity index 84%
rename from app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml
rename to app/code/Magento/Customer/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml
index 7c774a634b369..4c59edbcb8057 100644
--- a/app/code/Magento/Braintree/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml
+++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SwitchAccountActionGroup.xml
@@ -5,9 +5,9 @@
* See COPYING.txt for license details.
*/
-->
-
+
@@ -24,5 +24,4 @@
-
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml
index c7335f9024218..da36cf722325e 100755
--- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml
@@ -44,12 +44,29 @@
Austin
Texas
US
+ United States
78729
512-345-6789
Yes
Yes
RegionTX
+
+ John
+ Doe
+ Magento
+
+ - 7700 West Parmer Lane
+
+ Austin
+ Texas
+ US
+ United States
+ 78729
+ 512-345-6789
+ Yes
+ RegionTX
+
John
Doe
@@ -68,6 +85,23 @@
RegionNY
United States
+
+ John
+ Doe
+ 368
+
+ - 368 Broadway St.
+ - 113
+
+ New York
+ New York
+ US
+ 10001
+ 512-345-6789
+ Yes
+ RegionNY
+ United States
+
John
Doe
@@ -95,6 +129,7 @@
Los Angeles
California
US
+ United States
90001
512-345-6789
Yes
@@ -148,4 +183,12 @@
California
+
+ false
+ true
+
+
+ true
+ false
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml
index 44ac0981d9577..b3c0d8d9e0047 100644
--- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml
@@ -45,6 +45,16 @@
0
US_Address_TX
+
+ 1
+ John.Doe@example.com
+ John
+ Doe
+ John Doe
+ pwdTest123!
+ 0
+ 0
+
0
true
@@ -73,6 +83,20 @@
US_Address_NY_Not_Default_Address
UK_Not_Default_Address
+
+ 0
+ true
+ true
+ John.Doe@example.com
+ John
+ Doe
+ John Doe
+ pwdTest123!
+ 0
+ 0
+ US_Address_TX_Default_Billing
+ US_Address_NY_Default_Shipping
+
0
true
@@ -142,4 +166,16 @@
0
UK_Not_Default_Address
+
+ 1
+ John.Doe@example.com
+ John
+ Doe
+ John Doe
+ pwdTest123!
+ 0
+ 0
+ US_Default_Billing_Address_TX
+ US_Default_Shipping_Address_CA
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/NewCustomerData.xml
similarity index 77%
rename from app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml
rename to app/code/Magento/Customer/Test/Mftf/Data/NewCustomerData.xml
index 30345ec31bacd..cdd117c2a0b12 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Data/NewCustomerData.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Data/NewCustomerData.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
Abgar
Abgaryan
@@ -20,5 +20,4 @@
9999
Armenia
-
diff --git a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml
index d662c5cef6032..9bd382da8eb92 100644
--- a/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Page/AdminEditCustomerPage.xml
@@ -13,5 +13,6 @@
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml
index e2ebf638934c6..0d273da353005 100644
--- a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerCreatePage.xml
@@ -9,6 +9,7 @@
-
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerLogoutSuccessPage.xml b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerLogoutSuccessPage.xml
new file mode 100644
index 0000000000000..9c1fc7aa8a88d
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Page/StorefrontCustomerLogoutSuccessPage.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCreateUserSection.xml
similarity index 81%
rename from app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/AdminCreateUserSection.xml
index 98d748b5a30ea..376b0b9f66db9 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/AdminCreateUserSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCreateUserSection.xml
@@ -5,7 +5,9 @@
* See COPYING.txt for license details.
*/
-->
-
+
+
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml
index d5a410164a6f1..6a3687bb77c8f 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerAccountInformationSection.xml
@@ -9,6 +9,7 @@
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml
index 3ff880c64e6d6..0a56763b66704 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerMainActionsSection.xml
@@ -11,5 +11,6 @@
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml
new file mode 100644
index 0000000000000..c4a4d650c1e59
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerShoppingCartSection.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminDeleteUserSection.xml
similarity index 68%
rename from app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/AdminDeleteUserSection.xml
index bf2e2b44eb602..0ba197999be6c 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/AdminDeleteUserSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminDeleteUserSection.xml
@@ -5,11 +5,13 @@
* See COPYING.txt for license details.
*/
-->
-
+
+
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerAddressesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerAddressesSection.xml
new file mode 100644
index 0000000000000..04d6c4dc2a09d
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerAddressesSection.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminUserGridSection.xml
similarity index 74%
rename from app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/AdminUserGridSection.xml
index 9564bc61f799c..7c4a76871d58c 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/AdminUserGridSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminUserGridSection.xml
@@ -5,7 +5,9 @@
* See COPYING.txt for license details.
*/
-->
-
+
+
-
\ No newline at end of file
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml
similarity index 84%
rename from app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml
index e4a75b1b6a842..60c635387199a 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/CustomersPageSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/CustomersPageSection.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/CustomersSubmenuSection.xml
similarity index 68%
rename from app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/CustomersSubmenuSection.xml
index 937afb83da96f..6eeef1ba9daf0 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/CustomersSubmenuSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/CustomersSubmenuSection.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/NewCustomerPageSection.xml
similarity index 92%
rename from app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/NewCustomerPageSection.xml
index d302f9c7d0cba..abb8aa6c1d826 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/NewCustomerPageSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/NewCustomerPageSection.xml
@@ -7,7 +7,7 @@
-->
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
new file mode 100644
index 0000000000000..59da4e9279a03
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAccountInformationSection.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressFormSection.xml
new file mode 100644
index 0000000000000..112ced1bc375f
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressFormSection.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml
index 05bbc559defac..29a2f549274a7 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerAddressesSection.xml
@@ -9,7 +9,13 @@
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml
index 2b5662cdd623e..ee14ee5c165c5 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerCreateFormSection.xml
@@ -11,9 +11,23 @@
+
diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml
index a0c83f5bc491b..1955c6a417ba9 100644
--- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontPanelHeaderSection.xml
@@ -12,5 +12,8 @@
+
+
+
diff --git a/app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml
similarity index 78%
rename from app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml
rename to app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml
index 3a07cbc6dd145..4442e317694ee 100644
--- a/app/code/Magento/Braintree/Test/Mftf/Section/SwitchAccountSection.xml
+++ b/app/code/Magento/Customer/Test/Mftf/Section/SwitchAccountSection.xml
@@ -7,8 +7,7 @@
-->
-
+ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd">
-
-
diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml
new file mode 100644
index 0000000000000..413bbfd06a539
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontAddCustomerAddressTest.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml
new file mode 100644
index 0000000000000..d9d1c9f2e05a0
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontUpdateCustomerAddressTest.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php
new file mode 100644
index 0000000000000..31bcc37612302
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Unit/Block/Address/GridTest.php
@@ -0,0 +1,196 @@
+objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+ $this->currentCustomer = $this->getMockBuilder(\Magento\Customer\Helper\Session\CurrentCustomer::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getCustomer'])
+ ->getMock();
+
+ $this->addressCollectionFactory = $this->getMockBuilder(CollectionFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->countryFactory = $this->getMockBuilder(\Magento\Directory\Model\CountryFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->urlBuilder = $this->getMockForAbstractClass(\Magento\Framework\UrlInterface::class);
+
+ $this->gridBlock = $this->objectManager->getObject(
+ \Magento\Customer\Block\Address\Grid::class,
+ [
+ 'addressCollectionFactory' => $this->addressCollectionFactory,
+ 'currentCustomer' => $this->currentCustomer,
+ 'countryFactory' => $this->countryFactory,
+ '_urlBuilder' => $this->urlBuilder
+ ]
+ );
+ }
+
+ /**
+ * Test for \Magento\Customer\Block\Address\Book::getChildHtml method with 'pager' argument
+ */
+ public function testGetChildHtml()
+ {
+ $customerId = 1;
+
+ /** @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit_Framework_MockObject_MockObject $block */
+ $block = $this->getMockBuilder(\Magento\Framework\View\Element\BlockInterface::class)
+ ->setMethods(['setCollection'])
+ ->getMockForAbstractClass();
+ /** @var $layout \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject */
+ $layout = $this->getMockForAbstractClass(\Magento\Framework\View\LayoutInterface::class);
+ /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */
+ $customer = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\CustomerInterface::class);
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ $addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setOrder', 'setCustomerFilter', 'load'])
+ ->getMock();
+
+ $layout->expects($this->atLeastOnce())->method('getChildName')->with('NameInLayout', 'pager')
+ ->willReturn('ChildName');
+ $layout->expects($this->atLeastOnce())->method('renderElement')->with('ChildName', true)
+ ->willReturn('OutputString');
+ $layout->expects($this->atLeastOnce())->method('createBlock')
+ ->with(\Magento\Theme\Block\Html\Pager::class, 'customer.addresses.pager')->willReturn($block);
+ $customer->expects($this->atLeastOnce())->method('getId')->willReturn($customerId);
+ $this->currentCustomer->expects($this->atLeastOnce())->method('getCustomer')->willReturn($customer);
+ $addressCollection->expects($this->atLeastOnce())->method('setOrder')->with('entity_id', 'desc')
+ ->willReturnSelf();
+ $addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->with([$customerId])
+ ->willReturnSelf();
+ $this->addressCollectionFactory->expects($this->atLeastOnce())->method('create')
+ ->willReturn($addressCollection);
+ $block->expects($this->atLeastOnce())->method('setCollection')->with($addressCollection)->willReturnSelf();
+ $this->gridBlock->setNameInLayout('NameInLayout');
+ $this->gridBlock->setLayout($layout);
+ $this->assertEquals('OutputString', $this->gridBlock->getChildHtml('pager'));
+ }
+
+ /**
+ * Test for \Magento\Customer\Block\Address\Grid::getAddressEditUrl method
+ */
+ public function testGetAddAddressUrl()
+ {
+ $addressId = 1;
+ $expectedUrl = 'expected_url';
+ $this->urlBuilder->expects($this->atLeastOnce())->method('getUrl')
+ ->with('customer/address/edit', ['_secure' => true, 'id' => $addressId])
+ ->willReturn($expectedUrl);
+ $this->assertEquals($expectedUrl, $this->gridBlock->getAddressEditUrl($addressId));
+ }
+
+ public function testGetAdditionalAddresses()
+ {
+ $customerId = 1;
+ /** @var \Magento\Customer\Api\Data\CustomerInterface|\PHPUnit_Framework_MockObject_MockObject $customer */
+ $customer = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\CustomerInterface::class);
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ $addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setOrder', 'setCustomerFilter', 'load', 'getIterator'])
+ ->getMock();
+ $addressDataModel = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\AddressInterface::class);
+ $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getId', 'getDataModel'])
+ ->getMock();
+ $collection = [$address, $address, $address];
+ $address->expects($this->exactly(3))->method('getId')
+ ->willReturnOnConsecutiveCalls(1, 2, 3);
+ $address->expects($this->atLeastOnce())->method('getDataModel')->willReturn($addressDataModel);
+ $customer->expects($this->atLeastOnce())->method('getId')->willReturn($customerId);
+ $customer->expects($this->atLeastOnce())->method('getDefaultBilling')->willReturn('1');
+ $customer->expects($this->atLeastOnce())->method('getDefaultShipping')->willReturn('2');
+
+ $this->currentCustomer->expects($this->atLeastOnce())->method('getCustomer')->willReturn($customer);
+ $addressCollection->expects($this->atLeastOnce())->method('setOrder')->with('entity_id', 'desc')
+ ->willReturnSelf();
+ $addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->with([$customerId])
+ ->willReturnSelf();
+ $addressCollection->expects($this->atLeastOnce())->method('getIterator')
+ ->willReturn(new \ArrayIterator($collection));
+ $this->addressCollectionFactory->expects($this->atLeastOnce())->method('create')
+ ->willReturn($addressCollection);
+
+ $this->assertEquals($addressDataModel, $this->gridBlock->getAdditionalAddresses()[0]);
+ }
+
+ /**
+ * Test for \Magento\Customer\ViewModel\CustomerAddress::getStreetAddress method
+ */
+ public function testGetStreetAddress()
+ {
+ $street = ['Line 1', 'Line 2'];
+ $expectedAddress = 'Line 1, Line 2';
+ $address = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\AddressInterface::class);
+ $address->expects($this->atLeastOnce())->method('getStreet')->willReturn($street);
+ $this->assertEquals($expectedAddress, $this->gridBlock->getStreetAddress($address));
+ }
+
+ /**
+ * Test for \Magento\Customer\ViewModel\CustomerAddress::getCountryByCode method
+ */
+ public function testGetCountryByCode()
+ {
+ $countryId = 'US';
+ $countryName = 'United States';
+ $country = $this->getMockBuilder(\Magento\Directory\Model\Country::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['loadByCode', 'getName'])
+ ->getMock();
+ $this->countryFactory->expects($this->atLeastOnce())->method('create')->willReturn($country);
+ $country->expects($this->atLeastOnce())->method('loadByCode')->with($countryId)->willReturnSelf();
+ $country->expects($this->atLeastOnce())->method('getName')->willReturn($countryName);
+ $this->assertEquals($countryName, $this->gridBlock->getCountryByCode($countryId));
+ }
+}
diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php
index c2a795fc95016..7ae55f44421c7 100644
--- a/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Controller/Address/FormPostTest.php
@@ -455,14 +455,20 @@ public function testExecute(
$regionCode,
$newRegionId,
$newRegion,
- $newRegionCode
+ $newRegionCode,
+ $existingDefaultBilling = false,
+ $existingDefaultShipping = false,
+ $setDefaultBilling = false,
+ $setDefaultShipping = false
): void {
$existingAddressData = [
'country_id' => $countryId,
'region_id' => $regionId,
'region' => $region,
'region_code' => $regionCode,
- 'customer_id' => $customerId
+ 'customer_id' => $customerId,
+ 'default_billing' => $existingDefaultBilling,
+ 'default_shipping' => $existingDefaultShipping,
];
$newAddressData = [
'country_id' => $countryId,
@@ -486,8 +492,8 @@ public function testExecute(
->method('getParam')
->willReturnMap([
['id', null, $addressId],
- ['default_billing', false, $addressId],
- ['default_shipping', false, $addressId],
+ ['default_billing', $existingDefaultBilling, $setDefaultBilling],
+ ['default_shipping', $existingDefaultShipping, $setDefaultShipping],
]);
$this->addressRepository->expects($this->once())
@@ -565,11 +571,11 @@ public function testExecute(
->willReturnSelf();
$this->addressData->expects($this->once())
->method('setIsDefaultBilling')
- ->with()
+ ->with($setDefaultBilling)
->willReturnSelf();
$this->addressData->expects($this->once())
->method('setIsDefaultShipping')
- ->with()
+ ->with($setDefaultShipping)
->willReturnSelf();
$this->messageManager->expects($this->once())
@@ -628,11 +634,11 @@ public function dataProviderTestExecute(): array
[1, 1, 1, 2, null, null, 12, null, null],
[1, 1, 1, 2, 'Alaska', null, 12, null, 'CA'],
- [1, 1, 1, 2, 'Alaska', 'AK', 12, 'California', null],
+ [1, 1, 1, 2, 'Alaska', 'AK', 12, 'California', null, true, true, true, false],
- [1, 1, 1, 2, null, null, 12, null, null],
- [1, 1, 1, 2, 'Alaska', null, 12, null, 'CA'],
- [1, 1, 1, 2, 'Alaska', 'AK', 12, 'California', null],
+ [1, 1, 1, 2, null, null, 12, null, null, false, false, true, false],
+ [1, 1, 1, 2, 'Alaska', null, 12, null, 'CA', true, false, true, false],
+ [1, 1, 1, 2, 'Alaska', 'AK', 12, 'California', null, true, true, true, true],
];
}
diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php
index 78d9dd7003522..45e64f6557d51 100644
--- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/InlineEditTest.php
@@ -5,10 +5,14 @@
*/
namespace Magento\Customer\Test\Unit\Controller\Adminhtml\Index;
+use Magento\Customer\Model\AddressRegistry;
use Magento\Customer\Model\EmailNotificationInterface;
+use Magento\Framework\DataObject;
use Magento\Framework\Message\MessageInterface;
/**
+ * Unit tests for Inline customer edit
+ *
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
@@ -68,14 +72,27 @@ class InlineEditTest extends \PHPUnit\Framework\TestCase
/** @var EmailNotificationInterface|\PHPUnit_Framework_MockObject_MockObject */
private $emailNotification;
+ /** @var AddressRegistry|\PHPUnit_Framework_MockObject_MockObject */
+ private $addressRegistry;
+
/** @var array */
private $items;
+ /**
+ * Sets up mocks
+ *
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ */
protected function setUp()
{
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
- $this->request = $this->getMockForAbstractClass(\Magento\Framework\App\RequestInterface::class, [], '', false);
+ $this->request = $this->getMockForAbstractClass(
+ \Magento\Framework\App\RequestInterface::class,
+ [],
+ '',
+ false
+ );
$this->messageManager = $this->getMockForAbstractClass(
\Magento\Framework\Message\ManagerInterface::class,
[],
@@ -125,8 +142,12 @@ protected function setUp()
'',
false
);
- $this->logger = $this->getMockForAbstractClass(\Psr\Log\LoggerInterface::class, [], '', false);
-
+ $this->logger = $this->getMockForAbstractClass(
+ \Psr\Log\LoggerInterface::class,
+ [],
+ '',
+ false
+ );
$this->emailNotification = $this->getMockBuilder(EmailNotificationInterface::class)
->disableOriginalConstructor()
->getMock();
@@ -138,6 +159,7 @@ protected function setUp()
'messageManager' => $this->messageManager,
]
);
+ $this->addressRegistry = $this->createMock(\Magento\Customer\Model\AddressRegistry::class);
$this->controller = $objectManager->getObject(
\Magento\Customer\Controller\Adminhtml\Index\InlineEdit::class,
[
@@ -150,6 +172,7 @@ protected function setUp()
'addressDataFactory' => $this->addressDataFactory,
'addressRepository' => $this->addressRepository,
'logger' => $this->logger,
+ 'addressRegistry' => $this->addressRegistry
]
);
$reflection = new \ReflectionClass(get_class($this->controller));
@@ -166,6 +189,8 @@ protected function setUp()
}
/**
+ * Prepare mocks for tests
+ *
* @param int $populateSequence
*/
protected function prepareMocksForTesting($populateSequence = 0)
@@ -204,6 +229,9 @@ protected function prepareMocksForTesting($populateSequence = 0)
->willReturn(12);
}
+ /**
+ * Prepare mocks for update customers default billing address use case
+ */
protected function prepareMocksForUpdateDefaultBilling()
{
$this->prepareMocksForProcessAddressData();
@@ -212,12 +240,15 @@ protected function prepareMocksForUpdateDefaultBilling()
'firstname' => 'Firstname',
'lastname' => 'Lastname',
];
- $this->customerData->expects($this->once())
+ $this->customerData->expects($this->exactly(2))
->method('getAddresses')
->willReturn([$this->address]);
$this->address->expects($this->once())
->method('isDefaultBilling')
->willReturn(true);
+ $this->addressRegistry->expects($this->once())
+ ->method('retrieve')
+ ->willReturn(new DataObject());
$this->dataObjectHelper->expects($this->at(0))
->method('populateWithArray')
->with(
@@ -227,6 +258,9 @@ protected function prepareMocksForUpdateDefaultBilling()
);
}
+ /**
+ * Prepare mocks for processing customers address data use case
+ */
protected function prepareMocksForProcessAddressData()
{
$this->customerData->expects($this->once())
@@ -237,6 +271,9 @@ protected function prepareMocksForProcessAddressData()
->willReturn('Lastname');
}
+ /**
+ * Prepare mocks for error messages processing test
+ */
protected function prepareMocksForErrorMessagesProcessing()
{
$this->messageManager->expects($this->atLeastOnce())
@@ -261,6 +298,9 @@ protected function prepareMocksForErrorMessagesProcessing()
->willReturnSelf();
}
+ /**
+ * Unit test for updating customers billing address use case
+ */
public function testExecuteWithUpdateBilling()
{
$this->prepareMocksForTesting(1);
@@ -281,6 +321,9 @@ public function testExecuteWithUpdateBilling()
$this->assertSame($this->resultJson, $this->controller->execute());
}
+ /**
+ * Unit test for creating customer with empty data use case
+ */
public function testExecuteWithoutItems()
{
$this->resultJsonFactory->expects($this->once())
@@ -305,6 +348,9 @@ public function testExecuteWithoutItems()
$this->assertSame($this->resultJson, $this->controller->execute());
}
+ /**
+ * Unit test for verifying Localized Exception during inline edit
+ */
public function testExecuteLocalizedException()
{
$exception = new \Magento\Framework\Exception\LocalizedException(__('Exception message'));
@@ -312,6 +358,9 @@ public function testExecuteLocalizedException()
$this->customerData->expects($this->once())
->method('getDefaultBilling')
->willReturn(false);
+ $this->customerData->expects($this->once())
+ ->method('getAddresses')
+ ->willReturn([]);
$this->customerRepository->expects($this->once())
->method('save')
->with($this->customerData)
@@ -327,6 +376,9 @@ public function testExecuteLocalizedException()
$this->assertSame($this->resultJson, $this->controller->execute());
}
+ /**
+ * Unit test for verifying Execute Exception during inline edit
+ */
public function testExecuteException()
{
$exception = new \Exception('Exception message');
@@ -334,6 +386,9 @@ public function testExecuteException()
$this->customerData->expects($this->once())
->method('getDefaultBilling')
->willReturn(false);
+ $this->customerData->expects($this->once())
+ ->method('getAddresses')
+ ->willReturn([]);
$this->customerRepository->expects($this->once())
->method('save')
->with($this->customerData)
diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php
index 884aab711d168..10144bdc318c1 100644
--- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/MassAssignGroupTest.php
@@ -11,6 +11,7 @@
/**
* Class MassAssignGroupTest
+ *
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class MassAssignGroupTest extends \PHPUnit\Framework\TestCase
@@ -70,12 +71,17 @@ class MassAssignGroupTest extends \PHPUnit\Framework\TestCase
*/
protected $customerRepositoryMock;
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
$objectManagerHelper = new ObjectManagerHelper($this);
$this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class);
- $resultRedirectFactory = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class);
+ $resultRedirectFactory = $this->createMock(
+ \Magento\Backend\Model\View\Result\RedirectFactory::class
+ );
$this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class);
$this->requestMock = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class)
->disableOriginalConstructor()->getMock();
@@ -129,7 +135,8 @@ protected function setUp()
$this->customerCollectionFactoryMock->expects($this->once())
->method('create')
->willReturn($this->customerCollectionMock);
- $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class)
+ $this->customerRepositoryMock = $this
+ ->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class)
->getMockForAbstractClass();
$this->massAction = $objectManagerHelper->getObject(
\Magento\Customer\Controller\Adminhtml\Index\MassAssignGroup::class,
@@ -142,12 +149,18 @@ protected function setUp()
);
}
+ /**
+ * Unit test to verify mass customer group assignment use case
+ *
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
public function testExecute()
{
$customersIds = [10, 11, 12];
- $customerMock = $this->getMockBuilder(
- \Magento\Customer\Api\Data\CustomerInterface::class
- )->getMockForAbstractClass();
+ $customerMock = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class)
+ ->setMethods(['setData'])
+ ->disableOriginalConstructor()
+ ->getMockForAbstractClass();
$this->customerCollectionMock->expects($this->any())
->method('getAllIds')
->willReturn($customersIds);
@@ -168,6 +181,11 @@ public function testExecute()
$this->massAction->execute();
}
+ /**
+ * Unit test to verify expected error during mass customer group assignment use case
+ *
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
public function testExecuteWithException()
{
$customersIds = [10, 11, 12];
diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php
index c52d5b2fb370f..8d802e907a810 100644
--- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php
@@ -14,6 +14,8 @@
use Magento\Framework\Controller\Result\Redirect;
/**
+ * Testing Save Customer use case from admin page
+ *
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @covers \Magento\Customer\Controller\Adminhtml\Index\Save
@@ -435,6 +437,10 @@ public function testExecuteWithExistentCustomer()
$customerEmail = 'customer@email.com';
$customerMock->expects($this->once())->method('getEmail')->willReturn($customerEmail);
+ $customerMock->expects($this->once())
+ ->method('getAddresses')
+ ->willReturn([]);
+
$this->emailNotificationMock->expects($this->once())
->method('credentialsChanged')
->with($customerMock, $customerEmail)
diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php
index 0273c445bdd2a..22c9d90c086dc 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php
@@ -1472,12 +1472,15 @@ public function testChangePassword()
$passwordHash = '1a2b3f4c';
$this->reInitModel();
- $customer = $this->getMockBuilder(Customer::class)
+ $customer = $this->getMockBuilder(CustomerInterface::class)
->disableOriginalConstructor()
->getMock();
$customer->expects($this->any())
->method('getId')
->willReturn($customerId);
+ $customer->expects($this->once())
+ ->method('getAddresses')
+ ->willReturn([]);
$this->customerRepository
->expects($this->once())
diff --git a/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php b/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php
index cb396e32509b7..65831069aa1fb 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/CustomerTest.php
@@ -13,9 +13,12 @@
use Magento\Customer\Model\Customer;
use Magento\Customer\Model\AccountConfirmation;
+use Magento\Customer\Model\ResourceModel\Address\CollectionFactory as AddressCollectionFactory;
+use Magento\Customer\Api\Data\CustomerInterfaceFactory;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.TooManyFields)
*/
class CustomerTest extends \PHPUnit\Framework\TestCase
{
@@ -68,6 +71,21 @@ class CustomerTest extends \PHPUnit\Framework\TestCase
*/
private $accountConfirmation;
+ /**
+ * @var AddressCollectionFactory|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $addressesFactory;
+
+ /**
+ * @var CustomerInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $customerDataFactory;
+
+ /**
+ * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $dataObjectHelper;
+
protected function setUp()
{
$this->_website = $this->createMock(\Magento\Store\Model\Website::class);
@@ -100,6 +118,19 @@ protected function setUp()
$this->_encryptor = $this->createMock(\Magento\Framework\Encryption\EncryptorInterface::class);
$helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->accountConfirmation = $this->createMock(AccountConfirmation::class);
+ $this->addressesFactory = $this->getMockBuilder(AddressCollectionFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $this->customerDataFactory = $this->getMockBuilder(CustomerInterfaceFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $this->dataObjectHelper = $this->getMockBuilder(\Magento\Framework\Api\DataObjectHelper::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['populateWithArray'])
+ ->getMock();
+
$this->_model = $helper->getObject(
\Magento\Customer\Model\Customer::class,
[
@@ -112,7 +143,10 @@ protected function setUp()
'registry' => $this->registryMock,
'resource' => $this->resourceMock,
'dataObjectProcessor' => $this->dataObjectProcessor,
- 'accountConfirmation' => $this->accountConfirmation
+ 'accountConfirmation' => $this->accountConfirmation,
+ '_addressesFactory' => $this->addressesFactory,
+ 'customerDataFactory' => $this->customerDataFactory,
+ 'dataObjectHelper' => $this->dataObjectHelper
]
);
}
@@ -186,13 +220,13 @@ public function testSendNewAccountEmailWithoutStoreId()
->will($this->returnValue($transportMock));
$this->_model->setData([
- 'website_id' => 1,
- 'store_id' => 1,
- 'email' => 'email@example.com',
- 'firstname' => 'FirstName',
- 'lastname' => 'LastName',
- 'middlename' => 'MiddleName',
- 'prefix' => 'Name Prefix',
+ 'website_id' => 1,
+ 'store_id' => 1,
+ 'email' => 'email@example.com',
+ 'firstname' => 'FirstName',
+ 'lastname' => 'LastName',
+ 'middlename' => 'MiddleName',
+ 'prefix' => 'Name Prefix',
]);
$this->_model->sendNewAccountEmail('registered');
}
@@ -310,4 +344,43 @@ public function testUpdateData()
$this->assertEquals($this->_model->getData(), $expectedResult);
}
+
+ /**
+ * Test for the \Magento\Customer\Model\Customer::getDataModel() method
+ */
+ public function testGetDataModel()
+ {
+ $customerId = 1;
+ $this->_model->setEntityId($customerId);
+ $this->_model->setId($customerId);
+ $addressDataModel = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\AddressInterface::class);
+ $address = $this->getMockBuilder(\Magento\Customer\Model\Address::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setCustomer', 'getDataModel'])
+ ->getMock();
+ $address->expects($this->atLeastOnce())->method('getDataModel')->willReturn($addressDataModel);
+ $addresses = new \ArrayIterator([$address, $address]);
+ $addressCollection = $this->getMockBuilder(\Magento\Customer\Model\ResourceModel\Address\Collection::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setCustomerFilter', 'addAttributeToSelect', 'getIterator', 'getItems'])
+ ->getMock();
+ $addressCollection->expects($this->atLeastOnce())->method('setCustomerFilter')->willReturnSelf();
+ $addressCollection->expects($this->atLeastOnce())->method('addAttributeToSelect')->willReturnSelf();
+ $addressCollection->expects($this->atLeastOnce())->method('getIterator')
+ ->willReturn($addresses);
+ $addressCollection->expects($this->atLeastOnce())->method('getItems')
+ ->willReturn($addresses);
+ $this->addressesFactory->expects($this->atLeastOnce())->method('create')->willReturn($addressCollection);
+ $customerDataObject = $this->getMockForAbstractClass(\Magento\Customer\Api\Data\CustomerInterface::class);
+ $this->customerDataFactory->expects($this->atLeastOnce())->method('create')->willReturn($customerDataObject);
+ $this->dataObjectHelper->expects($this->atLeastOnce())->method('populateWithArray')
+ ->with($customerDataObject, $this->_model->getData(), \Magento\Customer\Api\Data\CustomerInterface::class)
+ ->willReturnSelf();
+ $customerDataObject->expects($this->atLeastOnce())->method('setAddresses')
+ ->with([$addressDataModel, $addressDataModel])
+ ->willReturnSelf();
+ $customerDataObject->expects($this->atLeastOnce())->method('setId')->with($customerId)->willReturnSelf();
+ $this->_model->getDataModel();
+ $this->assertEquals($customerDataObject, $this->_model->getDataModel());
+ }
}
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php
index 658472d13ab93..83915731ea5a9 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/AttributeMetadataCacheTest.php
@@ -15,7 +15,14 @@
use Magento\Framework\App\CacheInterface;
use Magento\Framework\Serialize\SerializerInterface;
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Store\Api\Data\StoreInterface;
+use Magento\Store\Model\StoreManagerInterface;
+/**
+ * AttributeMetadataCache Test
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
class AttributeMetadataCacheTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -43,6 +50,16 @@ class AttributeMetadataCacheTest extends \PHPUnit\Framework\TestCase
*/
private $attributeMetadataCache;
+ /**
+ * @var StoreInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $storeMock;
+
+ /**
+ * @var StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ private $storeManagerMock;
+
protected function setUp()
{
$objectManager = new ObjectManager($this);
@@ -50,13 +67,18 @@ protected function setUp()
$this->stateMock = $this->createMock(StateInterface::class);
$this->serializerMock = $this->createMock(SerializerInterface::class);
$this->attributeMetadataHydratorMock = $this->createMock(AttributeMetadataHydrator::class);
+ $this->storeMock = $this->createMock(StoreInterface::class);
+ $this->storeManagerMock = $this->createMock(StoreManagerInterface::class);
+ $this->storeManagerMock->method('getStore')->willReturn($this->storeMock);
+ $this->storeMock->method('getId')->willReturn(1);
$this->attributeMetadataCache = $objectManager->getObject(
AttributeMetadataCache::class,
[
'cache' => $this->cacheMock,
'state' => $this->stateMock,
'serializer' => $this->serializerMock,
- 'attributeMetadataHydrator' => $this->attributeMetadataHydratorMock
+ 'attributeMetadataHydrator' => $this->attributeMetadataHydratorMock,
+ 'storeManager' => $this->storeManagerMock
]
);
}
@@ -80,7 +102,8 @@ public function testLoadNoCache()
{
$entityType = 'EntityType';
$suffix = 'none';
- $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix;
+ $storeId = 1;
+ $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId;
$this->stateMock->expects($this->once())
->method('isEnabled')
->with(Type::TYPE_IDENTIFIER)
@@ -96,7 +119,8 @@ public function testLoad()
{
$entityType = 'EntityType';
$suffix = 'none';
- $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix;
+ $storeId = 1;
+ $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId;
$serializedString = 'serialized string';
$attributeMetadataOneData = [
'attribute_code' => 'attribute_code',
@@ -156,7 +180,8 @@ public function testSave()
{
$entityType = 'EntityType';
$suffix = 'none';
- $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix;
+ $storeId = 1;
+ $cacheKey = AttributeMetadataCache::ATTRIBUTE_METADATA_CACHE_PREFIX . $entityType . $suffix . $storeId;
$serializedString = 'serialized string';
$attributeMetadataOneData = [
'attribute_code' => 'attribute_code',
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php
index e4dc22ba40e31..5b4b50ca82117 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Metadata/Form/AbstractDataTest.php
@@ -205,37 +205,32 @@ public function applyOutputFilterDataProvider()
}
/**
+ * Tests input validation rules.
+ *
* @param null|string $value
* @param null|string $label
* @param null|string $inputValidation
* @param bool|array $expectedOutput
* @dataProvider validateInputRuleDataProvider
*/
- public function testValidateInputRule($value, $label, $inputValidation, $expectedOutput)
+ public function testValidateInputRule($value, $label, $inputValidation, $expectedOutput): void
{
$validationRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class)
->disableOriginalConstructor()
->setMethods(['getName', 'getValue'])
->getMockForAbstractClass();
- $validationRule->expects($this->any())
- ->method('getName')
- ->will($this->returnValue('input_validation'));
- $validationRule->expects($this->any())
- ->method('getValue')
- ->will($this->returnValue($inputValidation));
-
- $this->_attributeMock->expects($this->any())->method('getStoreLabel')->will($this->returnValue($label));
- $this->_attributeMock->expects(
- $this->any()
- )->method(
- 'getValidationRules'
- )->will(
- $this->returnValue(
- [
- $validationRule,
- ]
- )
- );
+
+ $validationRule->method('getName')
+ ->willReturn('input_validation');
+
+ $validationRule->method('getValue')
+ ->willReturn($inputValidation);
+
+ $this->_attributeMock->method('getStoreLabel')
+ ->willReturn($label);
+
+ $this->_attributeMock->method('getValidationRules')
+ ->willReturn([$validationRule]);
$this->assertEquals($expectedOutput, $this->_model->validateInputRule($value));
}
@@ -256,6 +251,16 @@ public function validateInputRuleDataProvider()
\Zend_Validate_Alnum::NOT_ALNUM => '"mylabel" contains non-alphabetic or non-numeric characters.'
]
],
+ [
+ 'abc qaz',
+ 'mylabel',
+ 'alphanumeric',
+ [
+ \Zend_Validate_Alnum::NOT_ALNUM => '"mylabel" contains non-alphabetic or non-numeric characters.'
+ ]
+ ],
+ ['abcqaz', 'mylabel', 'alphanumeric', true],
+ ['abc qaz', 'mylabel', 'alphanum-with-spaces', true],
[
'!@#$',
'mylabel',
diff --git a/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php b/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php
index 8971f155f782e..188bbde71c104 100644
--- a/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Observer/UpgradeCustomerPasswordObserverTest.php
@@ -7,6 +7,9 @@
use Magento\Customer\Observer\UpgradeCustomerPasswordObserver;
+/**
+ * Class UpgradeCustomerPasswordObserverTest for testing upgrade password observer
+ */
class UpgradeCustomerPasswordObserverTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -29,9 +32,13 @@ class UpgradeCustomerPasswordObserverTest extends \PHPUnit\Framework\TestCase
*/
protected $customerRegistry;
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
- $this->customerRepository = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class)
+ $this->customerRepository = $this
+ ->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class)
->getMockForAbstractClass();
$this->customerRegistry = $this->getMockBuilder(\Magento\Customer\Model\CustomerRegistry::class)
->disableOriginalConstructor()
@@ -47,6 +54,9 @@ protected function setUp()
);
}
+ /**
+ * Unit test for verifying customers password upgrade observer
+ */
public function testUpgradeCustomerPassword()
{
$customerId = '1';
@@ -57,6 +67,8 @@ public function testUpgradeCustomerPassword()
->setMethods(['getId'])
->getMock();
$customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class)
+ ->setMethods(['setData'])
+ ->disableOriginalConstructor()
->getMockForAbstractClass();
$customerSecure = $this->getMockBuilder(\Magento\Customer\Model\Data\CustomerSecure::class)
->disableOriginalConstructor()
diff --git a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php
index 130b3acd11e76..07b0a76043200 100644
--- a/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Ui/Component/Listing/Column/ValidationRulesTest.php
@@ -18,12 +18,6 @@ class ValidationRulesTest extends \PHPUnit\Framework\TestCase
protected function setUp()
{
- $this->validationRules = $this->getMockBuilder(
- \Magento\Customer\Ui\Component\Listing\Column\ValidationRules::class
- )
- ->disableOriginalConstructor()
- ->getMock();
-
$this->validationRule = $this->getMockBuilder(\Magento\Customer\Api\Data\ValidationRuleInterface::class)
->disableOriginalConstructor()
->getMock();
@@ -31,20 +25,26 @@ protected function setUp()
$this->validationRules = new ValidationRules();
}
- public function testGetValidationRules()
+ /**
+ * Tests input validation rules
+ *
+ * @param String $validationRule - provided input validation rules
+ * @param String $validationClass - expected input validation class
+ * @dataProvider validationRulesDataProvider
+ */
+ public function testGetValidationRules(String $validationRule, String $validationClass): void
{
$expectsRules = [
'required-entry' => true,
- 'validate-number' => true,
+ $validationClass => true,
];
- $this->validationRule->expects($this->atLeastOnce())
- ->method('getName')
+ $this->validationRule->method('getName')
->willReturn('input_validation');
- $this->validationRule->expects($this->atLeastOnce())
- ->method('getValue')
- ->willReturn('numeric');
- $this->assertEquals(
+ $this->validationRule->method('getValue')
+ ->willReturn($validationRule);
+
+ self::assertEquals(
$expectsRules,
$this->validationRules->getValidationRules(
true,
@@ -56,6 +56,23 @@ public function testGetValidationRules()
);
}
+ /**
+ * Provides possible validation rules.
+ *
+ * @return array
+ */
+ public function validationRulesDataProvider(): array
+ {
+ return [
+ ['alpha', 'validate-alpha'],
+ ['numeric', 'validate-number'],
+ ['alphanumeric', 'validate-alphanum'],
+ ['alphanum-with-spaces', 'validate-alphanum-with-spaces'],
+ ['url', 'validate-url'],
+ ['email', 'validate-email']
+ ];
+ }
+
public function testGetValidationRulesWithOnlyRequiredRule()
{
$expectsRules = [
diff --git a/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php b/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php
index b8f83421a6d62..6befec8e942a1 100644
--- a/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php
+++ b/app/code/Magento/Customer/Ui/Component/Listing/Column/ValidationRules.php
@@ -7,6 +7,9 @@
use Magento\Customer\Api\Data\ValidationRuleInterface;
+/**
+ * Provides validation classes according to corresponding rules.
+ */
class ValidationRules
{
/**
@@ -16,6 +19,7 @@ class ValidationRules
'alpha' => 'validate-alpha',
'numeric' => 'validate-number',
'alphanumeric' => 'validate-alphanum',
+ 'alphanum-with-spaces' => 'validate-alphanum-with-spaces',
'url' => 'validate-url',
'email' => 'validate-email',
];
diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml
index f053805409fe5..f5ee2b347a5b2 100644
--- a/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml
+++ b/app/code/Magento/Customer/view/frontend/layout/customer_address_form.xml
@@ -20,6 +20,7 @@
Magento\Customer\Block\DataProviders\AddressAttributeData
+ Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData
diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_address_index.xml b/app/code/Magento/Customer/view/frontend/layout/customer_address_index.xml
index bad120e46277f..2c5e5b98e5f7b 100644
--- a/app/code/Magento/Customer/view/frontend/layout/customer_address_index.xml
+++ b/app/code/Magento/Customer/view/frontend/layout/customer_address_index.xml
@@ -13,6 +13,7 @@
+