From 2716339b3c3c97e9af35ef6aecaf634b1fce8f8d Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Wed, 28 Oct 2020 17:25:55 -0500 Subject: [PATCH 01/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Model/Coupon/Quote/UpdateCouponUsages.php | 18 +-- .../SalesRule/Model/CouponUsageConsumer.php | 104 ++++++++++++++++++ .../Model/Service/CouponUsageScheduler.php | 99 +++++++++++++++++ .../Magento/SalesRule/etc/communication.xml | 3 + app/code/Magento/SalesRule/etc/queue.xml | 3 + .../Magento/SalesRule/etc/queue_consumer.xml | 1 + .../Magento/SalesRule/etc/queue_publisher.xml | 3 + .../Magento/SalesRule/etc/queue_topology.xml | 1 + 8 files changed, 223 insertions(+), 9 deletions(-) create mode 100644 app/code/Magento/SalesRule/Model/CouponUsageConsumer.php create mode 100644 app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php diff --git a/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php b/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php index 0ee2ee09cad57..8f29dd8e0a63c 100644 --- a/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php +++ b/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php @@ -8,9 +8,9 @@ namespace Magento\SalesRule\Model\Coupon\Quote; use Magento\Quote\Api\Data\CartInterface; -use Magento\SalesRule\Model\Coupon\Usage\Processor as CouponUsageProcessor; use Magento\SalesRule\Model\Coupon\Usage\UpdateInfo; use Magento\SalesRule\Model\Coupon\Usage\UpdateInfoFactory; +use Magento\SalesRule\Model\Service\CouponUsageScheduler; /** * Updates the coupon usages from quote @@ -18,24 +18,24 @@ class UpdateCouponUsages { /** - * @var CouponUsageProcessor + * @var UpdateInfoFactory */ - private $couponUsageProcessor; + private $updateInfoFactory; /** - * @var UpdateInfoFactory + * @var CouponUsageScheduler */ - private $updateInfoFactory; + private $couponUsageScheduler; /** - * @param CouponUsageProcessor $couponUsageProcessor + * @param CouponUsageScheduler $couponUsageScheduler * @param UpdateInfoFactory $updateInfoFactory */ public function __construct( - CouponUsageProcessor $couponUsageProcessor, + CouponUsageScheduler $couponUsageScheduler, UpdateInfoFactory $updateInfoFactory ) { - $this->couponUsageProcessor = $couponUsageProcessor; + $this->couponUsageScheduler = $couponUsageScheduler; $this->updateInfoFactory = $updateInfoFactory; } @@ -59,6 +59,6 @@ public function execute(CartInterface $quote, bool $increment): void $updateInfo->setCustomerId((int)$quote->getCustomerId()); $updateInfo->setIsIncrement($increment); - $this->couponUsageProcessor->process($updateInfo); + $this->couponUsageScheduler->schedule($updateInfo); } } diff --git a/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php b/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php new file mode 100644 index 0000000000000..c84d5f90a1125 --- /dev/null +++ b/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php @@ -0,0 +1,104 @@ +updateInfoFactory = $updateInfoFactory; + $this->processor = $processor; + $this->logger = $logger; + $this->serializer = $serializer; + $this->entityManager = $entityManager; + } + + /** + * Process coupon usage update + * + * @param OperationInterface $operation + * @return void + * @throws \Exception + */ + public function process(OperationInterface $operation): void + { + $q = 2; + + try { + $serializedData = $operation->getSerializedData(); + $data = $this->serializer->unserialize($serializedData); + $updateInfo = $this->updateInfoFactory->create(); + $updateInfo->setData($data); + $this->processor->process($updateInfo); + } catch (NotFoundException $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = $e->getMessage(); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + $status = OperationInterface::STATUS_TYPE_NOT_RETRIABLY_FAILED; + $errorCode = $e->getCode(); + $message = __('Sorry, something went wrong during rule usage update. Please see log for details.'); + } + + $operation->setStatus($status ?? OperationInterface::STATUS_TYPE_COMPLETE) + ->setErrorCode($errorCode ?? null) + ->setResultMessage($message ?? null); + + $this->entityManager->save($operation); + } +} diff --git a/app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php b/app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php new file mode 100644 index 0000000000000..79ecfb61290df --- /dev/null +++ b/app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php @@ -0,0 +1,99 @@ +bulkManagement = $bulkManagement; + $this->operationFactory = $operartionFactory; + $this->identityService = $identityService; + $this->serializer = $serializer; + $this->userContext = $userContext; + } + + /** + * Schedule sales rule usage info + * + * @param string $updateInfo + * @return boolean + */ + public function schedule(UpdateInfo $updateInfo): bool + { + $bulkUuid = $this->identityService->generateId(); + $bulkDescription = __('Rule processing: %1', implode(',', $updateInfo->getAppliedRuleIds())); + + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => self::TOPIC_NAME, + 'serialized_data' => $this->serializer->serialize($updateInfo->getData()), + 'status' => OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + $operation = $this->operationFactory->create($data); + + return $this->bulkManagement->scheduleBulk( + $bulkUuid, + [$operation], + $bulkDescription, + $this->userContext->getUserId() + ); + } +} diff --git a/app/code/Magento/SalesRule/etc/communication.xml b/app/code/Magento/SalesRule/etc/communication.xml index 4c905fa83e2fd..786e866f0e3c5 100644 --- a/app/code/Magento/SalesRule/etc/communication.xml +++ b/app/code/Magento/SalesRule/etc/communication.xml @@ -9,4 +9,7 @@ + + + diff --git a/app/code/Magento/SalesRule/etc/queue.xml b/app/code/Magento/SalesRule/etc/queue.xml index 8217a0b9f6c1a..87dce71b53054 100644 --- a/app/code/Magento/SalesRule/etc/queue.xml +++ b/app/code/Magento/SalesRule/etc/queue.xml @@ -9,4 +9,7 @@ + + + diff --git a/app/code/Magento/SalesRule/etc/queue_consumer.xml b/app/code/Magento/SalesRule/etc/queue_consumer.xml index 9eb585f48e8e3..bcebaf6a543b9 100644 --- a/app/code/Magento/SalesRule/etc/queue_consumer.xml +++ b/app/code/Magento/SalesRule/etc/queue_consumer.xml @@ -7,4 +7,5 @@ --> + diff --git a/app/code/Magento/SalesRule/etc/queue_publisher.xml b/app/code/Magento/SalesRule/etc/queue_publisher.xml index 0863fba2307c5..f1b8bddf2c090 100644 --- a/app/code/Magento/SalesRule/etc/queue_publisher.xml +++ b/app/code/Magento/SalesRule/etc/queue_publisher.xml @@ -9,4 +9,7 @@ + + + diff --git a/app/code/Magento/SalesRule/etc/queue_topology.xml b/app/code/Magento/SalesRule/etc/queue_topology.xml index fd6a9bf36721c..3902c8a3ab36f 100644 --- a/app/code/Magento/SalesRule/etc/queue_topology.xml +++ b/app/code/Magento/SalesRule/etc/queue_topology.xml @@ -8,5 +8,6 @@ + From 47d016b09da639508939187506300bf47f3e1e10 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 29 Oct 2020 10:45:55 -0500 Subject: [PATCH 02/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- app/code/Magento/SalesRule/Model/CouponUsageConsumer.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php b/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php index c84d5f90a1125..0520cb658e408 100644 --- a/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php +++ b/app/code/Magento/SalesRule/Model/CouponUsageConsumer.php @@ -75,8 +75,6 @@ public function __construct( */ public function process(OperationInterface $operation): void { - $q = 2; - try { $serializedData = $operation->getSerializedData(); $data = $this->serializer->unserialize($serializedData); From 585a937b065bc798e491aec9bee73eb3280d7158 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 29 Oct 2020 10:48:16 -0500 Subject: [PATCH 03/62] MDVA-31238: Problems with indexer --- app/code/Magento/SalesRule/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/composer.json b/app/code/Magento/SalesRule/composer.json index 572e191093275..580455b693153 100644 --- a/app/code/Magento/SalesRule/composer.json +++ b/app/code/Magento/SalesRule/composer.json @@ -25,7 +25,8 @@ "magento/module-widget": "*", "magento/module-captcha": "*", "magento/module-checkout": "*", - "magento/module-authorization": "*" + "magento/module-authorization": "*", + "magento/module-asynchronous-operations": "*" }, "suggest": { "magento/module-sales-rule-sample-data": "*" From 91f35a10457494a9a848775b58ad1797870bff12 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 29 Oct 2020 10:49:56 -0500 Subject: [PATCH 04/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- app/code/Magento/SalesRule/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/SalesRule/composer.json b/app/code/Magento/SalesRule/composer.json index 580455b693153..20246f67e337e 100644 --- a/app/code/Magento/SalesRule/composer.json +++ b/app/code/Magento/SalesRule/composer.json @@ -7,6 +7,7 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", + "magento/framework-bulk": "*", "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-catalog-rule": "*", From b6cb6972a4da820a87188a7778214479f8cff6a4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 29 Oct 2020 17:16:29 -0500 Subject: [PATCH 05/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- app/code/Magento/Catalog/etc/di.xml | 5 ++ .../Model/Coupon/Quote/UpdateCouponUsages.php | 14 +++--- ...Scheduler.php => CouponUsagePublisher.php} | 6 +-- .../SalesRule/Plugin/CouponUsagesTest.php | 50 +++++++++++++++++++ 4 files changed, 65 insertions(+), 10 deletions(-) rename app/code/Magento/SalesRule/Model/Service/{CouponUsageScheduler.php => CouponUsagePublisher.php} (95%) diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 8a116282e2578..149003104a9fd 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -1319,4 +1319,9 @@ + + + Magento\Catalog\CategoryImageUpload + + diff --git a/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php b/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php index 8f29dd8e0a63c..02da921e032e0 100644 --- a/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php +++ b/app/code/Magento/SalesRule/Model/Coupon/Quote/UpdateCouponUsages.php @@ -10,7 +10,7 @@ use Magento\Quote\Api\Data\CartInterface; use Magento\SalesRule\Model\Coupon\Usage\UpdateInfo; use Magento\SalesRule\Model\Coupon\Usage\UpdateInfoFactory; -use Magento\SalesRule\Model\Service\CouponUsageScheduler; +use Magento\SalesRule\Model\Service\CouponUsagePublisher; /** * Updates the coupon usages from quote @@ -23,19 +23,19 @@ class UpdateCouponUsages private $updateInfoFactory; /** - * @var CouponUsageScheduler + * @var CouponUsagePublisher */ - private $couponUsageScheduler; + private $couponUsagePublisher; /** - * @param CouponUsageScheduler $couponUsageScheduler + * @param CouponUsagePublisher $couponUsagePublisher * @param UpdateInfoFactory $updateInfoFactory */ public function __construct( - CouponUsageScheduler $couponUsageScheduler, + CouponUsagePublisher $couponUsagePublisher, UpdateInfoFactory $updateInfoFactory ) { - $this->couponUsageScheduler = $couponUsageScheduler; + $this->couponUsagePublisher = $couponUsagePublisher; $this->updateInfoFactory = $updateInfoFactory; } @@ -59,6 +59,6 @@ public function execute(CartInterface $quote, bool $increment): void $updateInfo->setCustomerId((int)$quote->getCustomerId()); $updateInfo->setIsIncrement($increment); - $this->couponUsageScheduler->schedule($updateInfo); + $this->couponUsagePublisher->publish($updateInfo); } } diff --git a/app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php b/app/code/Magento/SalesRule/Model/Service/CouponUsagePublisher.php similarity index 95% rename from app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php rename to app/code/Magento/SalesRule/Model/Service/CouponUsagePublisher.php index 79ecfb61290df..1d1bbb1f63ed3 100644 --- a/app/code/Magento/SalesRule/Model/Service/CouponUsageScheduler.php +++ b/app/code/Magento/SalesRule/Model/Service/CouponUsagePublisher.php @@ -18,7 +18,7 @@ /** * Scheduler for coupon usage queue */ -class CouponUsageScheduler +class CouponUsagePublisher { private const TOPIC_NAME = 'sales.rule.update.coupon.usage'; @@ -69,12 +69,12 @@ public function __construct( } /** - * Schedule sales rule usage info + * Publish sales rule usage info into the queue * * @param string $updateInfo * @return boolean */ - public function schedule(UpdateInfo $updateInfo): bool + public function publish(UpdateInfo $updateInfo): bool { $bulkUuid = $this->identityService->generateId(); $bulkDescription = __('Rule processing: %1', implode(',', $updateInfo->getAppliedRuleIds())); diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php index 4ed096fa4418a..bf8e9174a8216 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php @@ -14,6 +14,9 @@ use Magento\SalesRule\Model\Coupon; use Magento\SalesRule\Model\ResourceModel\Coupon\Usage; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException; +use Magento\TestFramework\MessageQueue\PreconditionFailedException; +use Magento\TestFramework\MessageQueue\PublisherConsumerController; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -26,6 +29,16 @@ */ class CouponUsagesTest extends TestCase { + /** + * @var PublisherConsumerController + */ + private $publisherConsumerController; + + /** + * @var array + */ + private $consumers = ['sales.rule.update.coupon.usage']; + /** * @var ObjectManagerInterface */ @@ -61,6 +74,35 @@ protected function setUp(): void $this->couponUsage = $this->objectManager->get(DataObject::class); $this->quoteManagement = $this->objectManager->get(QuoteManagement::class); $this->orderService = $this->objectManager->get(OrderService::class); + + $this->publisherConsumerController = Bootstrap::getObjectManager()->create( + PublisherConsumerController::class, + [ + 'consumers' => $this->consumers, + 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", + 'maxMessages' => null, + 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() + ] + ); + try { + $this->publisherConsumerController->startConsumers(); + } catch (EnvironmentPreconditionException $e) { + $this->markTestSkipped($e->getMessage()); + } catch (PreconditionFailedException $e) { + $this->fail( + $e->getMessage() + ); + } + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->publisherConsumerController->stopConsumers(); + parent::tearDown(); } /** @@ -74,6 +116,11 @@ public function testSubmitQuoteAndCancelOrder() $couponCode = 'one_usage'; $reservedOrderId = 'test01'; + $binDirectory = realpath(INTEGRATION_TESTS_DIR . '/bin/'); + $magentoCli = $binDirectory . '/magento'; + $consumerStartCommand = "php {$magentoCli} queue:consumers:start sales.rule.update.coupon.usage &"; + exec($consumerStartCommand); + /** @var Coupon $coupon */ $coupon = $this->objectManager->get(Coupon::class); $coupon->loadByCode($couponCode); @@ -83,6 +130,9 @@ public function testSubmitQuoteAndCancelOrder() // Make sure coupon usages value is incremented then order is placed. $order = $this->quoteManagement->submit($quote); + + + sleep(15); // timeout to processing Magento queue $this->usage->loadByCustomerCoupon($this->couponUsage, $customerId, $coupon->getId()); $coupon->loadByCode($couponCode); From a72c0431710c8136b185b17dea059217dee49b53 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 2 Nov 2020 14:54:37 -0600 Subject: [PATCH 06/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php index bf8e9174a8216..cc23259dba58c 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php @@ -109,6 +109,7 @@ protected function tearDown(): void * Test increasing coupon usages after after order placing and decreasing after order cancellation. * * @magentoDataFixture Magento/SalesRule/_files/coupons_limited_order.php + * @magentoDbIsolation disabled */ public function testSubmitQuoteAndCancelOrder() { @@ -130,9 +131,7 @@ public function testSubmitQuoteAndCancelOrder() // Make sure coupon usages value is incremented then order is placed. $order = $this->quoteManagement->submit($quote); - - - sleep(15); // timeout to processing Magento queue + sleep(10); // timeout to processing Magento queue $this->usage->loadByCustomerCoupon($this->couponUsage, $customerId, $coupon->getId()); $coupon->loadByCode($couponCode); From 986ac9904fd303ddc6025caed33863a32e6e2242 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 2 Nov 2020 15:48:07 -0600 Subject: [PATCH 07/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Magento/SalesRule/Plugin/CouponUsagesTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php index cc23259dba58c..e7732f742b591 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php @@ -80,7 +80,7 @@ protected function setUp(): void [ 'consumers' => $this->consumers, 'logFilePath' => TESTS_TEMP_DIR . "/MessageQueueTestLog.txt", - 'maxMessages' => null, + 'maxMessages' => 100, 'appInitParams' => Bootstrap::getInstance()->getAppInitParams() ] ); @@ -117,10 +117,7 @@ public function testSubmitQuoteAndCancelOrder() $couponCode = 'one_usage'; $reservedOrderId = 'test01'; - $binDirectory = realpath(INTEGRATION_TESTS_DIR . '/bin/'); - $magentoCli = $binDirectory . '/magento'; - $consumerStartCommand = "php {$magentoCli} queue:consumers:start sales.rule.update.coupon.usage &"; - exec($consumerStartCommand); + $this->publisherConsumerController->startConsumers(); /** @var Coupon $coupon */ $coupon = $this->objectManager->get(Coupon::class); From e5918517b1e65e6036f108961986f2e38ff90efa Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 3 Nov 2020 06:35:29 -0600 Subject: [PATCH 08/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- app/code/Magento/Catalog/etc/di.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 149003104a9fd..8a116282e2578 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -1319,9 +1319,4 @@ - - - Magento\Catalog\CategoryImageUpload - - From 09655eb97e40757b46d6efa596294143bd0a4eea Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 3 Nov 2020 06:45:48 -0600 Subject: [PATCH 09/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php index e7732f742b591..eebf5ea638d05 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Plugin/CouponUsagesTest.php @@ -26,6 +26,7 @@ * @magentoAppArea frontend * @magentoDbIsolation enabled * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CouponUsagesTest extends TestCase { From 248fc8dcf1ee672141c31c1e2268abcf6d8727f5 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 3 Nov 2020 07:07:17 -0600 Subject: [PATCH 10/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...minCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 6 ++++++ .../SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 6ce9909d06be5..4c90e18b1195c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -20,6 +20,12 @@ + + + + + + diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml index 3dd87d94d0148..d54b9fada0f4b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml @@ -13,4 +13,8 @@ 1 0 + + sales.rule.update.coupon.usage + 100 + From fbad9a1e8d953f9424bea28492e77ebc663a7e8f Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 3 Nov 2020 10:26:59 -0600 Subject: [PATCH 11/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 4c90e18b1195c..8c64918696f1f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -22,8 +22,8 @@ - - + + From 79a82bb212b5b8e274b75d52c815844dbc12b417 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 5 Nov 2020 16:02:13 -0600 Subject: [PATCH 12/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml index d54b9fada0f4b..3dd87d94d0148 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleCouponData.xml @@ -13,8 +13,4 @@ 1 0 - - sales.rule.update.coupon.usage - 100 - From 895f88bdb193ab3aff733764527e6614a3240f93 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Thu, 5 Nov 2020 17:11:36 -0600 Subject: [PATCH 13/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...inCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 8c64918696f1f..f4814274306c2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -20,11 +20,8 @@ - - - - - + + From ef7d78ff7b4cda909c0fe7d8e9c35a2b609a2f60 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 6 Nov 2020 08:41:00 -0600 Subject: [PATCH 14/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index f4814274306c2..4ba5d7f53c03f 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -51,6 +51,9 @@ + + + From 2ffaa301fa910189c3a5bb9c073b7ee0794f9cb6 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 6 Nov 2020 11:21:16 -0600 Subject: [PATCH 15/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 4ba5d7f53c03f..691f18058b0df 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -72,6 +72,9 @@ + + + From e39d6e93b01a7b88dc7f13c4968f21fb6a714daf Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 6 Nov 2020 11:44:03 -0600 Subject: [PATCH 16/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...nCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 691f18058b0df..aad1c4d831a11 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -68,13 +68,15 @@ + + + + + - - - From 01ee9ea91b73ffe83452a4fe4fc2011163a3afdb Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Fri, 6 Nov 2020 15:25:41 -0600 Subject: [PATCH 17/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index aad1c4d831a11..b4cd75d9cc498 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -70,11 +70,11 @@ - + - + From 83e45ae5fd20f8aa167c970bf4b25eb0cde9df98 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 9 Nov 2020 13:52:49 -0600 Subject: [PATCH 18/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...nCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index b4cd75d9cc498..71ca3e8a79060 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -20,9 +20,6 @@ - - - @@ -50,10 +47,6 @@ - - - - @@ -70,7 +63,6 @@ - From 1143af933f5cf6ab4a2c49d583fb3cd0a1c46c02 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 9 Nov 2020 13:54:07 -0600 Subject: [PATCH 19/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index 71ca3e8a79060..dcb27595cf042 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -69,6 +69,8 @@ + + From a8c96887994a53e0503f49a64a689fcd76d69c7b Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 9 Nov 2020 17:08:04 -0600 Subject: [PATCH 20/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- ...elTheCreatedOrderWithZeroSubtotalCheckoutTest.xml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml index dcb27595cf042..21ced2e2df278 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -35,7 +35,7 @@ 10.00 - + @@ -62,14 +62,18 @@ - + + + + - + - + + From 396ab4de86c1fcd9952c691a29d5a01acd208e79 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 9 Nov 2020 17:08:22 -0600 Subject: [PATCH 21/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Test/Mftf/Data/SalesRuleQueueData.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml new file mode 100644 index 0000000000000..1e3cf57e8777b --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml @@ -0,0 +1,15 @@ + + + + + + sales.rule.update.coupon.usage + 100 + + From b77d2296a68b16fd8a102b1216de5803293486cd Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Mon, 9 Nov 2020 17:11:11 -0600 Subject: [PATCH 22/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 74542be376c45..84b32beceb06e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -58,11 +58,15 @@ - + + + + + Date: Mon, 16 Nov 2020 17:27:38 -0600 Subject: [PATCH 23/62] MC-38900: Mutation setGuestEmailOnCart doesn't update quote address --- app/code/Magento/Quote/Model/Quote/Address.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index aee86eb1f8935..5ecae97834eb0 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -139,7 +139,7 @@ class Address extends AbstractAddress implements const ADDRESS_TYPE_BILLING = 'billing'; const ADDRESS_TYPE_SHIPPING = 'shipping'; - + private const CACHED_ITEMS_ALL = 'cached_items_all'; /** @@ -1652,12 +1652,7 @@ public function setCustomerId($customerId) */ public function getEmail() { - $email = $this->getData(self::KEY_EMAIL); - if (!$email && $this->getQuote()) { - $email = $this->getQuote()->getCustomerEmail(); - $this->setEmail($email); - } - return $email; + return $this->getData(self::KEY_EMAIL); } /** From 88272b7f4750a17d2527dc4a77a4c36334154129 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 17 Nov 2020 09:38:42 -0600 Subject: [PATCH 24/62] MC-38900: Mutation setGuestEmailOnCart doesn't update quote address --- app/code/Magento/Quote/Model/Quote/Address.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 5ecae97834eb0..9da6c9b0b61a1 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -1652,7 +1652,13 @@ public function setCustomerId($customerId) */ public function getEmail() { - return $this->getData(self::KEY_EMAIL); + $email = $this->getData(self::KEY_EMAIL); + $q = $this->getQuote(); + if (!$email && $this->getQuote() && $this->getQuote()->dataHasChangedFor('email')) { + $email = $this->getQuote()->getCustomerEmail(); + $this->setEmail($email); + } + return $email; } /** From cb5b1b86ed5aa6918639868f4622de3d284e4079 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 17 Nov 2020 13:35:19 -0600 Subject: [PATCH 25/62] MC-38900: Mutation setGuestEmailOnCart doesn't update quote address --- app/code/Magento/Quote/Model/Quote/Address.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 9da6c9b0b61a1..0edf603d131f8 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -1653,8 +1653,7 @@ public function setCustomerId($customerId) public function getEmail() { $email = $this->getData(self::KEY_EMAIL); - $q = $this->getQuote(); - if (!$email && $this->getQuote() && $this->getQuote()->dataHasChangedFor('email')) { + if (!$email && $this->getQuote() && $this->getQuote()->dataHasChangedFor('customer_email')) { $email = $this->getQuote()->getCustomerEmail(); $this->setEmail($email); } From 17b76e616ef353802acbb7b0b17101879e21b4f7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov Date: Tue, 17 Nov 2020 13:42:25 -0600 Subject: [PATCH 26/62] MC-38900: Mutation setGuestEmailOnCart doesn't update quote address --- app/code/Magento/Quote/Model/Quote/Address.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 0edf603d131f8..f918d480dd4e0 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -1653,7 +1653,7 @@ public function setCustomerId($customerId) public function getEmail() { $email = $this->getData(self::KEY_EMAIL); - if (!$email && $this->getQuote() && $this->getQuote()->dataHasChangedFor('customer_email')) { + if ($this->getQuote() && (!$email || $this->getQuote()->dataHasChangedFor('customer_email'))) { $email = $this->getQuote()->getCustomerEmail(); $this->setEmail($email); } From 20ce0894bd18e9b1375831ad9f8e2387f7e1f319 Mon Sep 17 00:00:00 2001 From: Victor Rad Date: Tue, 17 Nov 2020 14:26:06 -0600 Subject: [PATCH 27/62] MC-39024: Rest api PUT /V1/products/:sku/links calls does not update indexer by cron:run --- app/code/Magento/Catalog/etc/mview.xml | 2 + .../Magento/CatalogInventory/etc/mview.xml | 1 + app/code/Magento/CatalogRule/etc/mview.xml | 1 + .../Api/ProductLinkRepositoryTest.php | 122 +++++++++++++++++- 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/etc/mview.xml b/app/code/Magento/Catalog/etc/mview.xml index 7ae38a7f2d0e1..2c9d7d448afd2 100644 --- a/app/code/Magento/Catalog/etc/mview.xml +++ b/app/code/Magento/Catalog/etc/mview.xml @@ -49,6 +49,7 @@
+
@@ -56,6 +57,7 @@
+
diff --git a/app/code/Magento/CatalogInventory/etc/mview.xml b/app/code/Magento/CatalogInventory/etc/mview.xml index 338f1fe0610a1..9733fa32583f1 100644 --- a/app/code/Magento/CatalogInventory/etc/mview.xml +++ b/app/code/Magento/CatalogInventory/etc/mview.xml @@ -11,6 +11,7 @@
+
diff --git a/app/code/Magento/CatalogRule/etc/mview.xml b/app/code/Magento/CatalogRule/etc/mview.xml index 9f793d5c8c393..106e0ffabb2b2 100644 --- a/app/code/Magento/CatalogRule/etc/mview.xml +++ b/app/code/Magento/CatalogRule/etc/mview.xml @@ -21,6 +21,7 @@
+
diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php index 11e07d081636e..efa7341c36a40 100644 --- a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductLinkRepositoryTest.php @@ -7,28 +7,44 @@ namespace Magento\GroupedProduct\Api; use Magento\TestFramework\Helper\Bootstrap; +use Magento\Indexer\Model\Config; +use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Framework\Webapi\Rest\Request; class ProductLinkRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstract { const SERVICE_NAME = 'catalogProductLinkRepositoryV1'; const SERVICE_VERSION = 'V1'; const RESOURCE_PATH = '/V1/products/'; + const SERVICE_NAME_SEARCH = 'searchV1'; + const RESOURCE_PATH_SEARCH = '/V1/search/'; /** * @var \Magento\Framework\ObjectManagerInterface */ protected $objectManager; + /** + * @var array + */ + private $indexersState; + + /** + * @var mixed + */ + private $indexerRegistry; + protected function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); + $this->indexerRegistry = $this->objectManager->get(IndexerRegistry::class); } /** * @magentoApiDataFixture Magento/Catalog/_files/product_simple_duplicated.php * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped.php */ - public function testSave() + public function testSave(): void { $productSku = 'grouped-product'; $linkType = 'associated'; @@ -46,7 +62,7 @@ public function testSave() $serviceInfo = [ 'rest' => [ 'resourcePath' => self::RESOURCE_PATH . $productSku . '/links', - 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT, + 'httpMethod' => Request::HTTP_METHOD_PUT, ], 'soap' => [ 'service' => self::SERVICE_NAME, @@ -64,4 +80,106 @@ public function testSave() }); $this->assertEquals($productData, $actual[2]); } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * @magentoApiDataFixture Magento/GroupedProduct/_files/product_grouped.php + */ + public function testLinkWithScheduledIndex(): void + { + $this->setIndexScheduled(); + $productSkuGrouped = 'grouped-product'; + $productSimple = 'simple-1'; + $linkType = 'associated'; + $productData = [ + 'sku' => $productSkuGrouped, + 'link_type' => $linkType, + 'linked_product_type' => 'simple', + 'linked_product_sku' => $productSimple, + 'position' => 3, + 'extension_attributes' => [ + 'qty' => (float) 300.0000, + ], + ]; + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $productSkuGrouped . '/links', + 'httpMethod' => Request::HTTP_METHOD_PUT, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'Save', + ], + ]; + $this->_webApiCall($serviceInfo, ['entity' => $productData]); + + $searchCriteria = $this->buildSearchCriteria($productSimple); + $serviceInfo = $this->buildSearchServiceInfo($searchCriteria); + $response = $this->_webApiCall($serviceInfo, $searchCriteria); + $this->assertArrayHasKey('search_criteria', $response); + $this->assertArrayHasKey('items', $response); + $this->assertGreaterThan(1, count($response['items'])); + $this->assertGreaterThan(0, $response['items'][0]['id']); + $this->restoreIndexMode(); + } + + /** + * @param string $productSku + * @return array + */ + private function buildSearchCriteria(string $productSku): array + { + return [ + 'searchCriteria' => [ + 'request_name' => 'quick_search_container', + 'filter_groups' => [ + [ + 'filters' => [ + [ + 'field' => 'search_term', + 'value' => $productSku, + ] + ] + ] + ] + ] + ]; + } + + /** + * @param array $searchCriteria + * @return array + */ + private function buildSearchServiceInfo(array $searchCriteria): array + { + return [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH_SEARCH . '?' . http_build_query($searchCriteria), + 'httpMethod' => Request::HTTP_METHOD_GET + ], + 'soap' => [ + 'service' => self::SERVICE_NAME_SEARCH, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME_SEARCH . 'Search' + ] + ]; + } + + private function setIndexScheduled(): void + { + $indexerListIds = $this->objectManager->get(Config::class)->getIndexers(); + foreach ($indexerListIds as $indexerId) { + $indexer = $this->indexerRegistry->get($indexerId['indexer_id']); + $this->indexersState[$indexerId['indexer_id']] = $indexer->isScheduled(); + $indexer->setScheduled(true); + } + } + + private function restoreIndexMode(): void + { + foreach ($this->indexersState as $indexerId => $state) { + $this->indexerRegistry->get($indexerId)->setScheduled($state); + } + } } From 5aac129b18a8d53fb5a4907029d4fc8fb1ce54f5 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Wed, 18 Nov 2020 09:03:06 -0600 Subject: [PATCH 28/62] MC-38787: Admin Product Grid Page indicator issue --- app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index 534d292809ed1..761fcb6864fe1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -36,7 +36,8 @@ define([ imports: { totalSelected: '${ $.selectProvider }:totalSelected', totalRecords: '${ $.provider }:data.totalRecords', - filters: '${ $.provider }:params.filters' + filters: '${ $.provider }:params.filters', + search: '${ $.provider }:params.search' }, exports: { @@ -58,7 +59,8 @@ define([ 'pages': 'onPagesChange', 'pageSize': 'onPageSizeChange', 'totalRecords': 'updateCounter', - '${ $.provider }:params.filters': 'goFirst' + '${ $.provider }:params.filters': 'goFirst', + 'search': 'goFirst' }, modules: { From 9d6eb3c3167df12f56bd791c2429ed04eba6f9b4 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Wed, 18 Nov 2020 14:15:29 -0600 Subject: [PATCH 29/62] MC-38787: Admin Product Grid Page indicator issue --- app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index 761fcb6864fe1..0f5a9289b71ff 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -186,7 +186,7 @@ define([ * @returns {Paging} Chainable. */ goFirst: function () { - if (!_.isUndefined(this.filters)) { + if (!_.isUndefined(this.filters) || !_.isEmpty(this.search)) { this.current = 1; } From f756a57f02788a2d25cd6e653c8ec3c27b826904 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Wed, 18 Nov 2020 16:59:57 -0600 Subject: [PATCH 30/62] MC-38787: Admin Product Grid Page indicator issue --- app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index 0f5a9289b71ff..761fcb6864fe1 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -186,7 +186,7 @@ define([ * @returns {Paging} Chainable. */ goFirst: function () { - if (!_.isUndefined(this.filters) || !_.isEmpty(this.search)) { + if (!_.isUndefined(this.filters)) { this.current = 1; } From b02cc2ba14c3b26c764052a4b76a01f35fc635e8 Mon Sep 17 00:00:00 2001 From: Buba Suma Date: Wed, 18 Nov 2020 16:40:23 -0600 Subject: [PATCH 31/62] MC-39134: Custom options date is not populated when editing reorder items - Fix custom date option is not populated when editing quote item after enabling javascript calendar --- .../Block/Product/View/Options/Type/Date.php | 90 ++++- .../Product/View/Options/Type/DateTest.php | 324 ++++++++++++++++++ 2 files changed, 406 insertions(+), 8 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Date.php b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Date.php index 3a9d81eed4221..af921959f8e27 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/Type/Date.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/Type/Date.php @@ -5,6 +5,11 @@ */ namespace Magento\Catalog\Block\Product\View\Options\Type; +use DateTimeZone; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Form\FilterFactory; +use Magento\Framework\Stdlib\DateTime; + /** * Product options text type block * @@ -27,22 +32,30 @@ class Date extends \Magento\Catalog\Block\Product\View\Options\AbstractOptions */ protected $_catalogProductOptionTypeDate; + /** + * @var FilterFactory + */ + private $filterFactory; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Framework\Pricing\Helper\Data $pricingHelper * @param \Magento\Catalog\Helper\Data $catalogData * @param \Magento\Catalog\Model\Product\Option\Type\Date $catalogProductOptionTypeDate * @param array $data + * @param FilterFactory|null $filterFactory */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Pricing\Helper\Data $pricingHelper, \Magento\Catalog\Helper\Data $catalogData, \Magento\Catalog\Model\Product\Option\Type\Date $catalogProductOptionTypeDate, - array $data = [] + array $data = [], + ?FilterFactory $filterFactory = null ) { $this->_catalogProductOptionTypeDate = $catalogProductOptionTypeDate; parent::__construct($context, $pricingHelper, $catalogData, $data); + $this->filterFactory = $filterFactory ?? ObjectManager::getInstance()->get(FilterFactory::class); } /** @@ -77,14 +90,24 @@ public function getDateHtml() public function getCalendarDateHtml() { $option = $this->getOption(); - $value = $this->getProduct()->getPreconfiguredValues()->getData('options/' . $option->getId() . '/date'); + $values = $this->getProduct()->getPreconfiguredValues()->getData('options/' . $option->getId()); $yearStart = $this->_catalogProductOptionTypeDate->getYearStart(); $yearEnd = $this->_catalogProductOptionTypeDate->getYearEnd(); - $dateFormat = $this->_localeDate->getDateFormat(\IntlDateFormatter::SHORT); + $dateFormat = $this->_localeDate->getDateFormatWithLongYear(); /** Escape RTL characters which are present in some locales and corrupt formatting */ $escapedDateFormat = preg_replace('/[^MmDdYy\/\.\-]/', '', $dateFormat); + $value = null; + if (is_array($values)) { + $date = $this->getInternalDateString($values); + if ($date !== null) { + $dateFilter = $this->filterFactory->create('date', ['format' => $escapedDateFormat]); + $value = $dateFilter->outputFilter($date); + } elseif (isset($values['date'])) { + $value = $values['date']; + } + } $calendar = $this->getLayout()->createBlock( \Magento\Framework\View\Element\Html\Date::class )->setId( @@ -158,8 +181,8 @@ public function getTimeHtml() * Return drop-down html with range of values * * @param string $name Id/name of html select element - * @param int $from Start position - * @param int $to End position + * @param int $from Start position + * @param int $to End position * @param int|null $value Value selected * @return string Formatted Html */ @@ -209,9 +232,8 @@ protected function _getHtmlSelect($name, $value = null) $select->setExtraParams($extraParams); if ($value === null) { - $value = $this->getProduct()->getPreconfiguredValues()->getData( - 'options/' . $option->getId() . '/' . $name - ); + $values = $this->getProduct()->getPreconfiguredValues()->getData('options/' . $option->getId()); + $value = is_array($values) ? $this->parseDate($values, $name) : null; } if ($value !== null) { $select->setValue($value); @@ -233,4 +255,56 @@ protected function _getValueWithLeadingZeros($value) } return $value < 10 ? '0' . $value : $value; } + + /** + * Get internal date format of provided value + * + * @param array $value + * @return string|null + */ + private function getInternalDateString(array $value): ?string + { + $result = null; + if (!empty($value['date']) && !empty($value['date_internal'])) { + $dateTimeZone = new DateTimeZone($this->_localeDate->getConfigTimezone()); + $dateTimeObject = date_create_from_format( + DateTime::DATETIME_PHP_FORMAT, + $value['date_internal'], + $dateTimeZone + ); + if ($dateTimeObject !== false) { + $result = $dateTimeObject->format(DateTime::DATE_PHP_FORMAT); + } + } elseif (!empty($value['day']) && !empty($value['month']) && !empty($value['year'])) { + $dateTimeObject = $this->_localeDate->date(); + $dateTimeObject->setDate((int) $value['year'], (int) $value['month'], (int) $value['day']); + $result = $dateTimeObject->format(DateTime::DATE_PHP_FORMAT); + } + return $result; + } + + /** + * Parse option value and return the requested part + * + * @param array $value + * @param string $part [year, month, day, hour, minute, day_part] + * @return string|null + */ + private function parseDate(array $value, string $part): ?string + { + $result = null; + if (!empty($value['date']) && !empty($value['date_internal'])) { + $formatDate = explode(' ', $value['date_internal']); + $date = explode('-', $formatDate[0]); + $value['year'] = $date[0]; + $value['month'] = $date[1]; + $value['day'] = $date[2]; + } + + if (isset($value[$part])) { + $result = (string) $value[$part]; + } + + return $result; + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php new file mode 100644 index 0000000000000..91a54d8fc13fa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/Type/DateTest.php @@ -0,0 +1,324 @@ +productRepository = $objectManager->get(ProductRepositoryInterface::class); + $this->productHelper = $objectManager->get(ProductHelper::class); + $this->dataObjectFactory = $objectManager->get(DataObjectFactory::class); + $layout = $objectManager->get(LayoutInterface::class); + $this->localeResolver = $objectManager->get(ResolverInterface::class); + $this->defaultLocale = $this->localeResolver->getLocale(); + $this->block = $layout->createBlock( + Date::class, + 'product.info.options.date', + [ + 'data' => [ + 'template' => 'Magento_Catalog::product/view/options/type/date.phtml' + ] + ] + ); + $layout->createBlock( + Render::class, + 'product.price.render.default', + [ + 'data' => [ + 'price_render_handle' => 'catalog_product_prices', + 'use_link_for_as_low_as' => true, + ], + ] + ); + } + + /** + * @inheritDoc + */ + protected function tearDown(): void + { + $this->localeResolver->setLocale($this->defaultLocale); + parent::tearDown(); + } + + /** + * @magentoAppArea frontend + * @param array $data + * @param array $expected + * @dataProvider toHtmlWithDropDownDataProvider + */ + public function testToHtmlWithDropDown(array $data, array $expected): void + { + $this->prepareBlock($data); + $this->assertXPaths($expected); + } + + /** + * @magentoAppArea frontend + * @magentoConfigFixture current_store catalog/custom_options/use_calendar 1 + * @param array $data + * @param array $expected + * @param string|null $locale + * @dataProvider toHtmlWithCalendarDataProvider + */ + public function testToHtmlWithCalendar(array $data, array $expected, ?string $locale = null): void + { + if ($locale) { + $this->localeResolver->setLocale($locale); + } + $this->prepareBlock($data); + $this->assertXPaths($expected); + } + + /** + * @param array $expected + */ + private function assertXPaths(array $expected): void + { + $html = $this->block->toHtml(); + $domXpath = new \DOMXPath($this->getHtmlDocument($html)); + foreach ($expected as $xpath => $value) { + $xpath = strtr($xpath, ['{id}' => $this->block->getOption()->getId()]); + $nodes = $domXpath->query(strtr($xpath, ['{id}' => $this->block->getOption()->getId()])); + $this->assertEquals(1, $nodes->count(), 'Cannot find element \'' . $xpath . '"\' in the HTML'); + $this->assertEquals($value, $nodes->item(0)->getAttribute('value')); + } + } + + /** + * @param array $data + */ + private function prepareBlock(array $data): void + { + /** @var Product $product */ + $product = $this->productRepository->get('simple'); + $this->block->setProduct($product); + $option = $this->getDateTimeOption($product); + $this->block->setOption($option); + $buyRequest = $this->dataObjectFactory->create(); + $buyRequest->setData( + [ + 'qty' => 1, + 'options' => [ + $option->getId() => $data + ], + ] + ); + $this->productHelper->prepareProductOptions($product, $buyRequest); + } + + /** + * @param Product $product + * @return Option|null + */ + private function getDateTimeOption(Product $product): ?Option + { + $option = null; + foreach ($product->getOptions() as $customOption) { + if ($customOption->getType() === Option::OPTION_TYPE_DATE_TIME) { + $option = $customOption; + break; + } + } + return $option; + } + + /** + * @param string $source + * @return \DOMDocument + */ + private function getHtmlDocument(string $source): \DOMDocument + { + $page =<< + + + + Title + + +$source + + +HTML; + $domDocument = new \DOMDocument('1.0', 'UTF-8'); + libxml_use_internal_errors(true); + $domDocument->loadHTML($page); + libxml_use_internal_errors(false); + return $domDocument; + } + + /** + * @return array + */ + public function toHtmlWithDropDownDataProvider(): array + { + return [ + [ + [ + 'month' => '3', + 'day' => '5', + 'year' => '2020', + 'hour' => '2', + 'minute' => '15', + 'day_part' => 'am', + 'date_internal' => '2020-09-30 02:15:00' + ], + [ + '//select[@id="options_{id}_year"]/option[@selected]' => '2020', + '//select[@id="options_{id}_month"]/option[@selected]' => '3', + '//select[@id="options_{id}_day"]/option[@selected]' => '5', + '//select[@id="options_{id}_hour"]/option[@selected]' => '2', + '//select[@id="options_{id}_minute"]/option[@selected]' => '15', + '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am', + ] + ], + [ + [ + 'date' => '09/30/2022', + 'hour' => '2', + 'minute' => '15', + 'day_part' => 'am', + 'date_internal' => '2020-09-30 02:15:00' + ], + [ + '//select[@id="options_{id}_year"]/option[@selected]' => '2020', + '//select[@id="options_{id}_month"]/option[@selected]' => '9', + '//select[@id="options_{id}_day"]/option[@selected]' => '30', + '//select[@id="options_{id}_hour"]/option[@selected]' => '2', + '//select[@id="options_{id}_minute"]/option[@selected]' => '15', + '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am', + ] + ] + ]; + } + + /** + * @return array + */ + public function toHtmlWithCalendarDataProvider(): array + { + return [ + [ + [ + 'month' => '3', + 'day' => '5', + 'year' => '2020', + 'hour' => '2', + 'minute' => '15', + 'day_part' => 'am', + 'date_internal' => '2020-09-30 02:15:00' + ], + [ + '//input[@id="options_{id}_date"]' => '3/5/2020', + '//select[@id="options_{id}_hour"]/option[@selected]' => '2', + '//select[@id="options_{id}_minute"]/option[@selected]' => '15', + '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am', + ] + ], + [ + [ + 'date' => '09/30/2022', + 'hour' => '2', + 'minute' => '15', + 'day_part' => 'am', + 'date_internal' => '2020-09-30 02:15:00' + ], + [ + '//input[@id="options_{id}_date"]' => '9/30/2020', + '//select[@id="options_{id}_hour"]/option[@selected]' => '2', + '//select[@id="options_{id}_minute"]/option[@selected]' => '15', + '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am', + ] + ], + [ + [ + 'month' => '3', + 'day' => '5', + 'year' => '2020', + 'hour' => '2', + 'minute' => '15', + 'day_part' => 'am', + 'date_internal' => '2020-09-30 02:15:00' + ], + [ + '//input[@id="options_{id}_date"]' => '05/03/2020', + '//select[@id="options_{id}_hour"]/option[@selected]' => '2', + '//select[@id="options_{id}_minute"]/option[@selected]' => '15', + '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am', + ], + 'fr_FR' + ], + [ + [ + 'date' => '09/30/2022', + 'hour' => '2', + 'minute' => '15', + 'day_part' => 'am', + 'date_internal' => '2020-09-30 02:15:00' + ], + [ + '//input[@id="options_{id}_date"]' => '30/09/2020', + '//select[@id="options_{id}_hour"]/option[@selected]' => '2', + '//select[@id="options_{id}_minute"]/option[@selected]' => '15', + '//select[@id="options_{id}_day_part"]/option[@selected]' => 'am', + ], + 'fr_FR' + ] + ]; + } +} From 0c3561cfb197b80982a8cbf80bfbd268a704fe98 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Thu, 19 Nov 2020 15:38:52 -0600 Subject: [PATCH 32/62] MC-38787: Admin Product Grid Page indicator issue --- .../Magento/Ui/view/base/web/js/grid/paging/paging.js | 6 +++--- .../Magento/Ui/view/base/web/js/grid/search/search.js | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index 761fcb6864fe1..6b6636fac5668 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -37,7 +37,7 @@ define([ totalSelected: '${ $.selectProvider }:totalSelected', totalRecords: '${ $.provider }:data.totalRecords', filters: '${ $.provider }:params.filters', - search: '${ $.provider }:params.search' + keywordUpdated: '${ $.provider }:params.keywordUpdated' }, exports: { @@ -60,7 +60,7 @@ define([ 'pageSize': 'onPageSizeChange', 'totalRecords': 'updateCounter', '${ $.provider }:params.filters': 'goFirst', - 'search': 'goFirst' + 'keywordUpdated': 'goFirst' }, modules: { @@ -186,7 +186,7 @@ define([ * @returns {Paging} Chainable. */ goFirst: function () { - if (!_.isUndefined(this.filters)) { + if ((!_.isUndefined(this.filters) && _.keys(this.filters) > 1) || this.keywordUpdated) { this.current = 1; } diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js index 3f5434761ba18..307f3606a2e3f 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js @@ -22,6 +22,7 @@ define([ placeholder: $t('Search by keyword'), label: $t('Keyword'), value: '', + keywordUpdated: false, previews: [], chipsProvider: 'componentType = filtersChips, ns = ${ $.ns }', statefull: { @@ -31,7 +32,8 @@ define([ value: true, previews: true, inputValue: true, - focused: true + focused: true, + keywordUpdated: true }, imports: { inputValue: 'value', @@ -39,7 +41,8 @@ define([ focused: false }, exports: { - value: '${ $.provider }:params.search' + value: '${ $.provider }:params.search', + keywordUpdated: '${ $.provider }:params.keywordUpdated' }, modules: { chips: '${ $.chipsProvider }' @@ -124,6 +127,7 @@ define([ apply: function (value) { value = value || this.inputValue; + this.keywordUpdated = this.value !== this.inputValue; this.value = this.inputValue = value.trim(); return this; From f1f4a861b9b516637de3b1910101a7ba406f8ea7 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Thu, 19 Nov 2020 15:44:45 -0600 Subject: [PATCH 33/62] MC-38787: Admin Product Grid Page indicator issue --- app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index 6b6636fac5668..a411a7c1a6837 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -60,7 +60,7 @@ define([ 'pageSize': 'onPageSizeChange', 'totalRecords': 'updateCounter', '${ $.provider }:params.filters': 'goFirst', - 'keywordUpdated': 'goFirst' + '${ $.provider }:params.search': 'goFirst' }, modules: { From 36640fd5f43c0b7bc3fc7312ff491432744148b8 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Thu, 19 Nov 2020 15:54:26 -0600 Subject: [PATCH 34/62] MC-38787: Admin Product Grid Page indicator issue --- app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index a411a7c1a6837..9d4461d0bdc64 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -186,7 +186,10 @@ define([ * @returns {Paging} Chainable. */ goFirst: function () { - if ((!_.isUndefined(this.filters) && _.keys(this.filters) > 1) || this.keywordUpdated) { + if ( + (!_.isUndefined(this.filters) && _.keys(this.filters) > 1) || + (!_.isUndefined(this.keywordUpdated) && this.keywordUpdated) + ) { this.current = 1; } From b5b79b71cb252a91779cc3e2f78c6cf89e3f5a13 Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Thu, 19 Nov 2020 16:21:29 -0600 Subject: [PATCH 35/62] MC-38787: Admin Product Grid Page indicator issue --- .../Ui/view/base/web/js/grid/paging/paging.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js index 9d4461d0bdc64..5f6c21cf6167f 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/paging/paging.js @@ -60,7 +60,7 @@ define([ 'pageSize': 'onPageSizeChange', 'totalRecords': 'updateCounter', '${ $.provider }:params.filters': 'goFirst', - '${ $.provider }:params.search': 'goFirst' + '${ $.provider }:params.search': 'onSearchUpdate' }, modules: { @@ -186,10 +186,7 @@ define([ * @returns {Paging} Chainable. */ goFirst: function () { - if ( - (!_.isUndefined(this.filters) && _.keys(this.filters) > 1) || - (!_.isUndefined(this.keywordUpdated) && this.keywordUpdated) - ) { + if (!_.isUndefined(this.filters)) { this.current = 1; } @@ -287,6 +284,17 @@ define([ */ onPagesChange: function () { this.updateCursor(); + }, + + /** + * Resent the pagination to Page 1 on search keyword update + */ + onSearchUpdate: function () { + if (!_.isUndefined(this.keywordUpdated) && this.keywordUpdated) { + this.goFirst(); + } + + return this; } }); }); From ed41a6200b3517b49e9b64d5c44dc096294796bc Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Fri, 20 Nov 2020 12:25:59 -0600 Subject: [PATCH 36/62] MC-38787: Admin Product Grid Page indicator issue --- .../Magento/Ui/view/base/web/js/grid/search/search.js | 2 +- .../code/Magento/Ui/base/js/grid/search/search.test.js | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js index 307f3606a2e3f..d4db0100db7c6 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/search/search.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/search/search.js @@ -127,7 +127,7 @@ define([ apply: function (value) { value = value || this.inputValue; - this.keywordUpdated = this.value !== this.inputValue; + this.keywordUpdated = this.value !== value; this.value = this.inputValue = value.trim(); return this; diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js index 11af4cdc219a9..87fb61873b653 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/search/search.test.js @@ -37,5 +37,15 @@ define([ searchObj.updatePreview(); expect(searchObj.updatePreview).toHaveBeenCalled(); }); + it('set the proper keywordUpdated value on new search keyword', function () { + searchObj.value = 'keyword 1'; + expect(searchObj.keywordUpdated).toEqual(false); + searchObj.apply('keyword 2'); + expect(searchObj.keywordUpdated).toEqual(true); + searchObj.apply('keyword 2'); + expect(searchObj.keywordUpdated).toEqual(false); + searchObj.apply('keyword 3'); + expect(searchObj.keywordUpdated).toEqual(true); + }); }); }); From 72d4cd76ada26777ae3733d46c3e46d1ec482cac Mon Sep 17 00:00:00 2001 From: Roman Flowers Date: Fri, 20 Nov 2020 16:27:41 -0600 Subject: [PATCH 37/62] MC-38787: Admin Product Grid Page indicator issue --- ...hProductGridByStringNoClearActionGroup.xml | 23 +++++ ...dPageNumberSetsToOneAfterNewSearchTest.xml | 95 +++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml create mode 100644 app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml new file mode 100644 index 0000000000000..a763f37ed153f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml @@ -0,0 +1,23 @@ + + + + + + + Searches the Admin Products grid by string without clearing filters. + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml new file mode 100644 index 0000000000000..f2dd87e74e9de --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + <description value="Checking Catalog grid page number after entering a new search keyword"/> + <severity value="MINOR"/> + <testCaseId value="MC-xxxxx"/> + <useCaseId value="MC-38787"/> + <group value="Catalog"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <comment userInput="Clear product grid" stepKey="commentClearProductGrid"/> + <actionGroup ref="AdminProductCatalogPageOpenActionGroup" stepKey="goToProductCatalog"/> + <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> + <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteProductIfTheyExist"/> + <createData stepKey="category1" entity="SimpleSubCategory"/> + + <createData stepKey="simpleProduct1" entity="SimpleProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="simpleProduct2" entity="SimpleProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="simpleProduct3" entity="SimpleProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="simpleProduct4" entity="SimpleProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="virtualProduct1" entity="VirtualProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="virtualProduct2" entity="VirtualProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + <createData stepKey="virtualProduct3" entity="VirtualProduct"> + <requiredEntity createDataKey="category1"/> + </createData> + </before> + + <after> + <actionGroup ref="AdminProductCatalogPageOpenActionGroup" stepKey="goToProductCatalog"/> + <actionGroup ref="AdminDataGridDeleteCustomPerPageActionGroup" stepKey="deleteCustomAddedPerPage"> + <argument name="perPage" value="ProductPerPage.productCount"/> + </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/> + + <deleteData stepKey="deleteCategory1" createDataKey="category1"/> + + <deleteData stepKey="deleteSimpleProduct1" createDataKey="simpleProduct1"/> + <deleteData stepKey="deleteSimpleProduct2" createDataKey="simpleProduct2"/> + <deleteData stepKey="deleteSimpleProduct3" createDataKey="simpleProduct3"/> + <deleteData stepKey="deleteSimpleProduct4" createDataKey="simpleProduct4"/> + <deleteData stepKey="deleteVirtualProduct1" createDataKey="virtualProduct1"/> + <deleteData stepKey="deleteVirtualProduct2" createDataKey="virtualProduct2"/> + <deleteData stepKey="deleteVirtualProduct3" createDataKey="virtualProduct3"/> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="AdminProductCatalogPageOpenActionGroup" stepKey="goToProductCatalog"/> + + <actionGroup ref="AdminDataGridSelectCustomPerPageActionGroup" stepKey="select1ProductPerPage"> + <argument name="perPage" value="ProductPerPage.productCount"/> + </actionGroup> + + <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForSimpleProduct"> + <argument name="keyword" value="SimpleProduct"/> + </actionGroup> + + <comment userInput="Go to the next page" stepKey="nextPage"/> + <click selector="{{AdminDataGridPaginationSection.nextPage}}" stepKey="clickNextPageProductGrid"/> + <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageProductGrid"/> + + <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForVirtualProduct"> + <argument name="keyword" value="VirtualProduct"/> + </actionGroup> + + <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="1" stepKey="seeOnFirstPageProductGrid"/> + </test> +</tests> From fd8749f6ecea40dcd759b1bf76cb68695737fadf Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Fri, 20 Nov 2020 18:19:06 -0600 Subject: [PATCH 38/62] MC-38770: Exception during initialization is cacheable - fixed --- lib/internal/Magento/Framework/App/Bootstrap.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index 93d5535d0e10e..92b925836b295 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -386,7 +386,7 @@ private function initErrorHandler() $handler = new ErrorHandler(); set_error_handler([$handler, 'handler']); } - + /** * Getter for error code * @@ -428,9 +428,13 @@ public function isDeveloperMode() */ protected function terminate(\Throwable $e) { - + /** @var \Magento\Framework\HTTP\PhpEnvironment\Response $response */ + $response = $this->objectManager->get(\Magento\Framework\HTTP\PhpEnvironment\Response::class); + $response->clearHeaders(); + $response->setHttpResponseCode(500); + $response->setHeader('Content-Type', 'text/plain'); if ($this->isDeveloperMode()) { - echo $e; + $response->setBody($e); } else { $message = "An error has happened during application run. See exception log for details.\n"; try { @@ -441,8 +445,9 @@ protected function terminate(\Throwable $e) } catch (\Exception $e) { $message .= "Could not write error message to log. Please use developer mode to see the message.\n"; } - echo $message; + $response->setBody($message); } + $response->sendResponse(); exit(1); } // phpcs:enable From 719c206661d00fd526f43041b89d3ddd041154da Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Tue, 24 Nov 2020 11:05:16 -0600 Subject: [PATCH 39/62] MC-38787: Admin Product Grid Page indicator issue --- ...inGridPageNumberSetsToOneAfterNewSearchTest.xml | 14 ++++++++------ .../Section/AdminDataGridPaginationSection.xml | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml index f2dd87e74e9de..0cc57ba94dac6 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml @@ -13,10 +13,10 @@ <annotations> <features value="Catalog"/> <stories value="Catalog grid"/> - <title value="Checking Catalog grid page number after entering a new search keyword"/> - <description value="Checking Catalog grid page number after entering a new search keyword"/> - <severity value="MINOR"/> - <testCaseId value="MC-xxxxx"/> + <title value="Updating the search keyword in admin product grid should reset current page to the first one"/> + <description value="When changing the search keyword in admin product grid, new results should be displayed from the page one"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-39332"/> <useCaseId value="MC-38787"/> <group value="Catalog"/> </annotations> @@ -82,14 +82,16 @@ <argument name="keyword" value="SimpleProduct"/> </actionGroup> + <waitForElementVisible selector="{{AdminDataGridPaginationSection.totalPagesCount('4')}}" stepKey="seeTotalPagesIsFourOnFirstSearch"/> <comment userInput="Go to the next page" stepKey="nextPage"/> <click selector="{{AdminDataGridPaginationSection.nextPage}}" stepKey="clickNextPageProductGrid"/> - <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageProductGrid"/> + <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageProductGridOnFirstSearch"/> <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForVirtualProduct"> <argument name="keyword" value="VirtualProduct"/> </actionGroup> - <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="1" stepKey="seeOnFirstPageProductGrid"/> + <waitForElementVisible selector="{{AdminDataGridPaginationSection.totalPagesCount('3')}}" stepKey="seeTotalPagesIsThreeOnSecondSearch"/> + <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="1" stepKey="seeOnFirstPageProductGridOnSecondSearch"/> </test> </tests> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml index 51cebdb01a74d..eaea88fdddb03 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridPaginationSection.xml @@ -20,6 +20,7 @@ <element name="previousPage" type="button" selector="div.admin__data-grid-pager > button.action-previous" timeout="30"/> <element name="currentPage" type="input" selector="div.admin__data-grid-pager > input[data-ui-id='current-page-input']"/> <element name="totalPages" type="text" selector="div.admin__data-grid-pager > label"/> + <element name="totalPagesCount" type="text" selector="//div[@class='admin__data-grid-pager']//label[@class='admin__control-support-text' and .='of {{arg1}}']" parameterized="true"/> <element name="perPageDropDownValue" type="input" selector=".selectmenu-value input" timeout="30"/> <element name="selectedPage" type="input" selector="#sales_order_create_search_grid_page-current" timeout="30"/> <element name="nextPageActive" type="button" selector="div.admin__data-grid-pager > button.action-next:not(.disabled)" timeout="30"/> From 81d13022b4aa4b7d5c07e26be54e412563bb02d6 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Mon, 23 Nov 2020 10:55:25 -0600 Subject: [PATCH 40/62] MC-38951: Images positions are inconsistent across store-views if images were added in a store-view level - Fix images positions for default scope if image were added in store view level --- .../Product/Helper/Form/Gallery/Content.php | 11 +- .../Model/Product/Gallery/ReadHandler.php | 32 ++++- .../Helper/Form/Gallery/ContentTest.php | 130 ++++++++++++++++++ .../Block/Product/View/GalleryTest.php | 102 ++++++++++++++ 4 files changed, 270 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index 57cea59bee207..b06edc43cd71d 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -209,7 +209,14 @@ public function getImagesJson() */ private function sortImagesByPosition($images) { - if (is_array($images)) { + $nullPositions = []; + foreach ($images as $index => $image) { + if ($image['position'] === null) { + $nullPositions[] = $image; + unset($images[$index]); + } + } + if (is_array($images) && !empty($images)) { usort( $images, function ($imageA, $imageB) { @@ -217,7 +224,7 @@ function ($imageA, $imageB) { } ); } - return $images; + return array_merge($images, $nullPositions); } /** diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php index a3726207b3024..ed2e09249e495 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/ReadHandler.php @@ -64,9 +64,9 @@ public function execute($entity, $arguments = []) $this->addMediaDataToProduct( $entity, - $mediaEntries + $this->sortMediaEntriesByPosition($mediaEntries) ); - + return $entity; } @@ -108,7 +108,7 @@ public function getAttribute() * Find default value * * @param string $key - * @param string[] &$image + * @param string[] $image * @return string * @deprecated 101.0.1 * @since 101.0.0 @@ -121,4 +121,30 @@ protected function findDefaultValue($key, &$image) return ''; } + + /** + * Sort media entries by position + * + * @param array $mediaEntries + * @return array + */ + private function sortMediaEntriesByPosition(array $mediaEntries): array + { + $mediaEntriesWithNullPositions = []; + foreach ($mediaEntries as $index => $mediaEntry) { + if ($mediaEntry['position'] === null) { + $mediaEntriesWithNullPositions[] = $mediaEntry; + unset($mediaEntries[$index]); + } + } + if (!empty($mediaEntries)) { + usort( + $mediaEntries, + function ($entryA, $entryB) { + return ($entryA['position'] < $entryB['position']) ? -1 : 1; + } + ); + } + return array_merge($mediaEntries, $mediaEntriesWithNullPositions); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php index 7e94484961f9e..28c5d435cd038 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php @@ -6,15 +6,20 @@ namespace Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery; use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Gallery\UpdateHandler; use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\Registry; +use Magento\Store\Api\StoreRepositoryInterface; +use Magento\Store\Model\Store; use Magento\TestFramework\Helper\Bootstrap; /** * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ContentTest extends \PHPUnit\Framework\TestCase { @@ -35,6 +40,16 @@ class ContentTest extends \PHPUnit\Framework\TestCase */ private $dataPersistor; + /** + * @var StoreRepositoryInterface + */ + private $storeRepository; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + /** * @inheritdoc */ @@ -51,6 +66,8 @@ protected function setUp(): void $this->block->setElement($gallery); $this->registry = Bootstrap::getObjectManager()->get(Registry::class); $this->dataPersistor = Bootstrap::getObjectManager()->get(DataPersistorInterface::class); + $this->storeRepository = Bootstrap::getObjectManager()->create(StoreRepositoryInterface::class); + $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); } public function testGetUploader() @@ -120,6 +137,119 @@ public function getImagesAndImageTypesDataProvider() ]; } + /** + * Tests images positions in store view + * + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @dataProvider imagesPositionStoreViewDataProvider + * @param string $addFromStore + * @param array $newImages + * @param string $viewFromStore + * @param array $expectedImages + * @return void + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function testImagesPositionStoreView( + string $addFromStore, + array $newImages, + string $viewFromStore, + array $expectedImages + ): void { + $storeId = (int)$this->storeRepository->get($addFromStore)->getId(); + $product = $this->getProduct($storeId); + $images = $product->getData('media_gallery')['images']; + $images = array_merge($images, $newImages); + $product->setData('media_gallery', ['images' => $images]); + $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class); + $updateHandler->execute($product); + $storeId = (int)$this->storeRepository->get($viewFromStore)->getId(); + $product = $this->getProduct($storeId); + $this->registry->register('current_product', $product); + $actualImages = array_map( + function ($item) { + return [ + 'file' => $item['file'], + 'label' => $item['label'], + 'position' => $item['position'], + ]; + }, + json_decode($this->block->getImagesJson(), true) + ); + $this->assertEquals($expectedImages, array_values($actualImages)); + } + + /** + * @return array[] + */ + public function imagesPositionStoreViewDataProvider(): array + { + return [ + [ + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_small_image.jpg', + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ] + ], + 'default', + [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => 1, + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => null, + 'position' => null, + ], + ] + ], + [ + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_small_image.jpg', + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ] + ], + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => 1, + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'New Image Alt Text', + 'position' => 2, + ], + ] + ] + ]; + } + + /** + * Returns product for testing. + * + * @param int $storeId + * @param string $sku + * @return ProductInterface + */ + private function getProduct(int $storeId = Store::DEFAULT_STORE_ID, string $sku = 'simple'): ProductInterface + { + return $this->productRepository->get($sku, false, $storeId, true); + } + /** * Prepare product, and set it to registry and data persistor. * diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php index b57969280cdf3..2941349a94eaa 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Gallery\UpdateHandler; use Magento\Catalog\Model\ResourceModel\Product as ProductResource; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\View\LayoutInterface; @@ -392,6 +393,107 @@ public function galleryImagesOnStoreViewDataProvider(): array ]; } + /** + * Tests images positions in store view + * + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoConfigFixture default/web/url/catalog_media_url_format image_optimization_parameters + * @dataProvider imagesPositionStoreViewDataProvider + * @param string $addFromStore + * @param array $newImages + * @param string $viewFromStore + * @param array $expectedImages + * @return void + */ + public function testImagesPositionStoreView( + string $addFromStore, + array $newImages, + string $viewFromStore, + array $expectedImages + ): void { + $storeId = (int)$this->storeRepository->get($addFromStore)->getId(); + $product = $this->getProduct($storeId); + $images = $product->getData('media_gallery')['images']; + $images = array_merge($images, $newImages); + $product->setData('media_gallery', ['images' => $images]); + $updateHandler = Bootstrap::getObjectManager()->create(UpdateHandler::class); + $updateHandler->execute($product); + $storeId = (int)$this->storeRepository->get($viewFromStore)->getId(); + $product = $this->getProduct($storeId); + $this->block->setData('product', $product); + $actualImages = array_map( + function ($item) { + return [ + 'img' => parse_url($item['img'], PHP_URL_PATH), + 'caption' => $item['caption'], + 'position' => $item['position'], + ]; + }, + $this->serializer->unserialize($this->block->getGalleryImagesJson()) + ); + $this->assertEquals($expectedImages, array_values($actualImages)); + } + + /** + * @return array[] + */ + public function imagesPositionStoreViewDataProvider(): array + { + return [ + [ + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_small_image.jpg', + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ] + ], + 'default', + [ + [ + 'img' => '/media/catalog/product/m/a/magento_image.jpg', + 'caption' => 'Image Alt Text', + 'position' => 1, + ], + [ + 'img' => '/media/catalog/product/m/a/magento_small_image.jpg', + 'caption' => 'Simple Product', + 'position' => null, + ], + ] + ], + [ + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_small_image.jpg', + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ] + ], + 'fixture_second_store', + [ + [ + 'img' => '/media/catalog/product/m/a/magento_image.jpg', + 'caption' => 'Image Alt Text', + 'position' => 1, + ], + [ + 'img' => '/media/catalog/product/m/a/magento_small_image.jpg', + 'caption' => 'New Image Alt Text', + 'position' => 2, + ], + ] + ] + ]; + } + /** * Updates product gallery images and saves product. * From 09293a587a174c463c8f3d3ff5de775bf372c97c Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Wed, 25 Nov 2020 07:21:45 -0600 Subject: [PATCH 41/62] MC-38787: Admin Product Grid Page indicator issue --- ...hProductGridByStringNoClearActionGroup.xml | 2 +- ...dPageNumberSetsToOneAfterNewSearchTest.xml | 60 +++++++++++-------- ...GridAssertCurrentPageNumberActionGroup.xml | 22 +++++++ ...minGridAssertTotalPageCountActionGroup.xml | 22 +++++++ .../AdminGridGoToNextPageActionGroup.xml | 19 ++++++ 5 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml index a763f37ed153f..95ddfd37c0037 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml @@ -13,7 +13,7 @@ <description>Searches the Admin Products grid by string without clearing filters.</description> </annotations> <arguments> - <argument name="keyword" type="string"/> + <argument name="keyword" defaultValue="" type="string"/> </arguments> <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{keyword}}" stepKey="fillKeywordSearchField"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml index 0cc57ba94dac6..f833171166141 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml @@ -9,7 +9,6 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminGridPageNumberSetsToOneAfterNewSearchTest"> - <annotations> <features value="Catalog"/> <stories value="Catalog grid"/> @@ -27,27 +26,28 @@ <actionGroup ref="AdminProductCatalogPageOpenActionGroup" stepKey="goToProductCatalog"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridToDefaultView"/> <actionGroup ref="DeleteProductsIfTheyExistActionGroup" stepKey="deleteProductIfTheyExist"/> - <createData stepKey="category1" entity="SimpleSubCategory"/> - <createData stepKey="simpleProduct1" entity="SimpleProduct"> + <!-- Create required prerequisites --> + <createData entity="SimpleSubCategory" stepKey="category1"/> + <createData entity="SimpleProduct" stepKey="simpleProduct1"> <requiredEntity createDataKey="category1"/> </createData> - <createData stepKey="simpleProduct2" entity="SimpleProduct"> + <createData entity="SimpleProduct" stepKey="simpleProduct2"> <requiredEntity createDataKey="category1"/> </createData> - <createData stepKey="simpleProduct3" entity="SimpleProduct"> + <createData entity="SimpleProduct" stepKey="simpleProduct3"> <requiredEntity createDataKey="category1"/> </createData> - <createData stepKey="simpleProduct4" entity="SimpleProduct"> + <createData entity="SimpleProduct" stepKey="simpleProduct4"> <requiredEntity createDataKey="category1"/> </createData> - <createData stepKey="virtualProduct1" entity="VirtualProduct"> + <createData entity="VirtualProduct" stepKey="virtualProduct1"> <requiredEntity createDataKey="category1"/> </createData> - <createData stepKey="virtualProduct2" entity="VirtualProduct"> + <createData entity="VirtualProduct" stepKey="virtualProduct2"> <requiredEntity createDataKey="category1"/> </createData> - <createData stepKey="virtualProduct3" entity="VirtualProduct"> + <createData entity="VirtualProduct" stepKey="virtualProduct3"> <requiredEntity createDataKey="category1"/> </createData> </before> @@ -59,39 +59,47 @@ </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/> - <deleteData stepKey="deleteCategory1" createDataKey="category1"/> - - <deleteData stepKey="deleteSimpleProduct1" createDataKey="simpleProduct1"/> - <deleteData stepKey="deleteSimpleProduct2" createDataKey="simpleProduct2"/> - <deleteData stepKey="deleteSimpleProduct3" createDataKey="simpleProduct3"/> - <deleteData stepKey="deleteSimpleProduct4" createDataKey="simpleProduct4"/> - <deleteData stepKey="deleteVirtualProduct1" createDataKey="virtualProduct1"/> - <deleteData stepKey="deleteVirtualProduct2" createDataKey="virtualProduct2"/> - <deleteData stepKey="deleteVirtualProduct3" createDataKey="virtualProduct3"/> + <!-- Delete prerequisites --> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="simpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="simpleProduct3" stepKey="deleteSimpleProduct3"/> + <deleteData createDataKey="simpleProduct4" stepKey="deleteSimpleProduct4"/> + <deleteData createDataKey="virtualProduct1" stepKey="deleteVirtualProduct1"/> + <deleteData createDataKey="virtualProduct2" stepKey="deleteVirtualProduct2"/> + <deleteData createDataKey="virtualProduct3" stepKey="deleteVirtualProduct3"/> + <deleteData createDataKey="category1" stepKey="deleteCategory1"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <actionGroup ref="AdminProductCatalogPageOpenActionGroup" stepKey="goToProductCatalog"/> + <!-- Set the product grid to display one product per page --> <actionGroup ref="AdminDataGridSelectCustomPerPageActionGroup" stepKey="select1ProductPerPage"> <argument name="perPage" value="ProductPerPage.productCount"/> </actionGroup> + <!-- Performing the first search and assertions --> <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForSimpleProduct"> <argument name="keyword" value="SimpleProduct"/> </actionGroup> + <actionGroup ref="AdminGridAssertTotalPageCountActionGroup" stepKey="waitForTotalPagesCountFourToBeVisible"> + <argument name="expectedTotalPageCount" value="4"/> + </actionGroup> + <actionGroup ref="AdminGridGoToNextPageActionGroup" stepKey="clickNextPageProductGrid"/> + <actionGroup ref="AdminGridAssertCurrentPageNumberActionGroup" stepKey="assertCurrentPageIsTwoOnProductGridFirstSearch"> + <argument name="expectedCurrentPageNumber" value="2"/> + </actionGroup> - <waitForElementVisible selector="{{AdminDataGridPaginationSection.totalPagesCount('4')}}" stepKey="seeTotalPagesIsFourOnFirstSearch"/> - <comment userInput="Go to the next page" stepKey="nextPage"/> - <click selector="{{AdminDataGridPaginationSection.nextPage}}" stepKey="clickNextPageProductGrid"/> - <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="2" stepKey="seeOnSecondPageProductGridOnFirstSearch"/> - + <!-- Performing the second search and assertions of successful current page number reset --> <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForVirtualProduct"> <argument name="keyword" value="VirtualProduct"/> </actionGroup> - - <waitForElementVisible selector="{{AdminDataGridPaginationSection.totalPagesCount('3')}}" stepKey="seeTotalPagesIsThreeOnSecondSearch"/> - <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="1" stepKey="seeOnFirstPageProductGridOnSecondSearch"/> + <actionGroup ref="AdminGridAssertTotalPageCountActionGroup" stepKey="waitForTotalPagesCountThreeToBeVisible"> + <argument name="expectedTotalPageCount" value="3"/> + </actionGroup> + <actionGroup ref="AdminGridAssertCurrentPageNumberActionGroup" stepKey="assertCurrentPageIsOneOnProductGridSecondSearch"> + <argument name="expectedCurrentPageNumber" value="1"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml new file mode 100644 index 0000000000000..1765dbe1e7fcd --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertCurrentPageNumberActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridAssertCurrentPageNumberActionGroup"> + <annotations> + <description> + Assert current page number on admin grid + </description> + </annotations> + <arguments> + <argument name="expectedCurrentPageNumber" defaultValue="1" type="string"/> + </arguments> + + <seeInField selector="{{AdminDataGridPaginationSection.currentPage}}" userInput="{{expectedCurrentPageNumber}}" stepKey="seeCurrentPageNumberOnGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml new file mode 100644 index 0000000000000..402bd077f9fbb --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridAssertTotalPageCountActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridAssertTotalPageCountActionGroup"> + <annotations> + <description> + Assert total page count on admin grid + </description> + </annotations> + <arguments> + <argument name="expectedTotalPageCount" defaultValue="1" type="string"/> + </arguments> + + <waitForElementVisible selector="{{AdminDataGridPaginationSection.totalPagesCount('expectedTotalPageCount')}}" stepKey="waitForTotalPagesCountToBeVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml new file mode 100644 index 0000000000000..7da082a279688 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridGoToNextPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridGoToNextPageActionGroup"> + <annotations> + <description> + Go to next page of the admin grid. + </description> + </annotations> + + <click selector="{{AdminDataGridPaginationSection.nextPage}}" stepKey="clickNextPageOnGrid"/> + </actionGroup> +</actionGroups> From 3067d2d9ec14e7dec195977c90ae7f3057d5728f Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Thu, 26 Nov 2020 05:25:48 -0600 Subject: [PATCH 42/62] MC-39354: Magento 2.4.1 - REST API - Shipping and Billing Address - Issues with Custom Attributes Values - Adding billing address custom customer attribute --- ...GuestShippingInformationManagementTest.php | 47 +++++++++ ...mer_address_with_custom_text_attribute.php | 96 +++++++++++++++++++ ...ss_with_custom_text_attribute_rollback.php | 33 +++++++ 3 files changed, 176 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php index 4cb4b00d08a84..e54ce16051d60 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/Api/GuestShippingInformationManagementTest.php @@ -12,6 +12,8 @@ use Magento\Customer\Api\CustomerRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\ShippingAssignmentInterface; use Magento\TestFramework\Helper\Bootstrap; @@ -120,6 +122,51 @@ public function testDifferentAddresses(bool $swapShipping): void $this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation); } + /** + * Test save address information with customer custom address attribute for quote + * + * @return void + * + * @throws LocalizedException + * @throws NoSuchEntityException + * @magentoDataFixture Magento/Sales/_files/quote.php + * @magentoDataFixture Magento/Customer/_files/customer_address_with_custom_text_attribute.php + */ + public function testSaveAddressInformationWithCustomerCustomAddressAttribute(): void + { + $carts = $this->cartRepo->getList( + $this->searchCriteria->addFilter('reserved_order_id', 'test01')->create() + )->getItems(); + $currentQuote = array_pop($carts); + $guestCustomer = $this->customerRepo->get('JohnDoe@mail.com'); + + $customerCustomAddressAttribute = $guestCustomer->getCustomAttributes(); + + /** @var ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $currentQuote->getExtensionAttributes()->getShippingAssignments()[0]; + $shippingAddress = $shippingAssignment->getShipping()->getAddress(); + $billingAddress = $currentQuote->getBillingAddress(); + + if ($customerCustomAddressAttribute) { + $shippingAddress->setCustomAttributes($customerCustomAddressAttribute); + $billingAddress->setCustomAttributes($customerCustomAddressAttribute); + } + + /** @var ShippingInformationInterface $shippingInformation */ + $shippingInformation = $this->shippingFactory->create(); + $shippingInformation->setBillingAddress($billingAddress); + $shippingInformation->setShippingAddress($shippingAddress); + $shippingInformation->setShippingMethodCode('flatrate'); + $shippingInformation->setShippingCarrierCode('flatrate'); + /** @var QuoteIdMask $idMask */ + $idMask = $this->maskFactory->create(); + $idMask->load($currentQuote->getId(), 'quote_id'); + + $paymentDetails = $this->management->saveAddressInformation($idMask->getMaskedId(), $shippingInformation); + $this->assertNotEmpty($paymentDetails); + $this->assertEquals($currentQuote->getGrandTotal(), $paymentDetails->getTotals()->getSubtotal()); + } + /** * Different variations for addresses test. * diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php new file mode 100644 index 0000000000000..ebe4ad76405ef --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Model\Address; +use Magento\Customer\Model\Customer; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Type; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Model\Attribute; +use Magento\Eav\Model\Entity\Attribute\Set; + +$objectManager = Bootstrap::getObjectManager(); +/** @var $entityType Type */ +$entityType = $objectManager + ->create(Config::class) + ->getEntityType('customer'); + +/** @var $attributeSet Set */ +$attributeSet = Bootstrap::getObjectManager() + ->create(Set::class); + +$select = Bootstrap::getObjectManager()->create( + Attribute::class, + [ + 'data' => [ + 'frontend_input' => 'text', + 'frontend_label' => ['test_text_attribute'], + 'sort_order' => 1, + 'backend_type' => 'varchar', + 'is_user_defined' => 1, + 'is_system' => 0, + 'is_used_in_grid' => 1, + 'is_required' => '0', + 'is_visible' => 1, + 'used_in_forms' => [ + 'customer_address_edit', + 'adminhtml_customer_address' + ], + 'attribute_set_id' => $entityType->getDefaultAttributeSetId(), + 'attribute_group_id' => $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()), + 'entity_type_id' => $entityType->getId(), + 'default_value' => '', + ], + ] +); +$select->setAttributeCode('test_text_attribute'); +$select->save(); + +$customer = $objectManager + ->create(Customer::class); +$customer->setWebsiteId(1) + ->setEntityId(1) + ->setEntityTypeId($entityType->getId()) + ->setAttributeSetId($entityType->getDefaultAttributeSetId()) + ->setEmail('JohnDoe@mail.com') + ->setPassword('password') + ->setGroupId(1) + ->setStoreId(1) + ->setIsActive(1) + ->setFirstname('John') + ->setLastname('Doe') + ->setGender(2) + ->setTestTextAttribute('123'); +$customer->isObjectNew(true); +// Create address +$address = $objectManager->create(Address::class); +// default_billing and default_shipping information would not be saved, it is needed only for simple check +$address->addData( + [ + 'firstname' => 'Charles', + 'lastname' => 'Alston', + 'street' => '3781 Neuport Lane', + 'city' => 'Panola', + 'country_id' => 'US', + 'region_id' => '51', + 'postcode' => '30058', + 'telephone' => '770-322-3514', + 'default_billing' => 1, + 'default_shipping' => 1, + ] +); +// Assign customer and address +$customer->addAddress($address); +$customer->save(); +// Mark last address as default billing and default shipping for current customer +$customer->setDefaultBilling($address->getId()); +$customer->setDefaultShipping($address->getId()); +$customer->save(); + +$objectManager->get(Registry::class)->unregister('_fixture/Magento_ImportExport_Customer'); +$objectManager->get(Registry::class)->register('_fixture/Magento_ImportExport_Customer', $customer); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php new file mode 100644 index 0000000000000..3b276b77fbed5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_address_with_custom_text_attribute_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Model\Customer; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Model\Attribute; + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var $attribute Attribute */ +$attribute = Bootstrap::getObjectManager()->create( + Attribute::class +); +$attribute->loadByCode('customer', 'test_text_attribute'); +$attribute->delete(); + +/** @var Customer $customer */ +$customer = Bootstrap::getObjectManager() + ->create(Customer::class); +$customer->setWebsiteId(1); +$customer->loadByEmail('JohnDoe@mail.com'); +$customer->delete(); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 30ac6b5bd5fba5782498aba9b59eb096eebb44c8 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Sat, 28 Nov 2020 17:32:49 -0600 Subject: [PATCH 43/62] MC-38900: GraphQL Mutation setGuestEmailOnCart doesn't update quote address --- .../Quote/Guest/SetGuestEmailOnCartTest.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php index 7a14aca72d83f..ccafe5669e526 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php @@ -8,6 +8,9 @@ namespace Magento\GraphQl\Quote\Guest; use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -21,10 +24,22 @@ class SetGuestEmailOnCartTest extends GraphQlAbstract */ private $getMaskedQuoteIdByReservedOrderId; + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteResource + */ + private $quoteResource; + protected function setUp(): void { $objectManager = Bootstrap::getObjectManager(); $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->quoteFactory = $objectManager->get(QuoteFactory::class); + $this->quoteResource = $objectManager->get(QuoteResource::class); } /** @@ -43,6 +58,32 @@ public function testSetGuestEmailOnCart() $this->assertEquals($email, $response['setGuestEmailOnCart']['cart']['email']); } + /** + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + */ + public function testSetGuestEmailOnCartWithDifferentEmailAddress() + { + $reservedOrderId = 'test_quote'; + $secondEmail = 'attempt2@example.com'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + + $email = 'attempt1@example.com'; + $query = $this->getQuery($maskedQuoteId, $email); + $this->graphQlMutation($query); + + $query = $this->getQuery($maskedQuoteId, $secondEmail); + $this->graphQlMutation($query); + + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); + $addresses = $quote->getAddressesCollection(); + foreach ($addresses as $address) { + if ($address->getAddressType() === Address::ADDRESS_TYPE_SHIPPING) { + $this->assertEquals($secondEmail, $address->getEmail()); + } + } + } + /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php From 834793513617586d6f28ae039f5e5e8d3d63d7a4 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Sat, 28 Nov 2020 17:42:43 -0600 Subject: [PATCH 44/62] MC-38900: GraphQL Mutation setGuestEmailOnCart doesn't update quote address --- .../Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php index ccafe5669e526..e3c77a40a470a 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/SetGuestEmailOnCartTest.php @@ -77,6 +77,7 @@ public function testSetGuestEmailOnCartWithDifferentEmailAddress() $quote = $this->quoteFactory->create(); $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); $addresses = $quote->getAddressesCollection(); + $this->assertEquals(2, $addresses->count()); foreach ($addresses as $address) { if ($address->getAddressType() === Address::ADDRESS_TYPE_SHIPPING) { $this->assertEquals($secondEmail, $address->getEmail()); From 08db336c483bf16fded653e0d42d1aa164db91db Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Mon, 30 Nov 2020 09:26:20 -0600 Subject: [PATCH 45/62] MC-38787: Admin Product Grid Page indicator issue --- ...roup.xml => AdminSearchGridByStringNoClearActionGroup.xml} | 4 ++-- .../Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename app/code/Magento/Catalog/Test/Mftf/ActionGroup/{SearchProductGridByStringNoClearActionGroup.xml => AdminSearchGridByStringNoClearActionGroup.xml} (82%) diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSearchGridByStringNoClearActionGroup.xml similarity index 82% rename from app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml rename to app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSearchGridByStringNoClearActionGroup.xml index 95ddfd37c0037..afaa3c28c56e8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SearchProductGridByStringNoClearActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSearchGridByStringNoClearActionGroup.xml @@ -8,9 +8,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="SearchProductGridByStringNoClearActionGroup"> + <actionGroup name="AdminSearchGridByStringNoClearActionGroup"> <annotations> - <description>Searches the Admin Products grid by string without clearing filters.</description> + <description>Search the Admin grid by string without clearing filters.</description> </annotations> <arguments> <argument name="keyword" defaultValue="" type="string"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml index f833171166141..9bb7064b1870b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminGridPageNumberSetsToOneAfterNewSearchTest.xml @@ -80,7 +80,7 @@ </actionGroup> <!-- Performing the first search and assertions --> - <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForSimpleProduct"> + <actionGroup ref="AdminSearchGridByStringNoClearActionGroup" stepKey="searchForSimpleProduct"> <argument name="keyword" value="SimpleProduct"/> </actionGroup> <actionGroup ref="AdminGridAssertTotalPageCountActionGroup" stepKey="waitForTotalPagesCountFourToBeVisible"> @@ -92,7 +92,7 @@ </actionGroup> <!-- Performing the second search and assertions of successful current page number reset --> - <actionGroup ref="SearchProductGridByStringNoClearActionGroup" stepKey="searchForVirtualProduct"> + <actionGroup ref="AdminSearchGridByStringNoClearActionGroup" stepKey="searchForVirtualProduct"> <argument name="keyword" value="VirtualProduct"/> </actionGroup> <actionGroup ref="AdminGridAssertTotalPageCountActionGroup" stepKey="waitForTotalPagesCountThreeToBeVisible"> From 3e88e175e7ae6609bc8e54a669b988c86ba310ad Mon Sep 17 00:00:00 2001 From: Oleksandr Dubovyk <odubovyk@magento.com> Date: Mon, 30 Nov 2020 13:28:56 -0600 Subject: [PATCH 46/62] MC-38770: Exception during initialization is cacheable - moved dependency to use --- lib/internal/Magento/Framework/App/Bootstrap.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php index 92b925836b295..83f292cfdf703 100644 --- a/lib/internal/Magento/Framework/App/Bootstrap.php +++ b/lib/internal/Magento/Framework/App/Bootstrap.php @@ -13,6 +13,7 @@ use Magento\Framework\Autoload\Populator; use Magento\Framework\Config\File\ConfigFilePool; use Magento\Framework\Filesystem\DriverPool; +use Magento\Framework\HTTP\PhpEnvironment\Response; use Psr\Log\LoggerInterface; /** @@ -428,8 +429,8 @@ public function isDeveloperMode() */ protected function terminate(\Throwable $e) { - /** @var \Magento\Framework\HTTP\PhpEnvironment\Response $response */ - $response = $this->objectManager->get(\Magento\Framework\HTTP\PhpEnvironment\Response::class); + /** @var Response $response */ + $response = $this->objectManager->get(Response::class); $response->clearHeaders(); $response->setHttpResponseCode(500); $response->setHeader('Content-Type', 'text/plain'); From b9b60277c3d967bd8d570e9491d92e3fe4800992 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 30 Nov 2020 13:49:31 -0600 Subject: [PATCH 47/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../AdminOpenCMSBlocksGridActionGroup.xml | 19 ------------------- .../AdminOpenCmsBlocksGridActionGroup.xml | 18 ------------------ 2 files changed, 37 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml deleted file mode 100644 index 18e7e5fb52615..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSBlocksGridActionGroup.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCMSBlocksGridActionGroup"> - <annotations> - <description>Navigate to the Admin Blocks Grid page.</description> - </annotations> - - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml deleted file mode 100644 index 4b57e0c1274f6..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCmsBlocksGridActionGroup"> - <annotations> - <description>Goes to the Cms Blocks grid page.</description> - </annotations> - <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> -</actionGroups> From 12fcd207b8527d60260e788cd2d62769492d8567 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 30 Nov 2020 13:51:19 -0600 Subject: [PATCH 48/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml index 1e3cf57e8777b..4813d8aefdb8d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SalesRuleConsumerData"> <data key="consumerName">sales.rule.update.coupon.usage</data> - <data key="messageLimit">100</data> + <data key="messageLimit">3</data> </entity> </entities> From 4e82e08520c277bf07fd9436d467dbc8c9511ff7 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 30 Nov 2020 13:53:57 -0600 Subject: [PATCH 49/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index f33cf60b4ef6a..da9acc71b33b1 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -63,7 +63,7 @@ <argument name="consumerName" value="{{AdminCodeGeneratorMessageConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{AdminCodeGeneratorMessageConsumerData.messageLimit}}"/> </actionGroup> - <actionGroup ref="CliConsumerStartActionGroup" stepKey="startSalesRuleMessageQueue"> + <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> </actionGroup> From a53949837ce15882e638a297a43ff7415db76801 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 30 Nov 2020 14:03:05 -0600 Subject: [PATCH 50/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../AdminOpenCmsBlocksGridActionGroup.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml new file mode 100644 index 0000000000000..4b57e0c1274f6 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlocksGridActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCmsBlocksGridActionGroup"> + <annotations> + <description>Goes to the Cms Blocks grid page.</description> + </annotations> + <amOnPage url="{{CmsBlocksPage.url}}" stepKey="navigateToCMSBlocksGrid"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> From 2ed8a461e23641f6fea9fa566b9fe69de1a01f34 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Mon, 30 Nov 2020 15:29:47 -0600 Subject: [PATCH 51/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Test/StorefrontAutoGeneratedCouponCodeTest.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index da9acc71b33b1..1c63796827785 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -63,10 +63,6 @@ <argument name="consumerName" value="{{AdminCodeGeneratorMessageConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{AdminCodeGeneratorMessageConsumerData.messageLimit}}"/> </actionGroup> - <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> - <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> - <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> - </actionGroup> <actionGroup ref="ReloadPageActionGroup" stepKey="refreshPage"/> <comment userInput="Replacing reload action and preserve Backward Compatibility" stepKey="waitFormToReload1"/> <conditionalClick selector="{{AdminCartPriceRulesFormSection.manageCouponCodesHeader}}" @@ -105,10 +101,16 @@ <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage"/> + <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> + <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> + <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> + </actionGroup> + <reloadPage stepKey="refreshPage1"/> <!-- Step: 9-10. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage1"/> <waitForPageLoad stepKey="waitForPageLoad3"/> + <reloadPage stepKey="refreshPage2"/> <actionGroup ref="ApplyCartRuleOnStorefrontActionGroup" stepKey="applyCartPriceRule1"> <argument name="product" value="$$createSimpleProduct$$"/> <argument name="couponCode" value="{$couponCode}"/> From 9eb8dc0ac856f6beb20691e92da92f93063531b0 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Tue, 1 Dec 2020 12:44:40 -0600 Subject: [PATCH 52/62] MC-39279: [ CLARIFICATION ] Product zoom effect on iPhone is not similar to desktop or android zoom effect --- lib/web/magnifier/magnify.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/web/magnifier/magnify.js b/lib/web/magnifier/magnify.js index 559e7782f2476..f0c464f4d9042 100644 --- a/lib/web/magnifier/magnify.js +++ b/lib/web/magnifier/magnify.js @@ -314,7 +314,7 @@ define([ ratio, dimentions = {}; - if (allowZoomIn && (!transitionEnabled || !transitionActive) && (isTouchEnabled || + if (allowZoomIn && (!transitionEnabled && !transitionActive) && (isTouchEnabled || !$(zoomInButtonSelector).hasClass(zoomInDisabled))) { $image = $(fullscreenImageSelector); imgOriginalSize = getImageSize($image[0]); @@ -387,7 +387,7 @@ define([ ratio, fitIntoParent; - if (allowZoomOut && (!transitionEnabled || !transitionActive) && (isTouchEnabled || + if (allowZoomOut && (!transitionEnabled && !transitionActive) && (isTouchEnabled || !$(zoomOutButtonSelector).hasClass(zoomOutDisabled))) { allowZoomIn = true; $image = $(fullscreenImageSelector); @@ -679,7 +679,7 @@ define([ if ($image.hasClass(imageDraggableClass)) { $image.removeClass(imageDraggableClass); } - } else if (gallery.fullScreen && (!transitionEnabled || !transitionActive)) { + } else if (gallery.fullScreen && (!transitionEnabled && !transitionActive)) { imagePosY = getTop($image); imagePosX = $image.offset().left; @@ -719,7 +719,7 @@ define([ var clientX, clientY; - if (gallery.fullScreen && isDragActive && (!transitionEnabled || !transitionActive)) { + if (gallery.fullScreen && isDragActive && (!transitionEnabled && !transitionActive)) { if (allowZoomOut && !$image.hasClass(imageDraggableClass)) { $image.addClass(imageDraggableClass); From 842945116e09540bf4232b1c5e2580b405d2afb3 Mon Sep 17 00:00:00 2001 From: Roman Flowers <flowers@adobe.com> Date: Tue, 1 Dec 2020 15:01:48 -0600 Subject: [PATCH 53/62] MC-39279: [ CLARIFICATION ] Product zoom effect on iPhone is not similar to desktop or android zoom effect --- lib/web/magnifier/magnify.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/web/magnifier/magnify.js b/lib/web/magnifier/magnify.js index f0c464f4d9042..7dec3d2d1b9fd 100644 --- a/lib/web/magnifier/magnify.js +++ b/lib/web/magnifier/magnify.js @@ -35,14 +35,11 @@ define([ allowZoomOut = false, allowZoomIn = true; - (function () { - var style = document.documentElement.style, - transitionEnabled = style.transition !== undefined || - style.WebkitTransition !== undefined || - style.MozTransition !== undefined || - style.MsTransition !== undefined || - style.OTransition !== undefined; - })(); + transitionEnabled = document.documentElement.style.transition !== undefined || + document.documentElement.style.WebkitTransition !== undefined || + document.documentElement.style.MozTransition !== undefined || + document.documentElement.style.MsTransition !== undefined || + document.documentElement.style.OTransition !== undefined; /** * Return width and height of original image @@ -314,7 +311,7 @@ define([ ratio, dimentions = {}; - if (allowZoomIn && (!transitionEnabled && !transitionActive) && (isTouchEnabled || + if (allowZoomIn && (!transitionEnabled || !transitionActive) && (isTouchEnabled || !$(zoomInButtonSelector).hasClass(zoomInDisabled))) { $image = $(fullscreenImageSelector); imgOriginalSize = getImageSize($image[0]); @@ -387,7 +384,7 @@ define([ ratio, fitIntoParent; - if (allowZoomOut && (!transitionEnabled && !transitionActive) && (isTouchEnabled || + if (allowZoomOut && (!transitionEnabled || !transitionActive) && (isTouchEnabled || !$(zoomOutButtonSelector).hasClass(zoomOutDisabled))) { allowZoomIn = true; $image = $(fullscreenImageSelector); @@ -679,7 +676,7 @@ define([ if ($image.hasClass(imageDraggableClass)) { $image.removeClass(imageDraggableClass); } - } else if (gallery.fullScreen && (!transitionEnabled && !transitionActive)) { + } else if (gallery.fullScreen && (!transitionEnabled || !transitionActive)) { imagePosY = getTop($image); imagePosX = $image.offset().left; @@ -719,7 +716,7 @@ define([ var clientX, clientY; - if (gallery.fullScreen && isDragActive && (!transitionEnabled && !transitionActive)) { + if (gallery.fullScreen && isDragActive && (!transitionEnabled || !transitionActive)) { if (allowZoomOut && !$image.hasClass(imageDraggableClass)) { $image.addClass(imageDraggableClass); From aa53fa67b3bbfcc65777b84b034af901ea7c32f8 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 1 Dec 2020 18:51:41 -0600 Subject: [PATCH 54/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Test/StorefrontAutoGeneratedCouponCodeTest.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 1c63796827785..571bb30e34dd4 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -101,11 +101,6 @@ <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/> <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage"/> - <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> - <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> - <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> - </actionGroup> - <reloadPage stepKey="refreshPage1"/> <!-- Step: 9-10. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage1"/> @@ -151,6 +146,11 @@ <!-- Step; 15-16. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage3"/> <waitForPageLoad stepKey="waitForPageLoad5"/> + <!-- Start the usage processing consumer --> + <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> + <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> + <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> + </actionGroup> <actionGroup ref="ApplyCartRuleOnStorefrontActionGroup" stepKey="applyCartPriceRule3"> <argument name="product" value="$$createSimpleProduct$$"/> <argument name="couponCode" value="{$couponCode}"/> From 4a5c877c632bfce25e23c655b6c555fb435ff1ed Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 1 Dec 2020 18:52:34 -0600 Subject: [PATCH 55/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml index 4813d8aefdb8d..5ee98b49bd007 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleQueueData.xml @@ -10,6 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="SalesRuleConsumerData"> <data key="consumerName">sales.rule.update.coupon.usage</data> - <data key="messageLimit">3</data> + <data key="messageLimit">10</data> </entity> </entities> From cdc838667b98cdc8e3bad1b74435fcbe23f04d49 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 1 Dec 2020 18:58:05 -0600 Subject: [PATCH 56/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 571bb30e34dd4..1c9e6b99d073e 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -58,7 +58,7 @@ <see selector="{{AdminCartPriceRulesFormSection.successMessage}}" userInput="Message is added to queue, wait to get your coupons soon" stepKey="seeSuccessMessage"/> - <!-- Start message queue for export consumer and coupon processing --> + <!-- Start message queue for export consumer --> <actionGroup ref="CliConsumerStartActionGroup" stepKey="startMessageQueue"> <argument name="consumerName" value="{{AdminCodeGeneratorMessageConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{AdminCodeGeneratorMessageConsumerData.messageLimit}}"/> @@ -143,14 +143,15 @@ <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder1"/> <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage1"/> - <!-- Step; 15-16. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> - <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage3"/> - <waitForPageLoad stepKey="waitForPageLoad5"/> <!-- Start the usage processing consumer --> <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> </actionGroup> + + <!-- Step; 15-16. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> + <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage3"/> + <waitForPageLoad stepKey="waitForPageLoad5"/> <actionGroup ref="ApplyCartRuleOnStorefrontActionGroup" stepKey="applyCartPriceRule3"> <argument name="product" value="$$createSimpleProduct$$"/> <argument name="couponCode" value="{$couponCode}"/> From 4f4067bdfa1b33941d058eac93d18a949da7b6bb Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Tue, 1 Dec 2020 18:59:39 -0600 Subject: [PATCH 57/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 1c9e6b99d073e..36e70d4f80561 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -105,7 +105,6 @@ <!-- Step: 9-10. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage1"/> <waitForPageLoad stepKey="waitForPageLoad3"/> - <reloadPage stepKey="refreshPage2"/> <actionGroup ref="ApplyCartRuleOnStorefrontActionGroup" stepKey="applyCartPriceRule1"> <argument name="product" value="$$createSimpleProduct$$"/> <argument name="couponCode" value="{$couponCode}"/> From c6b97a0d210acbcd3863917b48e65c9dc7f1ac77 Mon Sep 17 00:00:00 2001 From: Oleksandr Iegorov <oiegorov@magento.com> Date: Wed, 2 Dec 2020 09:26:31 -0600 Subject: [PATCH 58/62] MC-29335: Missing paid orders and lock wait timeouts in Guest Checkout --- .../Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml index 36e70d4f80561..18de326a2919d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontAutoGeneratedCouponCodeTest.xml @@ -102,6 +102,12 @@ <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="clickPlaceOrder"/> <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage"/> + <!-- Start the usage processing consumer --> + <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue1"> + <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> + <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> + </actionGroup> + <!-- Step: 9-10. Open the Product Page, add the product to Cart, go to Shopping Cart and Apply the same coupon code --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="openProductPage1"/> <waitForPageLoad stepKey="waitForPageLoad3"/> @@ -143,7 +149,7 @@ <waitForElement selector="{{CheckoutSuccessMainSection.success}}" time="30" stepKey="waitForLoadSuccessPage1"/> <!-- Start the usage processing consumer --> - <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue"> + <actionGroup ref="CliConsumerStartActionGroup" stepKey="startUsageProcessingMessageQueue2"> <argument name="consumerName" value="{{SalesRuleConsumerData.consumerName}}"/> <argument name="maxMessages" value="{{SalesRuleConsumerData.messageLimit}}"/> </actionGroup> From 3163fae9967c1a429d6dde46d6c8133acc4b5f34 Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 3 Dec 2020 09:56:39 -0600 Subject: [PATCH 59/62] MC-38951: Images positions are inconsistent across store-views if images were added in a store-view level - Fix images positions for default scope if image were added in store view level --- .../Model/Product/Gallery/CreateHandler.php | 39 ++++++++--- .../Model/Import/Product.php | 11 ++- .../Import/Product/MediaGalleryProcessor.php | 6 +- ...uteMediaGalleryManagementInterfaceTest.php | 18 +++-- .../Helper/Form/Gallery/ContentTest.php | 2 +- .../Block/Product/View/GalleryTest.php | 2 +- .../Product/Gallery/UpdateHandlerTest.php | 67 +++++++++++++++++++ 7 files changed, 126 insertions(+), 19 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index 5a9d53ce80cf8..7a1bd21d78182 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -267,29 +267,50 @@ protected function processNewAndExistingImages($product, array &$images) { foreach ($images as &$image) { if (empty($image['removed'])) { + $isNew = empty($image['value_id']); $data = $this->processNewImage($product, $image); - if (!$product->isObjectNew()) { - $this->resourceModel->deleteGalleryValueInStore( - $image['value_id'], - $product->getData($this->metadata->getLinkField()), - $product->getStoreId() - ); - } // Add per store labels, position, disabled $data['value_id'] = $image['value_id']; $data['label'] = isset($image['label']) ? $image['label'] : ''; - $data['position'] = isset($image['position']) ? (int)$image['position'] : 0; + $data['position'] = isset($image['position']) && $image['position'] !== '' + ? (int)$image['position'] + : null; $data['disabled'] = isset($image['disabled']) ? (int)$image['disabled'] : 0; $data['store_id'] = (int)$product->getStoreId(); $data[$this->metadata->getLinkField()] = (int)$product->getData($this->metadata->getLinkField()); - $this->resourceModel->insertGalleryValueInStore($data); + $this->saveGalleryStoreValue($product, $data); + if ($isNew && $data['store_id'] !== Store::DEFAULT_STORE_ID) { + $dataForDefaultScope = $data; + $dataForDefaultScope['store_id'] = Store::DEFAULT_STORE_ID; + $dataForDefaultScope['disabled'] = 0; + $dataForDefaultScope['label'] = null; + $this->saveGalleryStoreValue($product, $dataForDefaultScope); + } } } } + /** + * Save media gallery store value + * + * @param Product $product + * @param array $data + */ + private function saveGalleryStoreValue(Product $product, array $data): void + { + if (!$product->isObjectNew()) { + $this->resourceModel->deleteGalleryValueInStore( + $data['value_id'], + $data[$this->metadata->getLinkField()], + $data['store_id'] + ); + } + $this->resourceModel->insertGalleryValueInStore($data); + } + /** * Processes image as new. * diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index 428961aa6ddf6..13b7cbc2dfd2a 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -1807,7 +1807,7 @@ protected function _saveProducts() if ($column === self::COL_MEDIA_IMAGE) { $rowData[$column][] = $uploadedFile; } - $mediaGallery[$storeId][$rowSku][$uploadedFile] = [ + $mediaGalleryStoreData = [ 'attribute_id' => $this->getMediaGalleryAttributeId(), 'label' => isset($rowLabels[$column][$columnImageKey]) ? $rowLabels[$column][$columnImageKey] @@ -1817,6 +1817,15 @@ protected function _saveProducts() ? $imageHiddenStates[$columnImage] : '0', 'value' => $uploadedFile, ]; + $mediaGallery[$storeId][$rowSku][$uploadedFile] = $mediaGalleryStoreData; + // Add record for default scope if it does not exist + if (!($mediaGallery[Store::DEFAULT_STORE_ID][$rowSku][$uploadedFile] ?? [])) { + //Set label and disabled values to their default values + $mediaGalleryStoreData['label'] = null; + $mediaGalleryStoreData['disabled'] = 0; + $mediaGallery[Store::DEFAULT_STORE_ID][$rowSku][$uploadedFile] = $mediaGalleryStoreData; + } + } } } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php index d4694b72ba64f..c838688c1c4f8 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php @@ -12,6 +12,7 @@ use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface; +use Magento\Store\Model\Store; /** * Process and saves images during import. @@ -259,7 +260,10 @@ private function prepareMediaGalleryValueData( $position = $data['position']; $storeId = $data['store_id']; $mediaGalleryValueData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value]; - $mediaGalleryValueData[$index]['position'] = $position + ($lastPositions[$storeId][$productId] ?? 0); + $lastPosition = $lastPositions[$storeId][$productId] + ?? $lastPositions[Store::DEFAULT_STORE_ID][$productId] + ?? 0; + $mediaGalleryValueData[$index]['position'] = $position + $lastPosition; unset($mediaGalleryValueData[$index]['value']); } return $mediaGalleryValueData; diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php index ba12a02cb5b1f..09987457e3c56 100644 --- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductAttributeMediaGalleryManagementInterfaceTest.php @@ -235,8 +235,8 @@ public function testCreateWithNotDefaultStoreId() $this->assertEquals($updatedImage['file'], $targetProduct->getData('image')); // No values for default store view were provided $this->assertNull($updatedImage['label_default']); - $this->assertNull($updatedImage['position_default']); - $this->assertNull($updatedImage['disabled_default']); + $this->assertEquals(1, $updatedImage['position_default']); + $this->assertEquals(0, $updatedImage['disabled_default']); } /** @@ -483,7 +483,9 @@ public function testCreateThrowsExceptionIfProvidedImageHasWrongMimeType() public function testCreateThrowsExceptionIfTargetProductDoesNotExist() { $this->expectException(\Exception::class); - $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.'); + $this->expectExceptionMessage( + 'The product that was requested doesn\'t exist. Verify the product and try again.' + ); $this->createServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media'; @@ -538,7 +540,9 @@ public function testCreateThrowsExceptionIfProvidedImageNameContainsForbiddenCha public function testUpdateThrowsExceptionIfTargetProductDoesNotExist() { $this->expectException(\Exception::class); - $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.'); + $this->expectExceptionMessage( + 'The product that was requested doesn\'t exist. Verify the product and try again.' + ); $this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media' . '/' . 'wrong-sku'; @@ -592,7 +596,9 @@ public function testUpdateThrowsExceptionIfThereIsNoImageWithGivenId() public function testDeleteThrowsExceptionIfTargetProductDoesNotExist() { $this->expectException(\Exception::class); - $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.'); + $this->expectExceptionMessage( + 'The product that was requested doesn\'t exist. Verify the product and try again.' + ); $this->deleteServiceInfo['rest']['resourcePath'] = '/V1/products/wrong_product_sku/media/9999'; $requestData = [ @@ -782,6 +788,6 @@ public function testAddProductVideo() $this->assertEquals(1, $updatedImage['position']); $this->assertEquals(0, $updatedImage['disabled']); $this->assertStringStartsWith('/t/e/test_image', $updatedImage['file']); - $this->assertEquals($videoContent, array_intersect($updatedImage, $videoContent)); + $this->assertEquals($videoContent, array_intersect_key($updatedImage, $videoContent)); } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php index 28c5d435cd038..56a07034bd490 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php @@ -206,7 +206,7 @@ public function imagesPositionStoreViewDataProvider(): array [ 'file' => '/m/a/magento_small_image.jpg', 'label' => null, - 'position' => null, + 'position' => 2, ], ] ], diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php index 2941349a94eaa..bcec3168c7885 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/GalleryTest.php @@ -462,7 +462,7 @@ public function imagesPositionStoreViewDataProvider(): array [ 'img' => '/media/catalog/product/m/a/magento_small_image.jpg', 'caption' => 'Simple Product', - 'position' => null, + 'position' => 2, ], ] ], diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php index 481ec6aeac0f2..f317b9bbf377e 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php @@ -593,6 +593,73 @@ public function updateImageDataProvider(): array ]; } + /** + * Tests that images positions are inconsistent across store-views if images were added in a store-view level + * + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @magentoDataFixture Magento/Store/_files/second_store.php + * @return void + */ + public function testAddImageInStoreView(): void + { + $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); + $existingImagePath = '/m/a/magento_image.jpg'; + $newImagePath = '/m/a/magento_small_image.jpg'; + $product = $this->getProduct($secondStoreId); + $images = $product->getData('media_gallery')['images']; + $newImage = [ + 'file' => $newImagePath, + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ]; + $images[] = $newImage; + $product->setData('media_gallery', ['images' => $images]); + $this->updateHandler->execute($product); + $product = $this->getProduct(Store::DEFAULT_STORE_ID); + $expectedImages = [ + [ + 'file' => $existingImagePath, + 'label' => 'Image Alt Text', + 'position' => 1 + ], + [ + 'file' => $newImagePath, + 'label' => null, + 'position' => 2 + ], + ]; + $actualImages = array_map( + function (\Magento\Framework\DataObject $item) { + return $item->toArray(['file', 'label', 'position']); + }, + $product->getMediaGalleryImages()->getItems() + ); + $this->assertEquals($expectedImages, array_values($actualImages)); + $product->cleanModelCache(); + $product = $this->getProduct($secondStoreId); + $expectedImages = [ + [ + 'file' => $existingImagePath, + 'label' => 'Image Alt Text', + 'position' => 1 + ], + [ + 'file' => $newImagePath, + 'label' => 'New Image Alt Text', + 'position' => 2 + ], + ]; + $actualImages = array_map( + function (\Magento\Framework\DataObject $item) { + return $item->toArray(['file', 'label', 'position']); + }, + $product->getMediaGalleryImages()->getItems() + ); + $this->assertEquals($expectedImages, array_values($actualImages)); + } + /** * Check product image link and product image exist * From 473182be13ae40c0e92548226d567eef041f98ad Mon Sep 17 00:00:00 2001 From: Buba Suma <soumah@adobe.com> Date: Thu, 3 Dec 2020 12:46:16 -0600 Subject: [PATCH 60/62] MC-38951: Images positions are inconsistent across store-views if images were added in a store-view level - Fix images positions for default scope if image were added in store view level --- .../Product/Gallery/UpdateHandlerTest.php | 123 +++++++++++------- .../Model/Import/ProductTest.php | 85 ++++++++++++ ...port_media_additional_images_storeview.csv | 2 + 3 files changed, 163 insertions(+), 47 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php index f317b9bbf377e..d20bf2907c780 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/Gallery/UpdateHandlerTest.php @@ -594,70 +594,99 @@ public function updateImageDataProvider(): array } /** - * Tests that images positions are inconsistent across store-views if images were added in a store-view level + * Tests that images are added correctly * * @magentoDataFixture Magento/Catalog/_files/product_with_image.php * @magentoDataFixture Magento/Store/_files/second_store.php + * @dataProvider addImagesDataProvider + * @param string $addFromStore + * @param array $newImages + * @param string $viewFromStore + * @param array $expectedImages + * @param array $select * @return void */ - public function testAddImageInStoreView(): void - { - $secondStoreId = (int)$this->storeRepository->get('fixture_second_store')->getId(); - $existingImagePath = '/m/a/magento_image.jpg'; - $newImagePath = '/m/a/magento_small_image.jpg'; - $product = $this->getProduct($secondStoreId); + public function testAddImages( + string $addFromStore, + array $newImages, + string $viewFromStore, + array $expectedImages, + array $select = ['file', 'label', 'position'] + ): void { + $storeId = (int)$this->storeRepository->get($addFromStore)->getId(); + $product = $this->getProduct($storeId); $images = $product->getData('media_gallery')['images']; - $newImage = [ - 'file' => $newImagePath, - 'position' => 2, - 'label' => 'New Image Alt Text', - 'disabled' => 0, - 'media_type' => 'image' - ]; - $images[] = $newImage; + $images = array_merge($images, $newImages); $product->setData('media_gallery', ['images' => $images]); $this->updateHandler->execute($product); - $product = $this->getProduct(Store::DEFAULT_STORE_ID); - $expectedImages = [ - [ - 'file' => $existingImagePath, - 'label' => 'Image Alt Text', - 'position' => 1 - ], - [ - 'file' => $newImagePath, - 'label' => null, - 'position' => 2 - ], - ]; + $storeId = (int)$this->storeRepository->get($viewFromStore)->getId(); + $product = $this->getProduct($storeId); $actualImages = array_map( - function (\Magento\Framework\DataObject $item) { - return $item->toArray(['file', 'label', 'position']); + function (\Magento\Framework\DataObject $item) use ($select) { + return $item->toArray($select); }, $product->getMediaGalleryImages()->getItems() ); $this->assertEquals($expectedImages, array_values($actualImages)); - $product->cleanModelCache(); - $product = $this->getProduct($secondStoreId); - $expectedImages = [ + } + + /** + * @return array[] + */ + public function addImagesDataProvider(): array + { + return [ [ - 'file' => $existingImagePath, - 'label' => 'Image Alt Text', - 'position' => 1 + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_small_image.jpg', + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ] + ], + 'default', + [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => 1, + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => null, + 'position' => 2, + ], + ] ], [ - 'file' => $newImagePath, - 'label' => 'New Image Alt Text', - 'position' => 2 - ], + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_small_image.jpg', + 'position' => 2, + 'label' => 'New Image Alt Text', + 'disabled' => 0, + 'media_type' => 'image' + ] + ], + 'fixture_second_store', + [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => 1, + ], + [ + 'file' => '/m/a/magento_small_image.jpg', + 'label' => 'New Image Alt Text', + 'position' => 2, + ], + ] + ] ]; - $actualImages = array_map( - function (\Magento\Framework\DataObject $item) { - return $item->toArray(['file', 'label', 'position']); - }, - $product->getMediaGalleryImages()->getItems() - ); - $this->assertEquals($expectedImages, array_values($actualImages)); } /** diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 01a6bfe7b39b6..3ca6754c77767 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -3330,4 +3330,89 @@ public function testUpdateImageByNameNotPrefixedWithSlash() $imageItems = $product->getMediaGalleryImages()->getItems(); $this->assertCount(0, $imageItems); } + + /** + * Tests that images are imported correctly + * + * @magentoDataFixture mediaImportImageFixture + * @magentoDataFixture Magento/Store/_files/core_fixturestore.php + * @magentoDataFixture Magento/Catalog/_files/product_with_image.php + * @dataProvider importImagesDataProvider + * @magentoAppIsolation enabled + * @param string $importFile + * @param string $productSku + * @param string $storeCode + * @param array $expectedImages + * @param array $select + */ + public function testImportImages( + string $importFile, + string $productSku, + string $storeCode, + array $expectedImages, + array $select = ['file', 'label', 'position'] + ): void { + $this->importDataForMediaTest($importFile); + $product = $this->getProductBySku($productSku, $storeCode); + $actualImages = array_map( + function (\Magento\Framework\DataObject $item) use ($select) { + return $item->toArray($select); + }, + $product->getMediaGalleryImages()->getItems() + ); + $this->assertEquals($expectedImages, array_values($actualImages)); + } + + /** + * @return array[] + */ + public function importImagesDataProvider(): array + { + return [ + [ + 'import_media_additional_images_storeview.csv', + 'simple', + 'default', + [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => 1 + ], + [ + 'file' => '/m/a/magento_additional_image_one.jpg', + 'label' => null, + 'position' => 2 + ], + [ + 'file' => '/m/a/magento_additional_image_two.jpg', + 'label' => null, + 'position' => 3 + ], + ] + ], + [ + 'import_media_additional_images_storeview.csv', + 'simple', + 'fixturestore', + [ + [ + 'file' => '/m/a/magento_image.jpg', + 'label' => 'Image Alt Text', + 'position' => 1 + ], + [ + 'file' => '/m/a/magento_additional_image_one.jpg', + 'label' => 'Additional Image Label One', + 'position' => 2 + ], + [ + 'file' => '/m/a/magento_additional_image_two.jpg', + 'label' => 'Additional Image Label Two', + 'position' => 3 + ], + ] + ] + ]; + } } diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv new file mode 100644 index 0000000000000..ed8755a73fcb1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/import_media_additional_images_storeview.csv @@ -0,0 +1,2 @@ +"sku","store_view_code","additional_images","additional_image_labels" +"simple","fixturestore","magento_additional_image_one.jpg, magento_additional_image_two.jpg","Additional Image Label One,Additional Image Label Two" From c84b731e5be20464dab54c25a1d3ee8bc7150d35 Mon Sep 17 00:00:00 2001 From: Arnob Saha <arnobsh@gmail.com> Date: Thu, 3 Dec 2020 12:57:20 -0600 Subject: [PATCH 61/62] MC-38655: [GraphQL] Gifting message is not saved in Order - Missing the event at GraphQL while Gift message saving --- .../GiftMessageGraphQl/etc/graphql/events.xml | 12 ++++++ .../GraphQl/Quote/Guest/PlaceOrderTest.php | 39 +++++++++++++++++++ .../GraphQl/Quote/_files/set_gift_options.php | 33 ++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 app/code/Magento/GiftMessageGraphQl/etc/graphql/events.xml create mode 100644 dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php diff --git a/app/code/Magento/GiftMessageGraphQl/etc/graphql/events.xml b/app/code/Magento/GiftMessageGraphQl/etc/graphql/events.xml new file mode 100644 index 0000000000000..2411221ded375 --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/etc/graphql/events.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + <event name="sales_model_service_quote_submit_before"> + <observer name="giftmessage" instance="Magento\GiftMessage\Observer\SalesEventQuoteSubmitBeforeObserver" shared="false" /> + </event> +</config> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php index 72d35fdd51b96..db9a12e654a2c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/PlaceOrderTest.php @@ -327,6 +327,45 @@ public function testPlaceOrderOfCustomerCart() $this->graphQlMutation($query); } + /** + * Test place order with gift message options + * + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoConfigFixture default_store carriers/flatrate/active 1 + * @magentoConfigFixture default_store carriers/tablerate/active 1 + * @magentoConfigFixture default_store carriers/freeshipping/active 1 + * @magentoConfigFixture default_store payment/banktransfer/active 1 + * @magentoConfigFixture default_store payment/cashondelivery/active 1 + * @magentoConfigFixture default_store payment/checkmo/active 1 + * @magentoConfigFixture default_store payment/purchaseorder/active 1 + * @magentoConfigFixture sales/gift_options/allow_order 1 + * @magentoConfigFixture default_store customer/create_account/auto_group_assign 1 + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_gift_options.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_checkmo_payment_method.php + */ + public function testPlaceOrderWithGiftMessage() + { + $reservedOrderId = 'test_quote'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); + + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('placeOrder', $response); + self::assertArrayHasKey('order_number', $response['placeOrder']['order']); + self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); + $orderIncrementId = $response['placeOrder']['order']['order_number']; + $order = $this->orderFactory->create(); + $order->loadByIncrementId($orderIncrementId); + $this->assertNotEmpty($order->getGiftMessageId()); + } + /** * @param string $maskedQuoteId * @return string diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php new file mode 100644 index 0000000000000..c870fa53c5e39 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/set_gift_options.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = $objectManager->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = $objectManager->get(QuoteResource::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = $objectManager->get(CartRepositoryInterface::class); + + +/** @var \Magento\GiftMessage\Model\Message $message */ +$message = $objectManager->create(\Magento\GiftMessage\Model\Message::class); +$message->setSender('Romeo'); +$message->setRecipient('Mercutio'); +$message->setMessage('I thought all for the best.'); +$message->save(); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quote->setGiftMessageId($message->getId()); +$cartRepository->save($quote); From 69c26af51b38754144eedf1ad73b44e479f4f29a Mon Sep 17 00:00:00 2001 From: Dmytro Horytskyi <dhorytskyi@magento.com> Date: Tue, 8 Dec 2020 21:04:52 -0600 Subject: [PATCH 62/62] MC-39776: JS error on Create New Customer Account page --- .../templates/shopping-assistance.phtml | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/LoginAsCustomerAssistance/view/frontend/templates/shopping-assistance.phtml b/app/code/Magento/LoginAsCustomerAssistance/view/frontend/templates/shopping-assistance.phtml index 7765975863485..5551ea1baba70 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/view/frontend/templates/shopping-assistance.phtml +++ b/app/code/Magento/LoginAsCustomerAssistance/view/frontend/templates/shopping-assistance.phtml @@ -11,20 +11,18 @@ use Magento\LoginAsCustomerAssistance\ViewModel\ShoppingAssistanceViewModel; /** @var Escaper $escaper */ /** @var ShoppingAssistanceViewModel $viewModel */ $viewModel = $block->getViewModel(); -?> -<script type="text/x-magento-init"> -{ - ".form-create-account, .form-edit-account": { - "Magento_LoginAsCustomerAssistance/js/opt-in": { - "allowAccess": "<?= /* @noEscape */ IsAssistanceEnabledInterface::ALLOWED ?>", - "denyAccess": "<?= /* @noEscape */ IsAssistanceEnabledInterface::DENIED ?>" +if ($viewModel->isLoginAsCustomerEnabled()): ?> + <script type="text/x-magento-init"> + { + ".form-create-account, .form-edit-account": { + "Magento_LoginAsCustomerAssistance/js/opt-in": { + "allowAccess": "<?= /* @noEscape */ IsAssistanceEnabledInterface::ALLOWED ?>", + "denyAccess": "<?= /* @noEscape */ IsAssistanceEnabledInterface::DENIED ?>" + } } } -} -</script> - -<?php if ($viewModel->isLoginAsCustomerEnabled()): ?> + </script> <div class="field choice"> <input type="checkbox" name="assistance_allowed_checkbox"