From 7a04c04bec1fa3525d8b43ca684e13da3cb37974 Mon Sep 17 00:00:00 2001 From: Pascal Brouwers Date: Fri, 24 Jan 2020 15:42:02 +0100 Subject: [PATCH 001/195] issue/26526 Fix orderRepository does not check if there are no extensionAttributes --- app/code/Magento/Sales/Model/OrderRepository.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/code/Magento/Sales/Model/OrderRepository.php b/app/code/Magento/Sales/Model/OrderRepository.php index f93de4c32d888..baac39b333712 100644 --- a/app/code/Magento/Sales/Model/OrderRepository.php +++ b/app/code/Magento/Sales/Model/OrderRepository.php @@ -157,6 +157,9 @@ public function get($id) private function setOrderTaxDetails(OrderInterface $order) { $extensionAttributes = $order->getExtensionAttributes(); + if ($extensionAttributes === null) { + $extensionAttributes = $this->orderExtensionFactory->create(); + } $orderTaxDetails = $this->orderTaxManagement->getOrderTaxDetails($order->getEntityId()); $appliedTaxes = $orderTaxDetails->getAppliedTaxes(); @@ -180,6 +183,9 @@ private function setOrderTaxDetails(OrderInterface $order) private function setPaymentAdditionalInfo(OrderInterface $order): void { $extensionAttributes = $order->getExtensionAttributes(); + if ($extensionAttributes === null) { + $extensionAttributes = $this->orderExtensionFactory->create(); + } $paymentAdditionalInformation = $order->getPayment()->getAdditionalInformation(); $objects = []; From 405dfec0026e6be2c11281d31774f08a293ded2d Mon Sep 17 00:00:00 2001 From: Stanislav Ilnytskyi Date: Fri, 14 Feb 2020 14:00:50 +0100 Subject: [PATCH 002/195] Product not exist fix when attempt to get an image --- app/code/Magento/Catalog/Helper/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index 5b0aa0c496ecd..085fb2c224b14 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -514,7 +514,7 @@ protected function initBaseFile() if ($this->getImageFile()) { $model->setBaseFile($this->getImageFile()); } else { - $model->setBaseFile($this->getProduct()->getData($model->getDestinationSubdir())); + $model->setBaseFile($this->getProduct() ? $this->getProduct()->getData($model->getDestinationSubdir()) : ''); } } return $this; From 652971e5b1cdd6df545b6c8a99cd1c306b0a10d9 Mon Sep 17 00:00:00 2001 From: Alex Taranovsky Date: Sun, 22 Mar 2020 21:04:47 +0200 Subject: [PATCH 003/195] magento/magento2#: Add a new index for `cron_schedule` table --- app/code/Magento/Cron/etc/db_schema.xml | 4 ++++ app/code/Magento/Cron/etc/db_schema_whitelist.json | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Cron/etc/db_schema.xml b/app/code/Magento/Cron/etc/db_schema.xml index 206b8f64f3ae7..c689baf0f4bca 100644 --- a/app/code/Magento/Cron/etc/db_schema.xml +++ b/app/code/Magento/Cron/etc/db_schema.xml @@ -28,5 +28,9 @@ + + + + diff --git a/app/code/Magento/Cron/etc/db_schema_whitelist.json b/app/code/Magento/Cron/etc/db_schema_whitelist.json index c8666896627e2..f0d6ebed8290f 100644 --- a/app/code/Magento/Cron/etc/db_schema_whitelist.json +++ b/app/code/Magento/Cron/etc/db_schema_whitelist.json @@ -12,10 +12,11 @@ }, "index": { "CRON_SCHEDULE_JOB_CODE": true, - "CRON_SCHEDULE_SCHEDULED_AT_STATUS": true + "CRON_SCHEDULE_SCHEDULED_AT_STATUS": true, + "CRON_SCHEDULE_SCHEDULE_ID_STATUS": true }, "constraint": { "PRIMARY": true } } -} \ No newline at end of file +} From 3f616adb872b020c2ec132f2998d806ef7da2b8a Mon Sep 17 00:00:00 2001 From: Ashoka de Wit Date: Thu, 26 Mar 2020 12:19:06 +0800 Subject: [PATCH 004/195] Convert MSRP currency of configurable product options The MSRP of configurable products is updated in Javascript when an option is chosen. Javascript uses an object with prices of the options. The prices in this object are converted to the chosen currency. MSRP prices however were not converted, leading to the price being displayed in a wrong currency. --- .../Block/Product/View/Type/Configurable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php index 55c0c8f6ca4ce..7fe0aca43c5b8 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php @@ -321,7 +321,7 @@ protected function getOptionPrices() 'tierPrices' => $tierPrices, 'msrpPrice' => [ 'amount' => $this->localeFormat->getNumber( - $product->getMsrp() + $this->priceCurrency->convertAndRound($product->getMsrp()) ), ], ]; From 2f8b560ab3801595d8e78b2f4c696c8c75974e9b Mon Sep 17 00:00:00 2001 From: Gaurav Agarwal <37572719+gauravagarwal1001@users.noreply.github.com> Date: Sun, 5 Apr 2020 03:42:02 +0530 Subject: [PATCH 005/195] Fix Issue #27350 --- .../templates/sales/invoice/create/items/renderer.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml index a7d49b4b3530a..f8dc1904288e0 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml @@ -16,7 +16,7 @@ getChildren($_item); ?> - + getOrderOptions() || $_item->getDescription()) : ?> @@ -34,7 +34,7 @@ getOrderItem()->getParentItem()) : ?> getSelectionAttributes($_item) ?> @@ -124,7 +124,7 @@ canShowPriceInfo($_item) || $shipTogether) : ?> - canEditQty()) : ?> + canEditQty() && $canEditItemQty) : ?> Date: Sun, 12 Apr 2020 17:38:41 +0200 Subject: [PATCH 006/195] Edits for bug magento/magento2/#11998 --- app/code/Magento/Catalog/Block/Product/View.php | 4 ++++ .../Block/Product/View/Type/Configurable.php | 5 +++++ .../Product/Type/Configurable/Variations/Prices.php | 3 +++ app/code/Magento/Tax/Pricing/Render/Adjustment.php | 13 +++++++++++++ .../view/base/templates/pricing/adjustment.phtml | 7 ++++--- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index 437171bcb4bc6..232788d79ae9f 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -196,6 +196,10 @@ public function getJsonConfig() 'productId' => (int)$product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), 'prices' => [ + 'baseOldPrice' => [ + 'amount' => $priceInfo->getPrice('regular_price')->getAmount()->getBaseAmount() * 1, + 'adjustments' => [] + ], 'oldPrice' => [ 'amount' => $priceInfo->getPrice('regular_price')->getAmount()->getValue() * 1, 'adjustments' => [] diff --git a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php index 55c0c8f6ca4ce..f3c3ed55a5d3a 100644 --- a/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php +++ b/app/code/Magento/ConfigurableProduct/Block/Product/View/Type/Configurable.php @@ -303,6 +303,11 @@ protected function getOptionPrices() $prices[$product->getId()] = [ + 'baseOldPrice' => [ + 'amount' => $this->localeFormat->getNumber( + $priceInfo->getPrice('regular_price')->getAmount()->getBaseAmount() + ), + ], 'oldPrice' => [ 'amount' => $this->localeFormat->getNumber( $priceInfo->getPrice('regular_price')->getAmount()->getValue() diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Variations/Prices.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Variations/Prices.php index b8a948d55f11a..492c5de55ad7f 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Variations/Prices.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Variations/Prices.php @@ -39,6 +39,9 @@ public function getFormattedPrices(\Magento\Framework\Pricing\PriceInfo\Base $pr $finalPrice = $priceInfo->getPrice('final_price'); return [ + 'baseOldPrice' => [ + 'amount' => $this->localeFormat->getNumber($regularPrice->getAmount()->getBaseAmount()), + ], 'oldPrice' => [ 'amount' => $this->localeFormat->getNumber($regularPrice->getAmount()->getValue()), ], diff --git a/app/code/Magento/Tax/Pricing/Render/Adjustment.php b/app/code/Magento/Tax/Pricing/Render/Adjustment.php index 8613e62f2983e..6141b958bbaa1 100644 --- a/app/code/Magento/Tax/Pricing/Render/Adjustment.php +++ b/app/code/Magento/Tax/Pricing/Render/Adjustment.php @@ -173,4 +173,17 @@ public function displayPriceExcludingTax() { return $this->taxHelper->displayPriceExcludingTax(); } + + /** + * Obtain a value for data-price-type attribute + * + * @return string + */ + public function getDataPriceType() + { + if ( $this->getData('price_type') !== 'finalPrice' && $priceType = $this->getData('price_type')) { + return 'base' . ucfirst($priceType); + } + return 'basePrice'; + } } diff --git a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml index e87d1c9eb96aa..5db066eb5e2fd 100644 --- a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml +++ b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml @@ -6,12 +6,13 @@ ?> + displayBothPrices()) : ?> - getDisplayAmountExclTax() ?> From 7f4b2766b8b068ca03595d0548e84ed94c47773b Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Mon, 13 Apr 2020 12:30:23 +0200 Subject: [PATCH 007/195] Static error fixes (code styling) --- app/code/Magento/Tax/Pricing/Render/Adjustment.php | 4 +++- .../Magento/Tax/view/base/templates/pricing/adjustment.phtml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/Pricing/Render/Adjustment.php b/app/code/Magento/Tax/Pricing/Render/Adjustment.php index 6141b958bbaa1..c2c3a24d59535 100644 --- a/app/code/Magento/Tax/Pricing/Render/Adjustment.php +++ b/app/code/Magento/Tax/Pricing/Render/Adjustment.php @@ -38,6 +38,8 @@ public function __construct( } /** + * Apply the right HTML output to the adjustment + * * @return string */ protected function apply() @@ -181,7 +183,7 @@ public function displayPriceExcludingTax() */ public function getDataPriceType() { - if ( $this->getData('price_type') !== 'finalPrice' && $priceType = $this->getData('price_type')) { + if ( $this->getData('price_type') !== 'finalPrice' && $priceType = $this->getData('price_type')){ return 'base' . ucfirst($priceType); } return 'basePrice'; diff --git a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml index 5db066eb5e2fd..685893151bc5a 100644 --- a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml +++ b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml @@ -8,7 +8,7 @@ -displayBothPrices()) : ?> +displayBothPrices()): ?> Date: Tue, 14 Apr 2020 08:50:01 +0200 Subject: [PATCH 008/195] Refactor small line of code. + Code Styling. --- app/code/Magento/Tax/Pricing/Render/Adjustment.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Tax/Pricing/Render/Adjustment.php b/app/code/Magento/Tax/Pricing/Render/Adjustment.php index c2c3a24d59535..ec451eb012878 100644 --- a/app/code/Magento/Tax/Pricing/Render/Adjustment.php +++ b/app/code/Magento/Tax/Pricing/Render/Adjustment.php @@ -183,8 +183,8 @@ public function displayPriceExcludingTax() */ public function getDataPriceType() { - if ( $this->getData('price_type') !== 'finalPrice' && $priceType = $this->getData('price_type')){ - return 'base' . ucfirst($priceType); + if ($this->getData('price_type') && $this->getData('price_type') !== 'finalPrice') { + return 'base' . ucfirst($this->getData('price_type')); } return 'basePrice'; } From a84629ff29f6011fceea1b4a40498a9ba78382d3 Mon Sep 17 00:00:00 2001 From: "v.prokopov" Date: Tue, 14 Apr 2020 21:38:57 +0300 Subject: [PATCH 009/195] fixed wrong customer group that assign to order --- .../Frontend/Quote/Address/CollectTotalsObserver.php | 4 +--- .../Frontend/Quote/Address/CollectTotalsObserverTest.php | 9 +-------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index a1228903e2323..b2004c60261fd 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -119,9 +119,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) $groupId = null; if (empty($customerVatNumber) || false == $this->customerVat->isCountryInEU($customerCountryCode)) { - $groupId = $customer->getId() ? $this->groupManagement->getDefaultGroup( - $storeId - )->getId() : $this->groupManagement->getNotLoggedInGroup()->getId(); + $groupId = $customer->getId() ? $quote->getCustomerGroupId() : $this->groupManagement->getNotLoggedInGroup()->getId(); } else { // Magento always has to emulate group even if customer uses default billing/shipping address $groupId = $this->customerVat->getCustomerGroupIdBasedOnVatNumber( diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index a590c8aa891a5..03ae2c12df07c 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -243,26 +243,19 @@ public function testDispatchWithDefaultCustomerGroupId() ->will($this->returnValue('customerCountryCode')); $this->quoteAddressMock->expects($this->once())->method('getVatId')->will($this->returnValue(null)); - $this->quoteMock->expects($this->once()) + $this->quoteMock->expects($this->exactly(2)) ->method('getCustomerGroupId') ->will($this->returnValue('customerGroupId')); $this->customerMock->expects($this->once())->method('getId')->will($this->returnValue('1')); - $this->groupManagementMock->expects($this->once()) - ->method('getDefaultGroup') - ->will($this->returnValue($this->groupInterfaceMock)); - $this->groupInterfaceMock->expects($this->once()) - ->method('getId')->will($this->returnValue('defaultCustomerGroupId')); /** Assertions */ $this->quoteAddressMock->expects($this->once()) ->method('setPrevQuoteCustomerGroupId') ->with('customerGroupId'); - $this->quoteMock->expects($this->once())->method('setCustomerGroupId')->with('defaultCustomerGroupId'); $this->customerDataFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->customerMock); $this->quoteMock->expects($this->once())->method('setCustomer')->with($this->customerMock); - /** SUT execution */ $this->model->execute($this->observerMock); } From 24793f9d90913df2489daffe7cce773c138a684e Mon Sep 17 00:00:00 2001 From: "v.prokopov" Date: Tue, 14 Apr 2020 21:44:07 +0300 Subject: [PATCH 010/195] refactored unit test class --- .../Address/CollectTotalsObserverTest.php | 85 ++++++++++++------- 1 file changed, 52 insertions(+), 33 deletions(-) diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index 03ae2c12df07c..4718119adc714 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -6,85 +6,104 @@ namespace Magento\Quote\Test\Unit\Observer\Frontend\Quote\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Customer\Api\Data\GroupInterface; +use Magento\Customer\Api\GroupManagementInterface; +use Magento\Customer\Model\Session; +use Magento\Customer\Model\Vat; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; +use Magento\Quote\Api\Data\ShippingInterface; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver; +use Magento\Quote\Observer\Frontend\Quote\Address\VatValidator; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + /** * Class CollectTotalsTest * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CollectTotalsObserverTest extends \PHPUnit\Framework\TestCase +class CollectTotalsObserverTest extends TestCase { /** - * @var \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver + * @var CollectTotalsObserver */ protected $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerAddressMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerSession; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerVatMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $addressRepository; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $quoteAddressMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $quoteMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $storeId; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $vatValidatorMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $observerMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $customerDataFactoryMock; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var ObjectManager */ protected $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $groupManagementMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var MockObject */ protected $groupInterfaceMock; @@ -93,10 +112,10 @@ class CollectTotalsObserverTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManager($this); $this->storeId = 1; $this->customerMock = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\CustomerInterface::class, + CustomerInterface::class, [], '', false, @@ -105,29 +124,29 @@ protected function setUp() ['getStoreId', 'getCustomAttribute', 'getId', '__wakeup'] ); $this->customerAddressMock = $this->createMock(\Magento\Customer\Helper\Address::class); - $this->customerVatMock = $this->createMock(\Magento\Customer\Model\Vat::class); + $this->customerVatMock = $this->createMock(Vat::class); $this->customerDataFactoryMock = $this->createPartialMock( - \Magento\Customer\Api\Data\CustomerInterfaceFactory::class, + CustomerInterfaceFactory::class, ['mergeDataObjectWithArray', 'create'] ); - $this->vatValidatorMock = $this->createMock(\Magento\Quote\Observer\Frontend\Quote\Address\VatValidator::class); + $this->vatValidatorMock = $this->createMock(VatValidator::class); $this->observerMock = $this->createPartialMock( - \Magento\Framework\Event\Observer::class, + Observer::class, ['getShippingAssignment', 'getQuote'] ); $this->quoteAddressMock = $this->createPartialMock( - \Magento\Quote\Model\Quote\Address::class, + Address::class, ['getCountryId', 'getVatId', 'getQuote', 'setPrevQuoteCustomerGroupId', '__wakeup'] ); $this->quoteMock = $this->createPartialMock( - \Magento\Quote\Model\Quote::class, + Quote::class, ['setCustomerGroupId', 'getCustomerGroupId', 'getCustomer', '__wakeup', 'setCustomer'] ); $this->groupManagementMock = $this->getMockForAbstractClass( - \Magento\Customer\Api\GroupManagementInterface::class, + GroupManagementInterface::class, [], '', false, @@ -140,7 +159,7 @@ protected function setUp() ); $this->groupInterfaceMock = $this->getMockForAbstractClass( - \Magento\Customer\Api\Data\GroupInterface::class, + GroupInterface::class, [], '', false, @@ -149,8 +168,8 @@ protected function setUp() ['getId'] ); - $shippingAssignmentMock = $this->createMock(\Magento\Quote\Api\Data\ShippingAssignmentInterface::class); - $shippingMock = $this->createMock(\Magento\Quote\Api\Data\ShippingInterface::class); + $shippingAssignmentMock = $this->createMock(ShippingAssignmentInterface::class); + $shippingMock = $this->createMock(ShippingInterface::class); $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); $shippingMock->expects($this->once())->method('getAddress')->willReturn($this->quoteAddressMock); @@ -163,14 +182,14 @@ protected function setUp() ->method('getCustomer') ->will($this->returnValue($this->customerMock)); - $this->addressRepository = $this->createMock(\Magento\Customer\Api\AddressRepositoryInterface::class); - $this->customerSession = $this->getMockBuilder(\Magento\Customer\Model\Session::class) + $this->addressRepository = $this->createMock(AddressRepositoryInterface::class); + $this->customerSession = $this->getMockBuilder(Session::class) ->disableOriginalConstructor() ->getMock(); $this->customerMock->expects($this->any())->method('getStoreId')->will($this->returnValue($this->storeId)); - $this->model = new \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver( + $this->model = new CollectTotalsObserver( $this->customerAddressMock, $this->customerVatMock, $this->vatValidatorMock, @@ -313,7 +332,7 @@ public function testDispatchWithAddressCustomerVatIdAndCountryId() $customerVat = "123123123"; $defaultShipping = 1; - $customerAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $customerAddress = $this->createMock(Address::class); $customerAddress->expects($this->any()) ->method("getVatId") ->willReturn($customerVat); @@ -350,7 +369,7 @@ public function testDispatchWithEmptyShippingAddress() $customerVat = "123123123"; $defaultShipping = 1; - $customerAddress = $this->createMock(\Magento\Customer\Api\Data\AddressInterface::class); + $customerAddress = $this->createMock(AddressInterface::class); $customerAddress->expects($this->once()) ->method("getCountryId") ->willReturn($customerCountryCode); From 8f1020af9f5cca737bb24696769f7eb81958556a Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Wed, 15 Apr 2020 19:39:19 +0200 Subject: [PATCH 011/195] Fix testing. --- .../Unit/Block/Product/View/Type/ConfigurableTest.php | 9 +++++++++ .../Product/Type/Configurable/Variations/PricesTest.php | 7 +++++-- .../Block/Product/Renderer/Configurable/PriceTest.php | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index 36fda4ef3245c..7fd66541adc7b 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -323,6 +323,9 @@ public function testGetJsonConfig() ->with($priceInfoMock) ->willReturn( [ + 'baseOldPrice' => [ + 'amount' => $amount, + ], 'oldPrice' => [ 'amount' => $amount, ], @@ -362,6 +365,9 @@ private function getExpectedArray($productId, $amount, $priceQty, $percentage): 'currencyFormat' => '%s', 'optionPrices' => [ $productId => [ + 'baseOldPrice' => [ + 'amount' => $amount, + ], 'oldPrice' => [ 'amount' => $amount, ], @@ -385,6 +391,9 @@ private function getExpectedArray($productId, $amount, $priceQty, $percentage): ], 'priceFormat' => [], 'prices' => [ + 'baseOldPrice' => [ + 'amount' => $amount, + ], 'oldPrice' => [ 'amount' => $amount, ], diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php index b4e7689498fe6..cab1e8f45e659 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php @@ -33,6 +33,9 @@ protected function setUp() public function testGetFormattedPrices() { $expected = [ + 'baseOldPrice' => [ + 'amount' => 1000 + ], 'oldPrice' => [ 'amount' => 500 ], @@ -54,8 +57,8 @@ public function testGetFormattedPrices() $this->localeFormatMock->expects($this->atLeastOnce()) ->method('getNumber') - ->withConsecutive([500], [1000], [500]) - ->will($this->onConsecutiveCalls(500, 1000, 500)); + ->withConsecutive([1000],[500], [1000], [500]) + ->will($this->onConsecutiveCalls(1000,500, 1000, 500)); $this->assertEquals($expected, $this->model->getFormattedPrices($priceInfoMock)); } diff --git a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php index 2c5bf805f92de..75834b8ba6c28 100644 --- a/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php +++ b/dev/tests/integration/testsuite/Magento/Swatches/Block/Product/Renderer/Configurable/PriceTest.php @@ -116,6 +116,7 @@ public function childProductsDataProvider(): array ], 'expected_data' => [ [ + 'baseOldPrice' => ['amount' => 150], 'oldPrice' => ['amount' => 150], 'basePrice' => ['amount' => 50], 'finalPrice' => ['amount' => 50], @@ -123,6 +124,7 @@ public function childProductsDataProvider(): array 'msrpPrice' => ['amount' => null], ], [ + 'baseOldPrice' => ['amount' => 150], 'oldPrice' => ['amount' => 150], 'basePrice' => ['amount' => 58.55], 'finalPrice' => ['amount' => 58.55], @@ -130,6 +132,7 @@ public function childProductsDataProvider(): array 'msrpPrice' => ['amount' => null], ], [ + 'baseOldPrice' => ['amount' => 150], 'oldPrice' => ['amount' => 150], 'basePrice' => ['amount' => 75], 'finalPrice' => ['amount' => 75], From cfb762d664a2da0933ee1a6fc46deb684fc6eecd Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Thu, 16 Apr 2020 09:33:04 +0200 Subject: [PATCH 012/195] Fix testing. --- .../Model/Product/Type/Configurable/Variations/PricesTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php index cab1e8f45e659..1e8821217f2b1 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php @@ -57,8 +57,8 @@ public function testGetFormattedPrices() $this->localeFormatMock->expects($this->atLeastOnce()) ->method('getNumber') - ->withConsecutive([1000],[500], [1000], [500]) - ->will($this->onConsecutiveCalls(1000,500, 1000, 500)); + ->withConsecutive([1000], [500], [1000], [500]) + ->will($this->onConsecutiveCalls(1000, 500, 1000, 500)); $this->assertEquals($expected, $this->model->getFormattedPrices($priceInfoMock)); } From c6a43f3920d29c046ca7a097e520f30f72f14ac7 Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Thu, 16 Apr 2020 14:39:01 +0200 Subject: [PATCH 013/195] New Testing format like #27500 --- .../Product/View/Type/ConfigurableTest.php | 52 +++++++++---------- .../Configurable/Variations/PricesTest.php | 6 +-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index 7fd66541adc7b..9ab288030cdd0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -15,79 +15,79 @@ class ConfigurableTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Block\Product\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Block\Product\Context|\PHPUnit\Framework\MockObject\MockObject */ private $context; /** - * @var \Magento\Framework\Stdlib\ArrayUtils|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Stdlib\ArrayUtils|\PHPUnit\Framework\MockObject\MockObject */ private $arrayUtils; /** - * @var \Magento\Framework\Json\EncoderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Json\EncoderInterface|\PHPUnit\Framework\MockObject\MockObject */ private $jsonEncoder; /** - * @var \Magento\ConfigurableProduct\Helper\Data|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\ConfigurableProduct\Helper\Data|\PHPUnit\Framework\MockObject\MockObject */ private $helper; /** - * @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Helper\Product|\PHPUnit\Framework\MockObject\MockObject */ private $product; /** - * @var \Magento\Customer\Helper\Session\CurrentCustomer|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Customer\Helper\Session\CurrentCustomer|\PHPUnit\Framework\MockObject\MockObject */ private $currentCustomer; /** - * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit\Framework\MockObject\MockObject */ private $priceCurrency; /** - * @var \Magento\Directory\Model\Currency|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Directory\Model\Currency|\PHPUnit\Framework\MockObject\MockObject */ private $currency; /** - * @var \Magento\ConfigurableProduct\Model\ConfigurableAttributeData|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\ConfigurableProduct\Model\ConfigurableAttributeData|\PHPUnit\Framework\MockObject\MockObject */ private $configurableAttributeData; /** - * @var \Magento\Framework\Locale\Format|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Locale\Format|\PHPUnit\Framework\MockObject\MockObject */ private $localeFormat; /** - * @var \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\ConfigurableProduct\Block\Product\View\Type\Configurable|\PHPUnit\Framework\MockObject\MockObject */ private $block; /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit\Framework\MockObject\MockObject */ private $storeManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ private $customerSession; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ private $variationPricesMock; /** * {@inheritDoc} */ - protected function setUp() + protected function setUp(): void { $this->mockContextObject(); @@ -230,7 +230,7 @@ public function cacheKeyProvider(): array * @param string|null $priceCurrency * @param string|null $customerGroupId */ - public function testGetCacheKeyInfo(array $expected, string $priceCurrency = null, string $customerGroupId = null) + public function testGetCacheKeyInfo(array $expected, string $priceCurrency = null, string $customerGroupId = null): void { $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->setMethods(['getCurrentCurrency']) @@ -258,7 +258,7 @@ public function testGetCacheKeyInfo(array $expected, string $priceCurrency = nul /** * Check that getJsonConfig() method returns expected value */ - public function testGetJsonConfig() + public function testGetJsonConfig(): void { $productId = 1; $amount = 10.50; @@ -416,10 +416,10 @@ private function getExpectedArray($productId, $amount, $priceQty, $percentage): /** * Retrieve mocks of \Magento\ConfigurableProduct\Model\Product\Type\Configurable object * - * @param \PHPUnit_Framework_MockObject_MockObject $productMock - * @return \PHPUnit_Framework_MockObject_MockObject + * @param \PHPUnit\Framework\MockObject\MockObject $productMock + * @return \PHPUnit\Framework\MockObject\MockObject */ - private function getProductTypeMock(\PHPUnit_Framework_MockObject_MockObject $productMock) + private function getProductTypeMock(\PHPUnit\Framework\MockObject\MockObject $productMock): \PHPUnit\Framework\MockObject\MockObject { $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) ->disableOriginalConstructor() @@ -459,7 +459,7 @@ private function getProductTypeMock(\PHPUnit_Framework_MockObject_MockObject $pr * * @return void */ - protected function mockContextObject() + protected function mockContextObject(): void { $this->storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) ->getMockForAbstractClass(); @@ -476,9 +476,9 @@ protected function mockContextObject() * Retrieve mock of \Magento\Framework\Pricing\Amount\AmountInterface object * * @param float $amount - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \Magento\Framework\Pricing\Amount\AmountInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected function getAmountMock($amount): \PHPUnit_Framework_MockObject_MockObject + protected function getAmountMock($amount): \PHPUnit\Framework\MockObject\MockObject { $amountMock = $this->getMockBuilder(\Magento\Framework\Pricing\Amount\AmountInterface::class) ->setMethods(['getValue', 'getBaseAmount']) @@ -496,12 +496,12 @@ protected function getAmountMock($amount): \PHPUnit_Framework_MockObject_MockObj /** * Retrieve mock of \Magento\Catalog\Pricing\Price\TierPriceInterface object * - * @param \PHPUnit_Framework_MockObject_MockObject $amountMock + * @param \PHPUnit\Framework\MockObject\MockObject $amountMock * @param float $priceQty * @param int $percentage - * @return \PHPUnit_Framework_MockObject_MockObject + * @return \PHPUnit\Framework\MockObject\MockObject */ - protected function getTierPriceMock(\PHPUnit_Framework_MockObject_MockObject $amountMock, $priceQty, $percentage) + protected function getTierPriceMock(\PHPUnit_Framework_MockObject_MockObject $amountMock, $priceQty, $percentage): \PHPUnit\Framework\MockObject\MockObject { $tierPrice = [ 'price_qty' => $priceQty, diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php index 1e8821217f2b1..c91c681d0458e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/Configurable/Variations/PricesTest.php @@ -13,7 +13,7 @@ class PricesTest extends TestCase { /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \PHPUnit\Framework\MockObject\MockObject */ private $localeFormatMock; @@ -22,7 +22,7 @@ class PricesTest extends TestCase */ private $model; - protected function setUp() + protected function setUp(): void { $this->localeFormatMock = $this->createMock(\Magento\Framework\Locale\Format::class); $this->model = new \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Variations\Prices( @@ -30,7 +30,7 @@ protected function setUp() ); } - public function testGetFormattedPrices() + public function testGetFormattedPrices(): void { $expected = [ 'baseOldPrice' => [ From 84124f74b55a88b09947f1fc561a4ad30ecdd07b Mon Sep 17 00:00:00 2001 From: Jack Krielen Date: Thu, 16 Apr 2020 16:16:17 +0200 Subject: [PATCH 014/195] Code Styling, Fixing --- .../Product/View/Type/ConfigurableTest.php | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index 9ab288030cdd0..475da7eb00daa 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -230,8 +230,11 @@ public function cacheKeyProvider(): array * @param string|null $priceCurrency * @param string|null $customerGroupId */ - public function testGetCacheKeyInfo(array $expected, string $priceCurrency = null, string $customerGroupId = null): void - { + public function testGetCacheKeyInfo( + array $expected, + string $priceCurrency = null, + string $customerGroupId = null + ): void { $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); @@ -385,7 +388,7 @@ private function getExpectedArray($productId, $amount, $priceQty, $percentage): ], ], 'msrpPrice' => [ - 'amount' => null , + 'amount' => null, ] ], ], @@ -419,8 +422,9 @@ private function getExpectedArray($productId, $amount, $priceQty, $percentage): * @param \PHPUnit\Framework\MockObject\MockObject $productMock * @return \PHPUnit\Framework\MockObject\MockObject */ - private function getProductTypeMock(\PHPUnit\Framework\MockObject\MockObject $productMock): \PHPUnit\Framework\MockObject\MockObject - { + private function getProductTypeMock( + \PHPUnit\Framework\MockObject\MockObject $productMock + ): \PHPUnit\Framework\MockObject\MockObject { $currencyMock = $this->getMockBuilder(\Magento\Directory\Model\Currency::class) ->disableOriginalConstructor() ->getMock(); @@ -501,8 +505,11 @@ protected function getAmountMock($amount): \PHPUnit\Framework\MockObject\MockObj * @param int $percentage * @return \PHPUnit\Framework\MockObject\MockObject */ - protected function getTierPriceMock(\PHPUnit_Framework_MockObject_MockObject $amountMock, $priceQty, $percentage): \PHPUnit\Framework\MockObject\MockObject - { + protected function getTierPriceMock( + \PHPUnit\Framework\MockObject\MockObject $amountMock, + $priceQty, + $percentage + ): \PHPUnit\Framework\MockObject\MockObject { $tierPrice = [ 'price_qty' => $priceQty, 'price' => $amountMock, From 6ba9fffec8cb4d9a97c96205ba732eaf7865185c Mon Sep 17 00:00:00 2001 From: Mohamed-Asar Date: Sun, 19 Apr 2020 10:46:11 +0530 Subject: [PATCH 015/195] Bug fix ui select list visible eventhough it is disabled --- app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js index b488a4b2f8c16..d1794c44bfdf9 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/ui-select.js @@ -661,7 +661,7 @@ define([ * @returns {Object} Chainable */ toggleListVisible: function () { - this.listVisible(!this.listVisible()); + this.listVisible(!this.disabled() && !this.listVisible()); return this; }, From a6cf5cb8901671db758391d7a099c6ed630f5944 Mon Sep 17 00:00:00 2001 From: "v.prokopov" Date: Mon, 20 Apr 2020 19:24:25 +0300 Subject: [PATCH 016/195] fixed too long string --- .../Observer/Frontend/Quote/Address/CollectTotalsObserver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index b2004c60261fd..c19dbc2c429ae 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -119,7 +119,8 @@ public function execute(\Magento\Framework\Event\Observer $observer) $groupId = null; if (empty($customerVatNumber) || false == $this->customerVat->isCountryInEU($customerCountryCode)) { - $groupId = $customer->getId() ? $quote->getCustomerGroupId() : $this->groupManagement->getNotLoggedInGroup()->getId(); + $groupId = $customer->getId() ? $quote->getCustomerGroupId() : + $this->groupManagement->getNotLoggedInGroup()->getId(); } else { // Magento always has to emulate group even if customer uses default billing/shipping address $groupId = $this->customerVat->getCustomerGroupIdBasedOnVatNumber( From c38d1eff47da68c410eacd92e32df67a0ab80ecf Mon Sep 17 00:00:00 2001 From: "v.prokopov" Date: Sat, 23 May 2020 23:01:18 +0300 Subject: [PATCH 017/195] fixed integration test --- .../Frontend/Quote/Address/CollectTotalsObserverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index 828dac514427d..27e4d2c9b876d 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -133,6 +133,6 @@ public function testChangeQuoteCustomerGroupIdForCustomerWithEnabledAutomaticGro ); $this->model->execute($eventObserver); - $this->assertEquals(1, $quote->getCustomer()->getGroupId()); + $this->assertEquals(2, $quote->getCustomer()->getGroupId()); } } From 674324c6ae3cdee5c664bac6be3e275070bda758 Mon Sep 17 00:00:00 2001 From: abdarrahman abouzaid Date: Thu, 11 Jun 2020 15:21:11 +0200 Subject: [PATCH 018/195] clear errors when apply new filters --- app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js index fe33389eabad4..848ad60219a2b 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/filters/filters.js @@ -200,6 +200,7 @@ define([ * @returns {Filters} Chainable. */ apply: function () { + $('body').notification('clear'); this.set('applied', removeEmpty(this.filters)); return this; From 8974e16d98b6154c64b5de6288d3d7ac7cc7291e Mon Sep 17 00:00:00 2001 From: syed Date: Thu, 11 Jun 2020 20:53:46 +0530 Subject: [PATCH 019/195] #28431 issue fixed --- app/code/Magento/Bundle/Model/Product/LinksList.php | 6 +++++- .../Bundle/Test/Unit/Model/Product/LinksListTest.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/Model/Product/LinksList.php b/app/code/Magento/Bundle/Model/Product/LinksList.php index aeb71d0e434ab..3202b22c1d2a4 100644 --- a/app/code/Magento/Bundle/Model/Product/LinksList.php +++ b/app/code/Magento/Bundle/Model/Product/LinksList.php @@ -50,8 +50,12 @@ public function getItems(\Magento\Catalog\Api\Data\ProductInterface $product, $o $productLinks = []; /** @var \Magento\Catalog\Model\Product $selection */ foreach ($selectionCollection as $selection) { + $bundledProductPrice = $selection->getSelectionPriceValue(); + if ($bundledProductPrice <= 0){ + $bundledProductPrice = $selection->getPrice(); + } $selectionPriceType = $product->getPriceType() ? $selection->getSelectionPriceType() : null; - $selectionPrice = $product->getPriceType() ? $selection->getSelectionPriceValue() : null; + $selectionPrice = $bundledProductPrice ? $bundledProductPrice : null; /** @var \Magento\Bundle\Api\Data\LinkInterface $productLink */ $productLink = $this->linkFactory->create(); diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/LinksListTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/LinksListTest.php index 6ac3cc11957a6..397d1babe05ba 100644 --- a/app/code/Magento/Bundle/Test/Unit/Model/Product/LinksListTest.php +++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/LinksListTest.php @@ -77,7 +77,7 @@ public function testLinksList() ->method('getSelectionsCollection') ->with([$optionId], $this->productMock) ->willReturn([$this->selectionMock]); - $this->productMock->expects($this->exactly(2))->method('getPriceType')->willReturn('price_type'); + $this->productMock->expects($this->once())->method('getPriceType')->willReturn('price_type'); $this->selectionMock->expects($this->once()) ->method('getSelectionPriceType') ->willReturn('selection_price_type'); From e962d7c44e3d754ef8de4582d9e636918384b276 Mon Sep 17 00:00:00 2001 From: syed Date: Fri, 12 Jun 2020 16:13:35 +0530 Subject: [PATCH 020/195] Fixed coding standards issues --- app/code/Magento/Bundle/Model/Product/LinksList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Model/Product/LinksList.php b/app/code/Magento/Bundle/Model/Product/LinksList.php index 3202b22c1d2a4..f9e00de1b1737 100644 --- a/app/code/Magento/Bundle/Model/Product/LinksList.php +++ b/app/code/Magento/Bundle/Model/Product/LinksList.php @@ -51,7 +51,7 @@ public function getItems(\Magento\Catalog\Api\Data\ProductInterface $product, $o /** @var \Magento\Catalog\Model\Product $selection */ foreach ($selectionCollection as $selection) { $bundledProductPrice = $selection->getSelectionPriceValue(); - if ($bundledProductPrice <= 0){ + if ($bundledProductPrice <= 0) { $bundledProductPrice = $selection->getPrice(); } $selectionPriceType = $product->getPriceType() ? $selection->getSelectionPriceType() : null; From 2178617d083e9687f46df627ff4d913d63da99d7 Mon Sep 17 00:00:00 2001 From: syed Date: Fri, 12 Jun 2020 16:15:28 +0530 Subject: [PATCH 021/195] Added shot description --- app/code/Magento/Bundle/Model/Product/LinksList.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/Bundle/Model/Product/LinksList.php b/app/code/Magento/Bundle/Model/Product/LinksList.php index f9e00de1b1737..e3bf749ec5f31 100644 --- a/app/code/Magento/Bundle/Model/Product/LinksList.php +++ b/app/code/Magento/Bundle/Model/Product/LinksList.php @@ -39,6 +39,8 @@ public function __construct( } /** + * Bundle Product Items Data + * * @param \Magento\Catalog\Api\Data\ProductInterface $product * @param int $optionId * @return \Magento\Bundle\Api\Data\LinkInterface[] From 7f4eccbb79f0a51f976d92c23c29254df8d2dde8 Mon Sep 17 00:00:00 2001 From: syed Date: Fri, 12 Jun 2020 17:21:29 +0530 Subject: [PATCH 022/195] Fixed PHPcs issues --- app/code/Magento/Bundle/Model/Product/LinksList.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Model/Product/LinksList.php b/app/code/Magento/Bundle/Model/Product/LinksList.php index e3bf749ec5f31..c35d475e04d84 100644 --- a/app/code/Magento/Bundle/Model/Product/LinksList.php +++ b/app/code/Magento/Bundle/Model/Product/LinksList.php @@ -40,7 +40,7 @@ public function __construct( /** * Bundle Product Items Data - * + * * @param \Magento\Catalog\Api\Data\ProductInterface $product * @param int $optionId * @return \Magento\Bundle\Api\Data\LinkInterface[] From cae2258c432e3c136e36480891ca0d21558d1b11 Mon Sep 17 00:00:00 2001 From: Alexander Steshuk Date: Wed, 17 Jun 2020 17:18:58 +0300 Subject: [PATCH 023/195] MFTF test --- ...tCustomerCheckoutWithCustomerGroupTest.xml | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml new file mode 100644 index 0000000000000..53d50b8ea8bf9 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml @@ -0,0 +1,89 @@ + + + + + + + + + <description value="Should be assign Customer Group to Order, when enabled setting fir Customer - Auto Group Assign"/> + <severity value="MAJOR"/> + <group value="checkout"/> + <group value="customer"/> + </annotations> + <before> + + <magentoCLI command="config:set customer/create_account/auto_group_assign 1" stepKey="enableAutoGroupAssign"/> + + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <actionGroup ref="AdminUpdateCustomerGroupByEmailActionGroup" stepKey="updateCustomerGroup"> + <argument name="emailAddress" value="$$createCustomer.email$$"/> + <argument name="customerGroup" value="Retail"/> + </actionGroup> + + </before> + <after> + <magentoCLI command="config:set customer/create_account/auto_group_assign 0" stepKey="disableAutoGroupAssign"/> + + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteUsCustomer"/> + <actionGroup ref="AdminClearCustomersFiltersActionGroup" stepKey="resetCustomerFilters"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteSimpleCategory"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="storefrontCustomerLogin"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + + <waitForPageLoad stepKey="waitForCatalogPageLoad"/> + + <actionGroup ref="StorefrontAddCategoryProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="productCount" value="CONST.one"/> + </actionGroup> + + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart"/> + <actionGroup ref="CheckoutSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRate"/> + <actionGroup ref="StorefrontCheckoutForwardFromShippingStepActionGroup" stepKey="goToReview"/> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrder"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"> + <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> + </actionGroup> + + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="orderNumber"/> + + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="addFilterToGridAndOpenOrder"> + <argument name="orderId" value="{$orderNumber}"/> + </actionGroup> + + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="Pending" stepKey="verifyOrderStatus"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="Customer" stepKey="verifyAccountInformation"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="$$createCustomer.email$$" stepKey="verifyCustomerEmail"/> + <see selector="{{AdminOrderDetailsInformationSection.accountInformation}}" userInput="Retail" stepKey="verifyCustomerGroup"/> + <see selector="{{AdminOrderDetailsInformationSection.billingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyBillingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.shippingAddress}}" userInput="{{US_Address_TX.street[0]}}" stepKey="verifyShippingAddress"/> + <see selector="{{AdminOrderDetailsInformationSection.itemsOrdered}}" userInput="$$createSimpleProduct.name$$" stepKey="verifyProductName"/> + + </test> +</tests> From a57dbd6d203251a4dd93f5d3fb258d53d7464821 Mon Sep 17 00:00:00 2001 From: poraphit <porraphit@gmail.com> Date: Fri, 26 Jun 2020 10:37:42 +0700 Subject: [PATCH 024/195] Remove related products that not exist in map list. --- .../Model/Resolver/Batch/AbstractLikedProducts.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php index 7ad2e5dde2985..e14d8bde6be74 100644 --- a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php @@ -110,6 +110,10 @@ private function findRelations(array $products, array $loadAttributes, int $link //Matching products with related products. $relationsData = []; foreach ($relations as $productId => $relatedIds) { + //Remove related products that not exist in map list. + $relatedIds = array_filter($relatedIds, function ($relatedId) use ($relatedProducts) { + return isset($relatedProducts[$relatedId]); + }); $relationsData[$productId] = array_map( function ($id) use ($relatedProducts) { return $relatedProducts[$id]; From 7f0f260d3110a055ca1f8fa4886e766dd303b6c1 Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Mon, 6 Jul 2020 15:37:39 +0300 Subject: [PATCH 025/195] magento2/issues/12087: Changes for Widget class. --- app/code/Magento/Widget/Model/Widget.php | 39 ++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index d07e84186b2c9..bb6c0f22fefdf 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -304,6 +304,8 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) if ($name == 'conditions') { $name = 'conditions_encoded'; $value = $this->conditionsHelper->encode($value); + } elseif ($this->isTextType($widget, $name)) { + $value = $this->encodeReservedChars($value); } elseif (is_array($value)) { $value = implode(',', $value); } elseif (trim($value) == '') { @@ -456,4 +458,41 @@ protected function sortParameters($firstElement, $secondElement) $bOrder = (int)$secondElement->getData('sort_order'); return $aOrder < $bOrder ? -1 : ($aOrder > $bOrder ? 1 : 0); } + + /** + * @param $string + * @return string|string[] + */ + private function encodeReservedChars($string) + { + $map = [ + '{' => urlencode('{'), + '}' => urlencode('}') + ]; + + return str_replace( + array_keys($map), + array_values($map), + $string + ); + } + + /** + * @param $widget + * @param $name + * @return bool + */ + private function isTextType($widget, $name) + { + $parameters = $widget->getParameters(); + + if (isset($parameters[$name]) && is_object($parameters[$name])) { + $type = $parameters[$name]->getType(); + if ($type == 'text') { + return true; + } + } + + return false; + } } From 2a48abee8a17331fdea9d3ba4738e5394d73a13b Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Mon, 6 Jul 2020 16:07:05 +0300 Subject: [PATCH 026/195] magento2/issues/12087: Changes for Adminhtml Widget Options class. --- .../Widget/Block/Adminhtml/Widget/Options.php | 4 ++++ app/code/Magento/Widget/Model/Widget.php | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index 32bae10c801c8..87ae52b2eadda 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -158,6 +158,10 @@ protected function _addField($parameter) $data['value'] = $parameter->getValue(); } + if ($parameter->getType() == 'text' && $data['value'] != '') { + $data['value'] = $this->_widget->decodeReservedChars($data['value']); + } + //prepare unique id value if ($fieldName == 'unique_id' && $data['value'] == '') { $data['value'] = hash('sha256', microtime(1)); diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index bb6c0f22fefdf..12af534325224 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -477,6 +477,24 @@ private function encodeReservedChars($string) ); } + /** + * @param $string + * @return array + */ + public function decodeReservedChars($string) + { + $map = [ + '{' => urlencode('{'), + '}' => urlencode('}') + ]; + + return str_replace( + array_values($map), + array_keys($map), + $string + ); + } + /** * @param $widget * @param $name From cb0b3d407dc1ac5c42e202a515a2aee97574b57d Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Tue, 7 Jul 2020 11:01:36 +0300 Subject: [PATCH 027/195] magento2/issues/12087: Fix static test. --- app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php | 1 + app/code/Magento/Widget/Model/Widget.php | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index 87ae52b2eadda..44c43055df8b9 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -136,6 +136,7 @@ public function addFields() * @return \Magento\Framework\Data\Form\Element\AbstractElement * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _addField($parameter) { diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 12af534325224..c01d8b226197d 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -293,6 +293,7 @@ public function getWidgetsArray($filters = []) * @param array $params Pre-configured Widget Params * @param bool $asIs Return result as widget directive(true) or as placeholder image(false) * @return string Widget directive ready to parse + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function getWidgetDeclaration($type, $params = [], $asIs = true) { @@ -460,6 +461,8 @@ protected function sortParameters($firstElement, $secondElement) } /** + * Encode reserved chars + * * @param $string * @return string|string[] */ @@ -478,6 +481,8 @@ private function encodeReservedChars($string) } /** + * Decode reserved chars + * * @param $string * @return array */ @@ -496,6 +501,8 @@ public function decodeReservedChars($string) } /** + * Is text type Widget parameter + * * @param $widget * @param $name * @return bool From 1f84319d6905dc9cc862fd0cb1244489b44f5052 Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Tue, 7 Jul 2020 14:39:30 +0300 Subject: [PATCH 028/195] magento2/issues/12087: Fix static test. --- app/code/Magento/Widget/Model/Widget.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index c01d8b226197d..49450ff6e017e 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -131,6 +131,7 @@ public function getWidgetByClassType($type) */ public function getConfigAsXml($type) { + // phpstan:ignore return $this->getXmlElementByType($type); } @@ -463,7 +464,7 @@ protected function sortParameters($firstElement, $secondElement) /** * Encode reserved chars * - * @param $string + * @param string $string * @return string|string[] */ private function encodeReservedChars($string) @@ -483,7 +484,7 @@ private function encodeReservedChars($string) /** * Decode reserved chars * - * @param $string + * @param string $string * @return array */ public function decodeReservedChars($string) @@ -503,8 +504,8 @@ public function decodeReservedChars($string) /** * Is text type Widget parameter * - * @param $widget - * @param $name + * @param \Magento\Framework\DataObject $widget + * @param string $name * @return bool */ private function isTextType($widget, $name) From 37e5e156af6d976eec05e6318c3f4bd02830d6b6 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 26 Jul 2020 20:05:14 +0700 Subject: [PATCH 029/195] Redirect to "Currency Option Path" more accurate --- .../Block/Adminhtml/System/Currency.php | 9 ++++++- .../Block/Adminhtml/System/CurrencyTest.php | 25 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php b/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php index ec73ac0cf7aa5..4ac4542421d48 100644 --- a/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php +++ b/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php @@ -41,7 +41,14 @@ protected function _prepareLayout() ] ); - $onClick = "setLocation('" . $this->getUrl('adminhtml/system_config/edit/section/currency') . "')"; + $currencyOptionPath = $this->getUrl( + 'adminhtml/system_config/edit', + [ + 'section' => 'currency', + '_fragment' => 'currency_options-link' + ] + ); + $onClick = "setLocation('" . $currencyOptionPath . "')"; $this->getToolbar()->addChild( 'options_button', diff --git a/app/code/Magento/CurrencySymbol/Test/Unit/Block/Adminhtml/System/CurrencyTest.php b/app/code/Magento/CurrencySymbol/Test/Unit/Block/Adminhtml/System/CurrencyTest.php index aa7cd06666121..4b86df94b4556 100644 --- a/app/code/Magento/CurrencySymbol/Test/Unit/Block/Adminhtml/System/CurrencyTest.php +++ b/app/code/Magento/CurrencySymbol/Test/Unit/Block/Adminhtml/System/CurrencyTest.php @@ -7,15 +7,22 @@ namespace Magento\CurrencySymbol\Test\Unit\Block\Adminhtml\System; +use Magento\Backend\Block\Template\Context; use Magento\Backend\Block\Widget\Button; use Magento\CurrencySymbol\Block\Adminhtml\System\Currency; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Framework\View\Element\BlockInterface; use Magento\Framework\View\LayoutInterface; use PHPUnit\Framework\TestCase; +use Magento\Framework\UrlInterface; class CurrencyTest extends TestCase { + /** + * Stub currency option link url + */ + const STUB_OPTION_LINK_URL = 'https://localhost/admin/system_config/edit/section/currency#currency_options-link'; + /** * Object manager helper * @@ -70,12 +77,25 @@ public function testPrepareLayout() ] ); + $contextMock = $this->createMock(Context::class); + $urlBuilderMock = $this->createMock(UrlInterface::class); + + $contextMock->expects($this->once())->method('getUrlBuilder')->willReturn($urlBuilderMock); + + $urlBuilderMock->expects($this->once())->method('getUrl')->with( + 'adminhtml/system_config/edit', + [ + 'section' => 'currency', + '_fragment' => 'currency_options-link' + ] + )->willReturn(self::STUB_OPTION_LINK_URL); + $childBlockMock->expects($this->at(1)) ->method('addChild') ->with( 'options_button', Button::class, - ['label' => __('Options'), 'onclick' => 'setLocation(\'\')'] + ['label' => __('Options'), 'onclick' => 'setLocation(\''.self::STUB_OPTION_LINK_URL.'\')'] ); $childBlockMock->expects($this->at(2)) @@ -90,7 +110,8 @@ public function testPrepareLayout() $block = $this->objectManagerHelper->getObject( Currency::class, [ - 'layout' => $layoutMock + 'layout' => $layoutMock, + 'context' => $contextMock ] ); $block->setLayout($layoutMock); From dc8a387ba3b0a01e31045f03399014d88793e04d Mon Sep 17 00:00:00 2001 From: Eden Duong <quocviet312@gmail.com> Date: Sun, 26 Jul 2020 20:40:00 +0700 Subject: [PATCH 030/195] Update app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php Co-authored-by: Yaroslav Rogoza <enarc@atwix.com> --- .../Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php b/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php index 4ac4542421d48..9e7a2b69f20a5 100644 --- a/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php +++ b/app/code/Magento/CurrencySymbol/Block/Adminhtml/System/Currency.php @@ -48,7 +48,7 @@ protected function _prepareLayout() '_fragment' => 'currency_options-link' ] ); - $onClick = "setLocation('" . $currencyOptionPath . "')"; + $onClick = "setLocation('$currencyOptionPath')"; $this->getToolbar()->addChild( 'options_button', From ca09097a7cfa6006fa420218265826f656744685 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 26 Jul 2020 22:12:06 +0700 Subject: [PATCH 031/195] Resolve Can not export Coupon Code to CSV issue29277 --- .../Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php index 53459f2c3e52f..d1440a2b547a4 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsCsv.php @@ -15,13 +15,14 @@ use Magento\Framework\View\Result\Layout; use Magento\Framework\App\ResponseInterface; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; /** * Export Coupons to csv file * * Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsCsv */ -class ExportCouponsCsv extends Quote implements HttpGetActionInterface +class ExportCouponsCsv extends Quote implements HttpGetActionInterface, HttpPostActionInterface { /** * Export coupon codes as CSV file From c82e68f85c97423a41d28fd6b2ca540c0f31fd0c Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Sun, 26 Jul 2020 22:19:12 +0700 Subject: [PATCH 032/195] Resolve Can not export Coupon Code to CSV,XML issue29277 --- .../Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php index fa3d4455410c4..401d8aea1aded 100644 --- a/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php +++ b/app/code/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCouponsXml.php @@ -15,13 +15,14 @@ use Magento\Framework\View\Result\Layout; use Magento\Framework\App\ResponseInterface; use Magento\Framework\App\Action\HttpGetActionInterface; +use Magento\Framework\App\Action\HttpPostActionInterface; /** * Export coupons to xml file * * Class \Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCouponsXml */ -class ExportCouponsXml extends Quote implements HttpGetActionInterface +class ExportCouponsXml extends Quote implements HttpGetActionInterface, HttpPostActionInterface { /** * Export coupon codes as excel xml file From a9e6752a024fd6998b5a838cdf84cc2004d6dbf7 Mon Sep 17 00:00:00 2001 From: Alexander Steshuk <grp-engcom-vendorworker-Kilo@adobe.com> Date: Mon, 27 Jul 2020 15:50:30 +0300 Subject: [PATCH 033/195] Correct Static and Unit test --- .../Frontend/Quote/Address/CollectTotalsObserverTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index 4eced46a2ee2e..ae2a4734215ad 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -150,7 +150,6 @@ protected function setUp(): void ->disableOriginalConstructor() ->getMock(); - $this->groupManagementMock = $this->getMockForAbstractClass( GroupManagementInterface::class, [], @@ -272,11 +271,6 @@ public function testDispatchWithDefaultCustomerGroupId() ->method('getCustomerGroupId') ->willReturn('customerGroupId'); $this->customerMock->expects($this->once())->method('getId')->willReturn('1'); - $this->groupManagementMock->expects($this->once()) - ->method('getDefaultGroup') - ->willReturn($this->groupInterfaceMock); - $this->groupInterfaceMock->expects($this->once()) - ->method('getId')->willReturn('defaultCustomerGroupId'); /** Assertions */ $this->quoteAddressMock->expects($this->once()) From e04514e48d089a4383064aa04d97b9c69d3f5c26 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Tue, 28 Jul 2020 21:28:02 +0700 Subject: [PATCH 034/195] Integration Test for Can not export Coupon Code to CSV,XML issue29277 --- .../ExportCoupons/ExportCouponsController.php | 87 +++++++++++++++++++ .../ExportCoupons/ExportCouponsCsvTest.php | 37 ++++++++ .../ExportCoupons/ExportCouponsXmlTest.php | 37 ++++++++ 3 files changed, 161 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php create mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php new file mode 100644 index 0000000000000..315860ea6973e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons; + +use Magento\Framework\App\ResourceConnection; +use Magento\SalesRule\Model\ResourceModel\Rule\Collection as RuleCollection; +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Abstract controller for test export coupon + */ +abstract class ExportCouponsController extends AbstractBackendController +{ + /** + * @var string + */ + protected $resource = 'Magento_SalesRule::quote'; + + /** + * @var Rule + */ + private $salesRule; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class); + $this->initSalesRule(); + } + + /** + * Prepare request + * + * @return void + */ + protected function prepareRequest(): void + { + $couponList = $this ->getCouponsIdList(); + if (count($couponList)) { + $this->getRequest()->setParams(['internal_ids' => $couponList[0]])->setMethod('POST'); + } + } + + /** + * Init current sales rule + * + * @return void + */ + private function initSalesRule(): void + { + /** @var RuleCollection $collection */ + $collection = Bootstrap::getObjectManager()->create(RuleCollection::class); + $collection->addFieldToFilter('name', 'Rule with coupon list'); + $this->salesRule = $collection->getFirstItem(); + } + + /** + * Retrieve id list of coupons + * + * @return array + */ + private function getCouponsIdList(): array + { + $select = $this->resourceConnection->getConnection() + ->select() + ->from($this->resourceConnection->getTableName('salesrule_coupon')) + ->columns(['coupon_id']) + ->where('rule_id=?', $this->salesRule->getId()); + + return $this->resourceConnection->getConnection()->fetchCol($select); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php new file mode 100644 index 0000000000000..50bf5017d75ac --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; + +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons\ExportCouponsController; + +/** + * Test export coupon csv + * + * Verify export csv + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/SalesRule/_files/cart_rule_with_coupon_list.php + */ +class ExportCouponsCsvTest extends ExportCouponsController +{ + /** + * @var string + */ + protected $uri = 'backend/sales_rule/promo_quote/exportCouponsCsv'; + + /** + * Test export csv + * + * @return void + */ + public function testExportCsv(): void + { + $this->prepareRequest(); + $this->dispatch($this->uri); + $this->assertStringNotContainsString('404 Error', $this->getResponse()->getBody()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php new file mode 100644 index 0000000000000..d57a0d78deb5d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; + +use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons\ExportCouponsController; + +/** + * Test export coupon xml + * + * Verify export xml + * @magentoAppArea adminhtml + * @magentoDataFixture Magento/SalesRule/_files/cart_rule_with_coupon_list.php + */ +class ExportCouponsXmlTest extends ExportCouponsController +{ + /** + * @var string + */ + protected $uri = 'backend/sales_rule/promo_quote/exportCouponsXml'; + + /** + * Test export xml + * + * @return void + */ + public function testExportCsv(): void + { + $this->prepareRequest(); + $this->dispatch($this->uri); + $this->assertStringNotContainsString('404 Error', $this->getResponse()->getBody()); + } +} From af02e482bf45c9982990b71738a3fb693f3bfba2 Mon Sep 17 00:00:00 2001 From: Eden <eden@magestore.com> Date: Wed, 29 Jul 2020 08:05:17 +0700 Subject: [PATCH 035/195] Fix static test for Integration Test for Can not export Coupon Code to CSV,XML issue29277 --- .../Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php | 2 +- .../Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php index 50bf5017d75ac..04d6ad570a883 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons; use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons\ExportCouponsController; diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php index d57a0d78deb5d..3484a6676d82e 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php @@ -5,7 +5,7 @@ */ declare(strict_types=1); -namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote; +namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons; use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons\ExportCouponsController; From 4f2d8de0f36132cab835e5af3bc88d461e87967f Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Thu, 6 Aug 2020 11:47:05 +0530 Subject: [PATCH 036/195] Added sticky in lac banner --- .../frontend/templates/html/notices.phtml | 4 +- .../view/frontend/web/css/source/_module.less | 64 ++++++++++--------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml b/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml index aa64e78aa234f..b2e0aaf20ce34 100644 --- a/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml +++ b/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/templates/html/notices.phtml @@ -11,7 +11,9 @@ $viewFileUrl = $block->getViewFileUrl('Magento_LoginAsCustomerFrontendUi::images/magento-icon.svg'); ?> <?php if ($block->getConfig()->isEnabled()): ?> - <div data-bind="scope: 'loginAsCustomer'" > + <div class="lac-notification-sticky" + data-mage-init='{"sticky":{"container": "body"}}' + data-bind="scope: 'loginAsCustomer'" > <div class="lac-notification clearfix" data-bind="visible: isVisible" style="display: none"> <div class="top-container"> <div class="lac-notification-icon wrapper"> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/web/css/source/_module.less b/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/web/css/source/_module.less index d630ff06c3e34..c42f5143b4fda 100644 --- a/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/web/css/source/_module.less +++ b/app/code/Magento/LoginAsCustomerFrontendUi/view/frontend/web/css/source/_module.less @@ -16,43 +16,47 @@ // --------------------------------------------- & when (@media-common = true) { - .lac-notification { - background-color: @lac-notification-background-color; - color: @lac-notification-color; - font-size: 16px; + .lac-notification-sticky { + position: relative; + z-index: 999; + .lac-notification { + background-color: @lac-notification-background-color; + color: @lac-notification-color; + font-size: 16px; - .lac-notification-icon { - float: left; - margin: 10px 25px 10px 10px; + .lac-notification-icon { + float: left; + margin: 10px 25px 10px 10px; - .logo-img { - display: block + .logo-img { + display: block + } } - } - .lac-notification-text { - float: left; - padding: 15px 0; - } + .lac-notification-text { + float: left; + padding: 15px 0; + } - .lac-notification-links { - float: right; - padding: 15px 0; + .lac-notification-links { + float: right; + padding: 15px 0; - a { - color: @lac-notification-links-color; - font-size: 14px; - } + a { + color: @lac-notification-links-color; + font-size: 14px; + } - .lac-notification-close-link { - &:after { - background: url('../Magento_LoginAsCustomerFrontendUi/images/close.svg'); - content: ' '; - display: inline-block; - height: 12px; - margin-left: 5px; - vertical-align: middle; - width: 12px; + .lac-notification-close-link { + &:after { + background: url('../Magento_LoginAsCustomerFrontendUi/images/close.svg'); + content: ' '; + display: inline-block; + height: 12px; + margin-left: 5px; + vertical-align: middle; + width: 12px; + } } } } From 43cca51a6d361c73bfebcbeeebf2430e345b5157 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Thu, 6 Aug 2020 16:20:05 +0530 Subject: [PATCH 037/195] MFTF test added --- ...oginAsCustomerBannerVisibleActionGroup.xml | 20 +++++++++++ .../StorefrontLoginToCustomerActionGroup.xml | 22 ++++++++++++ ...orefrontCustomerLoginAsCustomerSection.xml | 15 ++++++++ .../StorefrontLoginAsCustomerBannerTest.xml | 35 +++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml create mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml create mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml create mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml new file mode 100644 index 0000000000000..c37cf089398dc --- /dev/null +++ b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml @@ -0,0 +1,20 @@ +<?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="StorefrontAssertLoginAsCustomerBannerVisibleActionGroup"> + <annotations> + <description>Assert Login As Customer Banner is sticky</description> + </annotations> + + <seeElement selector="{{StorefrontCustomerLoginAsCustomerSection.loginAsCustomerBanner}}" stepKey="verifyBannerIsVisibleBeforeScroll"/> + <scrollTo selector="{{StorefrontCustomerLoginAsCustomerSection.copyright}}" stepKey="scrollToEndOfThePage" /> + <seeElement selector="{{StorefrontCustomerLoginAsCustomerSection.loginAsCustomerBanner}}" stepKey="verifyBannerIsVisibleAfterScroll"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml new file mode 100644 index 0000000000000..2227cd4a3109c --- /dev/null +++ b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.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="StorefrontLoginToCustomerActionGroup"> + <annotations> + <description>Login to customer account using Login As customer.</description> + </annotations> + <arguments> + <argument name="customerId" type="string"/> + </arguments> + + <amOnPage url="admin/loginascustomer/login/login/customer_id/{{customerId}}/" stepKey="loginAsCustomer"/> + <waitForElementVisible selector="{{StorefrontCustomerLoginAsCustomerSection.loginAsCustomerBanner}}" stepKey="waitForLoginAsCustomerPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml new file mode 100644 index 0000000000000..a58fcab7145d5 --- /dev/null +++ b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerLoginAsCustomerSection"> + <element name="loginAsCustomerBanner" type="text" selector=".lac-notification-sticky" timeout="30"/> + <element name="copyright" type="text" selector=".copyright" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml new file mode 100644 index 0000000000000..04b0acca3be63 --- /dev/null +++ b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontLoginAsCustomerBannerTest"> + <annotations> + <features value="StorefrontLoginAsCustomerBanner"/> + <stories value="User login as customer and verify banner is sticky"/> + <useCaseId value="https://github.com/magento/magento2/issues/29354"/> + <title value="User login as customer and verify banner is sticky"/> + <testCaseId value=""/> + <description value="User login as customer and verify banner is sticky"/> + <severity value="CRITICAL"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer" /> + </after> + + <actionGroup ref="StorefrontLoginToCustomerActionGroup" stepKey="LoginAsCustomer"> + <argument name="customerId" value="$$createCustomer.id$$"/> + </actionGroup> + <actionGroup ref="StorefrontAssertLoginAsCustomerBannerVisibleActionGroup" stepKey="assertBannerIsSticky" /> + </test> +</tests> From 804ed717fbe4ece509cb7cac483ff0aa8945770f Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Fri, 7 Aug 2020 12:36:07 +0530 Subject: [PATCH 038/195] refactoring MFTF test --- ...sCustomerNotificationBannerActionGroup.xml | 33 ++++++++++++ ...yLoginAsCustomerNotificationBannerTest.xml | 51 +++++++++++++++++++ ...oginAsCustomerBannerVisibleActionGroup.xml | 20 -------- .../StorefrontLoginToCustomerActionGroup.xml | 22 -------- ...orefrontCustomerLoginAsCustomerSection.xml | 15 ------ .../StorefrontLoginAsCustomerBannerTest.xml | 35 ------------- 6 files changed, 84 insertions(+), 92 deletions(-) create mode 100644 app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml create mode 100644 app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml delete mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml delete mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml delete mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml delete mode 100644 app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml new file mode 100644 index 0000000000000..46f582ca6d44e --- /dev/null +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml @@ -0,0 +1,33 @@ +<?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="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup"> + <annotations> + <description>Verify Sticky Login as Customer notification banner present on page.</description> + </annotations> + <arguments> + <argument name="customerFullName" type="string"/> + <argument name="websiteName" type="string" defaultValue="Main Website"/> + </arguments> + + <waitForElementVisible selector="{{StorefrontLoginAsCustomerNotificationSection.notificationText}}" stepKey="waitForNotificationBanner"/> + <see selector="{{StorefrontLoginAsCustomerNotificationSection.notificationText}}" + userInput="You are connected as {{customerFullName}} on {{websiteName}}" + stepKey="assertCorrectNotificationBannerMessage"/> + <seeElement selector="{{StorefrontLoginAsCustomerNotificationSection.closeLink}}" + stepKey="assertCloseNotificationBannerPresent"/> + <executeJS function="window.scrollTo(0,document.body.scrollHeight);" stepKey="scrollToBottomOfPage"/> + <see selector="{{StorefrontLoginAsCustomerNotificationSection.notificationText}}" + userInput="You are connected as {{customerFullName}} on {{websiteName}}" + stepKey="assertCorrectNotificationBannerMessageAfterScroll"/> + <seeElement selector="{{StorefrontLoginAsCustomerNotificationSection.closeLink}}" + stepKey="assertCloseNotificationBannerPresentAfterScroll"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml new file mode 100644 index 0000000000000..3368fa7e5be7a --- /dev/null +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontStickyLoginAsCustomerNotificationBannerTest"> + <annotations> + <features value="Login as Customer"/> + <useCaseId value="https://github.com/magento/magento2/issues/29354"/> + <stories value="Availability of sticky UI elements if module enable/disable"/> + <title value="Sticky Notification Banner is present on Storefront page"/> + <description + value="Verify that Sticky Notification Banner is present on page if 'Login as customer' functionality used"/> + <testCaseId value=""/> + <severity value="CRITICAL"/> + </annotations> + <before> + <magentoCLI command="config:set {{LoginAsCustomerConfigDataEnabled.path}} 1" + stepKey="enableLoginAsCustomer"/> + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushConfigCache"> + <argument name="tags" value="config"/> + </actionGroup> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> + </before> + + <after> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <magentoCLI command="config:set {{LoginAsCustomerConfigDataEnabled.path}} 0" + stepKey="disableLoginAsCustomer"/> + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushConfigCache"> + <argument name="tags" value="config"/> + </actionGroup> + </after> + + <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> + <argument name="customerId" value="$$createCustomer.id$$"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> + <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> + </actionGroup> + + <actionGroup ref="StorefrontSignOutNotificationBannerAndCloseTabActionGroup" stepKey="signOutAndCloseTab"/> + </test> +</tests> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml deleted file mode 100644 index c37cf089398dc..0000000000000 --- a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerBannerVisibleActionGroup.xml +++ /dev/null @@ -1,20 +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="StorefrontAssertLoginAsCustomerBannerVisibleActionGroup"> - <annotations> - <description>Assert Login As Customer Banner is sticky</description> - </annotations> - - <seeElement selector="{{StorefrontCustomerLoginAsCustomerSection.loginAsCustomerBanner}}" stepKey="verifyBannerIsVisibleBeforeScroll"/> - <scrollTo selector="{{StorefrontCustomerLoginAsCustomerSection.copyright}}" stepKey="scrollToEndOfThePage" /> - <seeElement selector="{{StorefrontCustomerLoginAsCustomerSection.loginAsCustomerBanner}}" stepKey="verifyBannerIsVisibleAfterScroll"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml deleted file mode 100644 index 2227cd4a3109c..0000000000000 --- a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/ActionGroup/StorefrontLoginToCustomerActionGroup.xml +++ /dev/null @@ -1,22 +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="StorefrontLoginToCustomerActionGroup"> - <annotations> - <description>Login to customer account using Login As customer.</description> - </annotations> - <arguments> - <argument name="customerId" type="string"/> - </arguments> - - <amOnPage url="admin/loginascustomer/login/login/customer_id/{{customerId}}/" stepKey="loginAsCustomer"/> - <waitForElementVisible selector="{{StorefrontCustomerLoginAsCustomerSection.loginAsCustomerBanner}}" stepKey="waitForLoginAsCustomerPageLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml deleted file mode 100644 index a58fcab7145d5..0000000000000 --- a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Section/StorefrontCustomerLoginAsCustomerSection.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontCustomerLoginAsCustomerSection"> - <element name="loginAsCustomerBanner" type="text" selector=".lac-notification-sticky" timeout="30"/> - <element name="copyright" type="text" selector=".copyright" timeout="30"/> - </section> -</sections> diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml b/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml deleted file mode 100644 index 04b0acca3be63..0000000000000 --- a/app/code/Magento/LoginAsCustomerFrontendUi/Test/Mftf/Test/StorefrontLoginAsCustomerBannerTest.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="StorefrontLoginAsCustomerBannerTest"> - <annotations> - <features value="StorefrontLoginAsCustomerBanner"/> - <stories value="User login as customer and verify banner is sticky"/> - <useCaseId value="https://github.com/magento/magento2/issues/29354"/> - <title value="User login as customer and verify banner is sticky"/> - <testCaseId value=""/> - <description value="User login as customer and verify banner is sticky"/> - <severity value="CRITICAL"/> - </annotations> - <before> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> - </before> - - <after> - <deleteData createDataKey="createCustomer" stepKey="deleteCustomer" /> - </after> - - <actionGroup ref="StorefrontLoginToCustomerActionGroup" stepKey="LoginAsCustomer"> - <argument name="customerId" value="$$createCustomer.id$$"/> - </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerBannerVisibleActionGroup" stepKey="assertBannerIsSticky" /> - </test> -</tests> From da381030bdeca058a6979142fa1ac265628f9f1d Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Wed, 5 Aug 2020 18:28:52 +0300 Subject: [PATCH 039/195] added unit test --- app/code/Magento/Catalog/Helper/Image.php | 8 +++++++- app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Helper/Image.php b/app/code/Magento/Catalog/Helper/Image.php index abb24b0768b2a..ab74b5694ce9f 100644 --- a/app/code/Magento/Catalog/Helper/Image.php +++ b/app/code/Magento/Catalog/Helper/Image.php @@ -384,7 +384,9 @@ public function backgroundColor($colorRGB) { // assume that 3 params were given instead of array if (!is_array($colorRGB)) { + //phpcs:disable $colorRGB = func_get_args(); + //phpcs:enabled } $this->_getModel()->setBackgroundColor($colorRGB); return $this; @@ -498,7 +500,11 @@ protected function initBaseFile() if ($this->getImageFile()) { $model->setBaseFile($this->getImageFile()); } else { - $model->setBaseFile($this->getProduct() ? $this->getProduct()->getData($model->getDestinationSubdir()) : ''); + $model->setBaseFile( + $this->getProduct() + ? $this->getProduct()->getData($model->getDestinationSubdir()) + : '' + ); } } return $this; diff --git a/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php index aa29972c91a62..c606b7537cc44 100644 --- a/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Helper/ImageTest.php @@ -396,6 +396,14 @@ public function testGetWidth() $this->assertEquals($data['width'], $this->helper->getWidth()); } + /** + * Check initBaseFile without properties - product + */ + public function testGetUrlWithOutProduct() + { + $this->assertNull($this->helper->getUrl()); + } + /** * @param array $data * @dataProvider getHeightDataProvider From c0ca7cdf42a3f4425827fe61f4b7711ae0996e8b Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Mon, 10 Aug 2020 11:05:10 +0530 Subject: [PATCH 040/195] Feedback changes --- ...insMessageOrderCreatedByAdminActionGroup.xml} | 2 +- ...merConfigNotAvailableDirectlyActionGroup.xml} | 2 +- ...ginAsCustomerConfigNotVisibleActionGroup.xml} | 2 +- ...nLoginAsCustomerConfigVisibleActionGroup.xml} | 2 +- ...AdminLoginAsCustomerLogRecordActionGroup.xml} | 2 +- ...stomerSectionLinkNotAvailableActionGroup.xml} | 2 +- ...insMessageOrderCreatedByAdminActionGroup.xml} | 2 +- ...StorefrontCustomerOnStoreViewActionGroup.xml} | 2 +- ...efrontLoginAsCustomerLoggedInActionGroup.xml} | 2 +- ...nAsCustomerNotificationBannerActionGroup.xml} | 2 +- ...nAsCustomerNotificationBannerActionGroup.xml} | 2 +- .../AdminLoginAsCustomerAutoDetectionTest.xml | 2 +- .../Test/AdminLoginAsCustomerLoggingTest.xml | 8 ++++---- .../AdminLoginAsCustomerManualChooseTest.xml | 4 ++-- ...inLoginAsCustomerMultishippingLoggingTest.xml | 8 ++++---- .../Test/AdminLoginAsCustomerPlaceOrderTest.xml | 4 ++-- .../Test/AdminLoginAsCustomerReorderTest.xml | 4 ++-- ...stomerSettingsAvailableForGlobalLevelTest.xml | 6 +++--- .../Test/AdminLoginAsCustomerUserLogoutTest.xml | 2 +- ...AdminLoginAsCustomerUserSingleSessionTest.xml | 4 ++-- ...oAccessToLoginAsCustomerConfigurationTest.xml | 4 ++-- .../AdminUIShownIfLoginAsCustomerEnabledTest.xml | 4 ++-- ...tomerBannerPresentOnAllPagesInSessionTest.xml | 16 ++++++++-------- ...rontLoginAsCustomerNotificationBannerTest.xml | 2 +- ...inAsCustomerSeeSpecialPriceOnCategoryTest.xml | 2 +- ...rShoppingCartIsNotMergedWithGuestCartTest.xml | 2 +- ...ickyLoginAsCustomerNotificationBannerTest.xml | 4 ++-- 27 files changed, 49 insertions(+), 49 deletions(-) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml => AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml => AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml => AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml} (90%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml => AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AdminAssertLoginAsCustomerLogRecordActionGroup.xml => AssertAdminLoginAsCustomerLogRecordActionGroup.xml} (94%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml => AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml => AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{StorefrontAssertCustomerOnStoreViewActionGroup.xml => AssertStorefrontCustomerOnStoreViewActionGroup.xml} (92%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml => AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml} (94%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml => AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml} (95%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml => AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml} (96%) diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml index bcf6fc96aa131..cfab1ffc5bbf4 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertContainsMessageOrderCreatedByAdminActionGroup"> + <actionGroup name="AssertAdminContainsMessageOrderCreatedByAdminActionGroup"> <annotations> <description>Assert Admin Order page contains message about Order created by a Store Administrator. </description> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml index 7e032b168f062..ec2261be6d20f 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup"> + <actionGroup name="AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup"> <annotations> <description>Verify Login as Customer config section is not available by direct url.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml similarity index 90% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml index 875869d9928a4..18c1fb32a6f93 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertLoginAsCustomerConfigNotVisibleActionGroup"> + <actionGroup name="AssertAdminLoginAsCustomerConfigNotVisibleActionGroup"> <annotations> <description>Verify no Login as Customer config section available.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml index cdc513651ad54..9ddb439b2e5a3 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertLoginAsCustomerConfigVisibleActionGroup"> + <actionGroup name="AssertAdminLoginAsCustomerConfigVisibleActionGroup"> <annotations> <description>Verify Login as Customer config section available.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerLogRecordActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerLogRecordActionGroup.xml similarity index 94% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerLogRecordActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerLogRecordActionGroup.xml index da47864e28eac..0c35992e025f3 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerLogRecordActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerLogRecordActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertLoginAsCustomerLogRecordActionGroup"> + <actionGroup name="AssertAdminLoginAsCustomerLogRecordActionGroup"> <annotations> <description>Assert Login as Customer Log record is correct.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml index 779cb1e5c8899..f05be04bc78fe 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup"> + <actionGroup name="AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup"> <annotations> <description>Verify Login as Customer config section isn't available.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml index f40ea7f93c7a1..da1be5eed010f 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup"> + <actionGroup name="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup"> <annotations> <description>Verify Storefront Order page contains message about Order created by a Store Administrator. </description> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertCustomerOnStoreViewActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontCustomerOnStoreViewActionGroup.xml similarity index 92% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertCustomerOnStoreViewActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontCustomerOnStoreViewActionGroup.xml index f63cda2303526..f8c240abeb8fd 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertCustomerOnStoreViewActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontCustomerOnStoreViewActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertCustomerOnStoreViewActionGroup"> + <actionGroup name="AssertStorefrontCustomerOnStoreViewActionGroup"> <annotations> <description>Assert Customer is on the provided Store View.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml similarity index 94% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml index bb7e938bdfb59..7badab1fc8dfd 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertLoginAsCustomerLoggedInActionGroup"> + <actionGroup name="AssertStorefrontLoginAsCustomerLoggedInActionGroup"> <annotations> <description>Verify Admin successfully logged in as Customer.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml similarity index 95% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml index ce2e261f10040..a2464caa54a38 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup"> + <actionGroup name="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup"> <annotations> <description>Verify Login as Customer notification banner present on page.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml similarity index 96% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml index 46f582ca6d44e..b1b3ccd05ddfc 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup"> + <actionGroup name="AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup"> <annotations> <description>Verify Sticky Login as Customer notification banner present on page.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml index 42d53113e20f4..bdfadaf378561 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml @@ -53,7 +53,7 @@ </actionGroup> <!-- Assert Customer logged on on default store view --> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml index bf2556b30967b..7156c407ec6fa 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml @@ -84,22 +84,22 @@ <actionGroup ref="AdminOpenLoginAsCustomerLogActionGroup" stepKey="gotoLoginAsCustomerLog"/> <!-- Perform assertions --> - <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminFirstCustomerLogRecord"> + <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminFirstCustomerLogRecord"> <argument name="rowNumber" value="4"/> <argument name="adminId" value="1"/> <argument name="customerId" value="$$createFirstCustomer.id$$"/> </actionGroup> - <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminSecondCustomerLogRecord"> + <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminSecondCustomerLogRecord"> <argument name="rowNumber" value="3"/> <argument name="adminId" value="1"/> <argument name="customerId" value="$$createSecondCustomer.id$$"/> </actionGroup> - <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminFirstCustomerLogRecord"> + <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminFirstCustomerLogRecord"> <argument name="rowNumber" value="2"/> <argument name="adminId" value="$$createNewAdmin.id$$"/> <argument name="customerId" value="$$createFirstCustomer.id$$"/> </actionGroup> - <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminSecondCustomerLogRecord"> + <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminSecondCustomerLogRecord"> <argument name="rowNumber" value="1"/> <argument name="adminId" value="$$createNewAdmin.id$$"/> <argument name="customerId" value="$$createSecondCustomer.id$$"/> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml index acae07d1cda11..5e6c48a10a894 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml @@ -53,11 +53,11 @@ </actionGroup> <!-- Assert Customer logged on on custom store view --> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertCustomerOnStoreViewActionGroup" stepKey="assertCustomStoreView"> + <actionGroup ref="AssertStorefrontCustomerOnStoreViewActionGroup" stepKey="assertCustomStoreView"> <argument name="storeViewName" value="{{customStore.name}}"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml index 8e5c121fed157..9031644e438bd 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml @@ -82,19 +82,19 @@ <waitForPageLoad stepKey="waitForOrderPageLoad"/> <!-- Assert Storefront Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageFirstOrder"> + <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageFirstOrder"> <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> </actionGroup> - <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageSecondOrder"> + <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageSecondOrder"> <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> </actionGroup> <!-- Assert Admin Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageFirstOrder"> + <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageFirstOrder"> <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> <argument name="adminUserFullName" value="Magento User"/> </actionGroup> - <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageSecondOrder"> + <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageSecondOrder"> <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> <argument name="adminUserFullName" value="Magento User"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml index a4994d5c041d2..f14dfe56610dc 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml @@ -81,12 +81,12 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderId"/> <!-- Assert Storefront Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> + <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> <!-- Assert Admin Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> + <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> <argument name="orderId" value="{$grabOrderId}"/> <argument name="adminUserFullName" value="{{activeAdmin.firstname}} {{activeAdmin.lastname}}"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml index 16e0b94112562..98a04cd02d40a 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml @@ -95,12 +95,12 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabReorderId"/> <!-- Assert Storefront Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> + <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> <argument name="orderId" value="${grabReorderId}"/> </actionGroup> <!-- Assert Admin Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> + <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> <argument name="orderId" value="${grabReorderId}"/> <argument name="adminUserFullName" value="{{activeAdmin.firstname}} {{activeAdmin.lastname}}"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml index 807603bdcba0a..4869990ff7c42 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml @@ -25,16 +25,16 @@ </after> <amOnPage url="{{AdminLoginAsCustomerConfigPage.url}}" stepKey="navigateToLoginAsCustomerConfigSection"/> - <actionGroup ref="AdminAssertLoginAsCustomerConfigVisibleActionGroup" stepKey="seeLoginAsCustomerConfig"/> + <actionGroup ref="AssertAdminLoginAsCustomerConfigVisibleActionGroup" stepKey="seeLoginAsCustomerConfig"/> <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToDefaultStoreView"> <argument name="storeViewName" value="'Default Store View'"/> </actionGroup> - <actionGroup ref="AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLinkOnStoreView"/> + <actionGroup ref="AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLinkOnStoreView"/> <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToDefaultWebsite"> <argument name="storeViewName" value="'Main Website'"/> </actionGroup> - <actionGroup ref="AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLink"/> + <actionGroup ref="AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLink"/> </test> </tests> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml index 4f484e73d580b..33cee2c230e31 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml @@ -43,7 +43,7 @@ </actionGroup> <!-- Assert correctly logged in as Customer --> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml index e4cd48d8e868e..0172f9febf02d 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml @@ -46,7 +46,7 @@ </actionGroup> <!-- Assert correctly logged in as First Customer --> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromFirstCustomerPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromFirstCustomerPage"> <argument name="customerFullName" value="$$createFirstCustomer.firstname$$ $$createFirstCustomer.lastname$$"/> <argument name="customerEmail" value="$$createFirstCustomer.email$$"/> </actionGroup> @@ -58,7 +58,7 @@ </actionGroup> <!-- Assert correctly logged in as Second Customer --> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromSecondCustomerPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromSecondCustomerPage"> <argument name="customerFullName" value="$$createSecondCustomer.firstname$$ $$createSecondCustomer.lastname$$"/> <argument name="customerEmail" value="$$createSecondCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml index c7f42de741862..4a9a067ebd208 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml @@ -87,10 +87,10 @@ </actionGroup> <!-- Assert no Login as Customer config section visible --> - <actionGroup ref="AdminAssertLoginAsCustomerConfigNotVisibleActionGroup" stepKey="assertConfigNotVisible"/> + <actionGroup ref="AssertAdminLoginAsCustomerConfigNotVisibleActionGroup" stepKey="assertConfigNotVisible"/> <!-- Assert Login as Customer config section is not available by direct url --> - <actionGroup ref="AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup" + <actionGroup ref="AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup" stepKey="assertConfigNotAvailableDirectly"/> </test> </tests> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml index 2bf364b29ba8d..6d5787fe605db 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml @@ -48,7 +48,7 @@ <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> @@ -70,7 +70,7 @@ <argument name="orderId" value="$grabOrderId"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromOrderPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromOrderPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml index 6874fcb4fd220..276a48923f764 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml @@ -48,20 +48,20 @@ <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> <!-- Go to Wishlist and assert notification banner --> <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> <waitForPageLoad stepKey="waitForWishlistPageLoad"/> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnWishList"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnWishList"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> <!-- Go to category page and assert notification banner --> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCategoryPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCategoryPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -69,7 +69,7 @@ <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductPage"> <argument name="productUrlKey" value="$$createSimpleProduct.custom_attributes[url_key]$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnProductPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnProductPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -79,14 +79,14 @@ <argument name="productCount" value="1"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCartPage"/> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCartPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCartPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> <!-- Proceed to checkout and assert notification banner --> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <waitForPageLoad stepKey="waitForProceedToCheckout"/> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCheckoutPage"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCheckoutPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -95,7 +95,7 @@ <waitForElementVisible selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerBeforePlaceOrder"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerBeforePlaceOrder"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -105,7 +105,7 @@ <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerAfterPlaceOrder"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerAfterPlaceOrder"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> </test> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml index d953c493562c6..5a4d5ac7b3e24 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml @@ -42,7 +42,7 @@ </actionGroup> <!-- Assert Notification Banner is present on page --> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml index 775fcb122e181..d22cb681afb30 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml @@ -77,7 +77,7 @@ <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml index 09ec1b427f515..96d0cec0eb20e 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml @@ -57,7 +57,7 @@ <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml index 3368fa7e5be7a..c040351b2ef76 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <magentoCLI command="config:set {{LoginAsCustomerConfigDataEnabled.path}} 0" stepKey="disableLoginAsCustomer"/> - <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushConfigCache"> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushConfigCache"> <argument name="tags" value="config"/> </actionGroup> </after> @@ -42,7 +42,7 @@ <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> + <actionGroup ref="AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> From b6a66b33ca8acbd554765e2bcb5395d33dcc6510 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 14 Aug 2020 20:53:42 +0800 Subject: [PATCH 041/195] magento/magento2#1724: Support batches processing for synchronization queue messages - updated synchronization consumer and added media content bulk service --- .../Model/Consume.php | 23 ++++++++++++++++--- .../Api/SynchronizeIdentitiesInterface.php | 23 +++++++++++++++++++ .../Model/Consume.php | 23 ++++++++++++++++--- 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/MediaContentSynchronizationApi/Api/SynchronizeIdentitiesInterface.php diff --git a/app/code/Magento/MediaContentSynchronization/Model/Consume.php b/app/code/Magento/MediaContentSynchronization/Model/Consume.php index bcce3514e4ad9..d690225607da4 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Consume.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Consume.php @@ -7,6 +7,8 @@ namespace Magento\MediaContentSynchronization\Model; +use Magento\Framework\Exception\LocalizedException; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; /** @@ -19,19 +21,34 @@ class Consume */ private $synchronize; + /** + * @var SynchronizeIdentitiesInterface + */ + private $synchronizeIdentities; + /** * @param SynchronizeInterface $synchronize + * @param SynchronizeIdentitiesInterface $synchronizeIdentities */ - public function __construct(SynchronizeInterface $synchronize) - { + public function __construct( + SynchronizeInterface $synchronize, + SynchronizeIdentitiesInterface $synchronizeIdentities + ) { $this->synchronize = $synchronize; + $this->synchronizeIdentities = $synchronizeIdentities; } /** * Run media files synchronization. + * @param string[] $message + * @throws LocalizedException */ - public function execute() : void + public function execute(array $message) : void { $this->synchronize->execute(); + + if (!empty($message)) { + $this->synchronizeIdentities->execute($message); + } } } diff --git a/app/code/Magento/MediaContentSynchronizationApi/Api/SynchronizeIdentitiesInterface.php b/app/code/Magento/MediaContentSynchronizationApi/Api/SynchronizeIdentitiesInterface.php new file mode 100644 index 0000000000000..7e21cbb570053 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronizationApi/Api/SynchronizeIdentitiesInterface.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronizationApi\Api; + +use Magento\MediaContentApi\Api\Data\ContentIdentityInterface; + +/** + * Synchronize bulk assets and contents + */ +interface SynchronizeIdentitiesInterface +{ + /** + * Synchronize media contents + * + * @param ContentIdentityInterface[] $contentIdentities + */ + public function execute(array $contentIdentities): void; +} diff --git a/app/code/Magento/MediaGallerySynchronization/Model/Consume.php b/app/code/Magento/MediaGallerySynchronization/Model/Consume.php index 79c0c9a1a803b..c565e0f728bd1 100644 --- a/app/code/Magento/MediaGallerySynchronization/Model/Consume.php +++ b/app/code/Magento/MediaGallerySynchronization/Model/Consume.php @@ -7,6 +7,8 @@ namespace Magento\MediaGallerySynchronization\Model; +use Magento\Framework\Exception\LocalizedException; +use Magento\MediaGallerySynchronizationApi\Api\SynchronizeFilesInterface; use Magento\MediaGallerySynchronizationApi\Api\SynchronizeInterface; /** @@ -19,19 +21,34 @@ class Consume */ private $synchronize; + /** + * @var SynchronizeFilesInterface + */ + private $synchronizeFiles; + /** * @param SynchronizeInterface $synchronize + * @param SynchronizeFilesInterface $synchronizeFiles */ - public function __construct(SynchronizeInterface $synchronize) - { + public function __construct( + SynchronizeInterface $synchronize, + SynchronizeFilesInterface $synchronizeFiles + ) { $this->synchronize = $synchronize; + $this->synchronizeFiles = $synchronizeFiles; } /** * Run media files synchronization. + * @param string[] $message + * @throws LocalizedException */ - public function execute() : void + public function execute(array $message) : void { $this->synchronize->execute(); + + if (!empty($message)) { + $this->synchronizeFiles->execute($message); + } } } From 425113c355ca8cb880cfec8c2c362dbfd802795a Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Mon, 17 Aug 2020 10:15:29 +0530 Subject: [PATCH 042/195] Revert "Feedback changes" This reverts commit c0ca7cdf42a3f4425827fe61f4b7711ae0996e8b. --- ...insMessageOrderCreatedByAdminActionGroup.xml} | 2 +- ...merConfigNotAvailableDirectlyActionGroup.xml} | 2 +- ...ginAsCustomerConfigNotVisibleActionGroup.xml} | 2 +- ...tLoginAsCustomerConfigVisibleActionGroup.xml} | 2 +- ...ssertLoginAsCustomerLogRecordActionGroup.xml} | 2 +- ...stomerSectionLinkNotAvailableActionGroup.xml} | 2 +- ...insMessageOrderCreatedByAdminActionGroup.xml} | 2 +- ...rontAssertCustomerOnStoreViewActionGroup.xml} | 2 +- ...AssertLoginAsCustomerLoggedInActionGroup.xml} | 2 +- ...nAsCustomerNotificationBannerActionGroup.xml} | 2 +- ...nAsCustomerNotificationBannerActionGroup.xml} | 2 +- .../AdminLoginAsCustomerAutoDetectionTest.xml | 2 +- .../Test/AdminLoginAsCustomerLoggingTest.xml | 8 ++++---- .../AdminLoginAsCustomerManualChooseTest.xml | 4 ++-- ...inLoginAsCustomerMultishippingLoggingTest.xml | 8 ++++---- .../Test/AdminLoginAsCustomerPlaceOrderTest.xml | 4 ++-- .../Test/AdminLoginAsCustomerReorderTest.xml | 4 ++-- ...stomerSettingsAvailableForGlobalLevelTest.xml | 6 +++--- .../Test/AdminLoginAsCustomerUserLogoutTest.xml | 2 +- ...AdminLoginAsCustomerUserSingleSessionTest.xml | 4 ++-- ...oAccessToLoginAsCustomerConfigurationTest.xml | 4 ++-- .../AdminUIShownIfLoginAsCustomerEnabledTest.xml | 4 ++-- ...tomerBannerPresentOnAllPagesInSessionTest.xml | 16 ++++++++-------- ...rontLoginAsCustomerNotificationBannerTest.xml | 2 +- ...inAsCustomerSeeSpecialPriceOnCategoryTest.xml | 2 +- ...rShoppingCartIsNotMergedWithGuestCartTest.xml | 2 +- ...ickyLoginAsCustomerNotificationBannerTest.xml | 4 ++-- 27 files changed, 49 insertions(+), 49 deletions(-) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml => AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml => AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml => AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml} (90%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml => AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertAdminLoginAsCustomerLogRecordActionGroup.xml => AdminAssertLoginAsCustomerLogRecordActionGroup.xml} (94%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml => AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml => StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml} (93%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertStorefrontCustomerOnStoreViewActionGroup.xml => StorefrontAssertCustomerOnStoreViewActionGroup.xml} (92%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml => StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml} (94%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml => StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml} (95%) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml => StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml} (96%) diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml index cfab1ffc5bbf4..bcf6fc96aa131 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminContainsMessageOrderCreatedByAdminActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertContainsMessageOrderCreatedByAdminActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminContainsMessageOrderCreatedByAdminActionGroup"> + <actionGroup name="AdminAssertContainsMessageOrderCreatedByAdminActionGroup"> <annotations> <description>Assert Admin Order page contains message about Order created by a Store Administrator. </description> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml index ec2261be6d20f..7e032b168f062 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup"> + <actionGroup name="AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup"> <annotations> <description>Verify Login as Customer config section is not available by direct url.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml similarity index 90% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml index 18c1fb32a6f93..875869d9928a4 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigNotVisibleActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigNotVisibleActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminLoginAsCustomerConfigNotVisibleActionGroup"> + <actionGroup name="AdminAssertLoginAsCustomerConfigNotVisibleActionGroup"> <annotations> <description>Verify no Login as Customer config section available.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml index 9ddb439b2e5a3..cdc513651ad54 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerConfigVisibleActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerConfigVisibleActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminLoginAsCustomerConfigVisibleActionGroup"> + <actionGroup name="AdminAssertLoginAsCustomerConfigVisibleActionGroup"> <annotations> <description>Verify Login as Customer config section available.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerLogRecordActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerLogRecordActionGroup.xml similarity index 94% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerLogRecordActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerLogRecordActionGroup.xml index 0c35992e025f3..da47864e28eac 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerLogRecordActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerLogRecordActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminLoginAsCustomerLogRecordActionGroup"> + <actionGroup name="AdminAssertLoginAsCustomerLogRecordActionGroup"> <annotations> <description>Assert Login as Customer Log record is correct.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml index f05be04bc78fe..779cb1e5c8899 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup"> + <actionGroup name="AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup"> <annotations> <description>Verify Login as Customer config section isn't available.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml similarity index 93% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml index da1be5eed010f..f40ea7f93c7a1 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup"> + <actionGroup name="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup"> <annotations> <description>Verify Storefront Order page contains message about Order created by a Store Administrator. </description> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontCustomerOnStoreViewActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertCustomerOnStoreViewActionGroup.xml similarity index 92% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontCustomerOnStoreViewActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertCustomerOnStoreViewActionGroup.xml index f8c240abeb8fd..f63cda2303526 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontCustomerOnStoreViewActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertCustomerOnStoreViewActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontCustomerOnStoreViewActionGroup"> + <actionGroup name="StorefrontAssertCustomerOnStoreViewActionGroup"> <annotations> <description>Assert Customer is on the provided Store View.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml similarity index 94% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml index 7badab1fc8dfd..bb7e938bdfb59 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerLoggedInActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerLoggedInActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontLoginAsCustomerLoggedInActionGroup"> + <actionGroup name="StorefrontAssertLoginAsCustomerLoggedInActionGroup"> <annotations> <description>Verify Admin successfully logged in as Customer.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml similarity index 95% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml index a2464caa54a38..ce2e261f10040 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontLoginAsCustomerNotificationBannerActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertLoginAsCustomerNotificationBannerActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup"> + <actionGroup name="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup"> <annotations> <description>Verify Login as Customer notification banner present on page.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml similarity index 96% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml index b1b3ccd05ddfc..46f582ca6d44e 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup"> + <actionGroup name="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup"> <annotations> <description>Verify Sticky Login as Customer notification banner present on page.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml index bdfadaf378561..42d53113e20f4 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerAutoDetectionTest.xml @@ -53,7 +53,7 @@ </actionGroup> <!-- Assert Customer logged on on default store view --> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml index 7156c407ec6fa..bf2556b30967b 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerLoggingTest.xml @@ -84,22 +84,22 @@ <actionGroup ref="AdminOpenLoginAsCustomerLogActionGroup" stepKey="gotoLoginAsCustomerLog"/> <!-- Perform assertions --> - <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminFirstCustomerLogRecord"> + <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminFirstCustomerLogRecord"> <argument name="rowNumber" value="4"/> <argument name="adminId" value="1"/> <argument name="customerId" value="$$createFirstCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminSecondCustomerLogRecord"> + <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyDefaultAdminSecondCustomerLogRecord"> <argument name="rowNumber" value="3"/> <argument name="adminId" value="1"/> <argument name="customerId" value="$$createSecondCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminFirstCustomerLogRecord"> + <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminFirstCustomerLogRecord"> <argument name="rowNumber" value="2"/> <argument name="adminId" value="$$createNewAdmin.id$$"/> <argument name="customerId" value="$$createFirstCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertAdminLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminSecondCustomerLogRecord"> + <actionGroup ref="AdminAssertLoginAsCustomerLogRecordActionGroup" stepKey="verifyNewAdminSecondCustomerLogRecord"> <argument name="rowNumber" value="1"/> <argument name="adminId" value="$$createNewAdmin.id$$"/> <argument name="customerId" value="$$createSecondCustomer.id$$"/> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml index 5e6c48a10a894..acae07d1cda11 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerManualChooseTest.xml @@ -53,11 +53,11 @@ </actionGroup> <!-- Assert Customer logged on on custom store view --> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerGird"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontCustomerOnStoreViewActionGroup" stepKey="assertCustomStoreView"> + <actionGroup ref="StorefrontAssertCustomerOnStoreViewActionGroup" stepKey="assertCustomStoreView"> <argument name="storeViewName" value="{{customStore.name}}"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml index 9031644e438bd..8e5c121fed157 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerMultishippingLoggingTest.xml @@ -82,19 +82,19 @@ <waitForPageLoad stepKey="waitForOrderPageLoad"/> <!-- Assert Storefront Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageFirstOrder"> + <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageFirstOrder"> <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> </actionGroup> - <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageSecondOrder"> + <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageSecondOrder"> <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> </actionGroup> <!-- Assert Admin Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageFirstOrder"> + <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageFirstOrder"> <argument name="orderId" value="{$getFirstOrderIdPlaceOrder}"/> <argument name="adminUserFullName" value="Magento User"/> </actionGroup> - <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageSecondOrder"> + <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageSecondOrder"> <argument name="orderId" value="{$getSecondOrderIdPlaceOrder}"/> <argument name="adminUserFullName" value="Magento User"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml index f14dfe56610dc..a4994d5c041d2 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerPlaceOrderTest.xml @@ -81,12 +81,12 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderId"/> <!-- Assert Storefront Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> + <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> <argument name="orderId" value="{$grabOrderId}"/> </actionGroup> <!-- Assert Admin Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> + <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> <argument name="orderId" value="{$grabOrderId}"/> <argument name="adminUserFullName" value="{{activeAdmin.firstname}} {{activeAdmin.lastname}}"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml index 98a04cd02d40a..16e0b94112562 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerReorderTest.xml @@ -95,12 +95,12 @@ <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabReorderId"/> <!-- Assert Storefront Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AssertStorefrontContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> + <actionGroup ref="StorefrontAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyStorefrontMessageOrderCreatedByAdmin"> <argument name="orderId" value="${grabReorderId}"/> </actionGroup> <!-- Assert Admin Order page contains message about Order created by a Store Administrator --> - <actionGroup ref="AssertAdminContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> + <actionGroup ref="AdminAssertContainsMessageOrderCreatedByAdminActionGroup" stepKey="verifyAdminMessageOrderCreatedByAdmin"> <argument name="orderId" value="${grabReorderId}"/> <argument name="adminUserFullName" value="{{activeAdmin.firstname}} {{activeAdmin.lastname}}"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml index 4869990ff7c42..807603bdcba0a 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerSettingsAvailableForGlobalLevelTest.xml @@ -25,16 +25,16 @@ </after> <amOnPage url="{{AdminLoginAsCustomerConfigPage.url}}" stepKey="navigateToLoginAsCustomerConfigSection"/> - <actionGroup ref="AssertAdminLoginAsCustomerConfigVisibleActionGroup" stepKey="seeLoginAsCustomerConfig"/> + <actionGroup ref="AdminAssertLoginAsCustomerConfigVisibleActionGroup" stepKey="seeLoginAsCustomerConfig"/> <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToDefaultStoreView"> <argument name="storeViewName" value="'Default Store View'"/> </actionGroup> - <actionGroup ref="AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLinkOnStoreView"/> + <actionGroup ref="AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLinkOnStoreView"/> <actionGroup ref="SwitchToTheNewStoreViewActionGroup" stepKey="switchToDefaultWebsite"> <argument name="storeViewName" value="'Main Website'"/> </actionGroup> - <actionGroup ref="AssertAdminLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLink"/> + <actionGroup ref="AdminAssertLoginAsCustomerSectionLinkNotAvailableActionGroup" stepKey="dontSeeLoginAsCustomerSectionLink"/> </test> </tests> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml index 33cee2c230e31..4f484e73d580b 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserLogoutTest.xml @@ -43,7 +43,7 @@ </actionGroup> <!-- Assert correctly logged in as Customer --> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml index 0172f9febf02d..e4cd48d8e868e 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminLoginAsCustomerUserSingleSessionTest.xml @@ -46,7 +46,7 @@ </actionGroup> <!-- Assert correctly logged in as First Customer --> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromFirstCustomerPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromFirstCustomerPage"> <argument name="customerFullName" value="$$createFirstCustomer.firstname$$ $$createFirstCustomer.lastname$$"/> <argument name="customerEmail" value="$$createFirstCustomer.email$$"/> </actionGroup> @@ -58,7 +58,7 @@ </actionGroup> <!-- Assert correctly logged in as Second Customer --> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromSecondCustomerPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromSecondCustomerPage"> <argument name="customerFullName" value="$$createSecondCustomer.firstname$$ $$createSecondCustomer.lastname$$"/> <argument name="customerEmail" value="$$createSecondCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml index 4a9a067ebd208..c7f42de741862 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminNoAccessToLoginAsCustomerConfigurationTest.xml @@ -87,10 +87,10 @@ </actionGroup> <!-- Assert no Login as Customer config section visible --> - <actionGroup ref="AssertAdminLoginAsCustomerConfigNotVisibleActionGroup" stepKey="assertConfigNotVisible"/> + <actionGroup ref="AdminAssertLoginAsCustomerConfigNotVisibleActionGroup" stepKey="assertConfigNotVisible"/> <!-- Assert Login as Customer config section is not available by direct url --> - <actionGroup ref="AssertAdminLoginAsCustomerConfigNotAvailableDirectlyActionGroup" + <actionGroup ref="AdminAssertLoginAsCustomerConfigNotAvailableDirectlyActionGroup" stepKey="assertConfigNotAvailableDirectly"/> </test> </tests> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml index 6d5787fe605db..2bf364b29ba8d 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/AdminUIShownIfLoginAsCustomerEnabledTest.xml @@ -48,7 +48,7 @@ <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromCustomerPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> @@ -70,7 +70,7 @@ <argument name="orderId" value="$grabOrderId"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromOrderPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerLoggedInActionGroup" stepKey="assertLoggedInFromOrderPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> <argument name="customerEmail" value="$$createCustomer.email$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml index 276a48923f764..6874fcb4fd220 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerBannerPresentOnAllPagesInSessionTest.xml @@ -48,20 +48,20 @@ <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> <!-- Go to Wishlist and assert notification banner --> <amOnPage url="{{StorefrontCustomerWishlistPage.url}}" stepKey="amOnWishListPage"/> <waitForPageLoad stepKey="waitForWishlistPageLoad"/> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnWishList"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnWishList"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> <!-- Go to category page and assert notification banner --> <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="navigateToCategoryPage"/> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCategoryPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCategoryPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -69,7 +69,7 @@ <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="openProductPage"> <argument name="productUrlKey" value="$$createSimpleProduct.custom_attributes[url_key]$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnProductPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnProductPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -79,14 +79,14 @@ <argument name="productCount" value="1"/> </actionGroup> <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCartPage"/> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCartPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCartPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> <!-- Proceed to checkout and assert notification banner --> <click selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="clickProceedToCheckout"/> <waitForPageLoad stepKey="waitForProceedToCheckout"/> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCheckoutPage"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerOnCheckoutPage"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -95,7 +95,7 @@ <waitForElementVisible selector="{{CheckoutShippingSection.next}}" time="30" stepKey="waitForNextButton"/> <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNext"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskAfterClickNext"/> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerBeforePlaceOrder"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerBeforePlaceOrder"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> @@ -105,7 +105,7 @@ <argument name="orderNumberMessage" value="CONST.successCheckoutOrderNumberMessage"/> <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerAfterPlaceOrder"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBannerAfterPlaceOrder"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> </test> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml index 5a4d5ac7b3e24..d953c493562c6 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerNotificationBannerTest.xml @@ -42,7 +42,7 @@ </actionGroup> <!-- Assert Notification Banner is present on page --> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml index d22cb681afb30..775fcb122e181 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerSeeSpecialPriceOnCategoryTest.xml @@ -77,7 +77,7 @@ <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml index 96d0cec0eb20e..09ec1b427f515 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontLoginAsCustomerShoppingCartIsNotMergedWithGuestCartTest.xml @@ -57,7 +57,7 @@ <actionGroup ref="AdminLoginAsCustomerLoginFromCustomerPageActionGroup" stepKey="loginAsCustomerFromCustomerPage"> <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> + <actionGroup ref="StorefrontAssertLoginAsCustomerNotificationBannerActionGroup" stepKey="assertNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml index c040351b2ef76..3368fa7e5be7a 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -33,7 +33,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <magentoCLI command="config:set {{LoginAsCustomerConfigDataEnabled.path}} 0" stepKey="disableLoginAsCustomer"/> - <actionGroup ref="CliCacheCleanActionGroup" stepKey="flushConfigCache"> + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushConfigCache"> <argument name="tags" value="config"/> </actionGroup> </after> @@ -42,7 +42,7 @@ <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> + <actionGroup ref="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> From 58b78f6d00d344563632822e9d90292cf7da31b1 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Mon, 17 Aug 2020 10:17:45 +0530 Subject: [PATCH 043/195] Feedback changes --- ...frontStickyLoginAsCustomerNotificationBannerActionGroup.xml} | 2 +- .../StorefrontStickyLoginAsCustomerNotificationBannerTest.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/{StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml => AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml} (96%) diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml similarity index 96% rename from app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml rename to app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml index 46f582ca6d44e..b1b3ccd05ddfc 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/ActionGroup/AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup"> + <actionGroup name="AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup"> <annotations> <description>Verify Sticky Login as Customer notification banner present on page.</description> </annotations> diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml index 3368fa7e5be7a..831f8c73a668f 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -42,7 +42,7 @@ <argument name="customerId" value="$$createCustomer.id$$"/> </actionGroup> - <actionGroup ref="StorefrontAssertStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> + <actionGroup ref="AssertStorefrontStickyLoginAsCustomerNotificationBannerActionGroup" stepKey="assertStickyNotificationBanner"> <argument name="customerFullName" value="$$createCustomer.firstname$$ $$createCustomer.lastname$$"/> </actionGroup> From debeec23125bb76b9a65b4fb0613efddfecfc9cd Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Mon, 17 Aug 2020 19:22:15 +0530 Subject: [PATCH 044/195] Fixed MFTF test --- .../StorefrontStickyLoginAsCustomerNotificationBannerTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml index 831f8c73a668f..8d30212e183d7 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -17,6 +17,7 @@ <description value="Verify that Sticky Notification Banner is present on page if 'Login as customer' functionality used"/> <testCaseId value=""/> + <group value="login_as_customer"/> <severity value="CRITICAL"/> </annotations> <before> From 29c3b265dfded16c3e688e505f15e1f055446731 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Wed, 19 Aug 2020 02:13:48 +0800 Subject: [PATCH 045/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - implemented gallery synchronization functionality --- .../MediaContentSynchronization/Model/Consume.php | 13 +++++++------ .../MediaContentSynchronization/Model/Publish.php | 5 +++-- .../Magento/MediaContentSynchronization/etc/di.xml | 1 + .../MediaGallerySynchronization/Model/Consume.php | 12 ++++++------ .../MediaGallerySynchronization/Model/Publish.php | 5 +++-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Model/Consume.php b/app/code/Magento/MediaContentSynchronization/Model/Consume.php index d690225607da4..d91b426e4f3ee 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Consume.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Consume.php @@ -8,6 +8,7 @@ namespace Magento\MediaContentSynchronization\Model; use Magento\Framework\Exception\LocalizedException; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; @@ -40,15 +41,15 @@ public function __construct( /** * Run media files synchronization. - * @param string[] $message + * @param string[] $identities * @throws LocalizedException */ - public function execute(array $message) : void + public function execute(array $identities) : void { - $this->synchronize->execute(); - - if (!empty($message)) { - $this->synchronizeIdentities->execute($message); + if (!empty($identities)) { + $this->synchronizeIdentities->execute($identities); + } else { + $this->synchronize->execute(); } } } diff --git a/app/code/Magento/MediaContentSynchronization/Model/Publish.php b/app/code/Magento/MediaContentSynchronization/Model/Publish.php index ad6fdd27d7067..f94328a3f4468 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Publish.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Publish.php @@ -34,12 +34,13 @@ public function __construct(PublisherInterface $publisher) /** * Publish media content synchronization message to the message queue. + * @param array $contentIdentities */ - public function execute() : void + public function execute(array $contentIdentities = []) : void { $this->publisher->publish( self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION, - [self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION] + $contentIdentities ); } } diff --git a/app/code/Magento/MediaContentSynchronization/etc/di.xml b/app/code/Magento/MediaContentSynchronization/etc/di.xml index d4615c15206e5..e5347f1a11561 100644 --- a/app/code/Magento/MediaContentSynchronization/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronization/etc/di.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface" type="Magento\MediaContentSynchronization\Model\Synchronize"/> + <preference for="Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface" type="Magento\MediaContentSynchronization\Model\SynchronizeIdentities"/> <type name="Magento\Framework\Console\CommandListInterface"> <arguments> <argument name="commands" xsi:type="array"> diff --git a/app/code/Magento/MediaGallerySynchronization/Model/Consume.php b/app/code/Magento/MediaGallerySynchronization/Model/Consume.php index c565e0f728bd1..981d60c1e8c28 100644 --- a/app/code/Magento/MediaGallerySynchronization/Model/Consume.php +++ b/app/code/Magento/MediaGallerySynchronization/Model/Consume.php @@ -40,15 +40,15 @@ public function __construct( /** * Run media files synchronization. - * @param string[] $message + * @param array $paths * @throws LocalizedException */ - public function execute(array $message) : void + public function execute(array $paths) : void { - $this->synchronize->execute(); - - if (!empty($message)) { - $this->synchronizeFiles->execute($message); + if (!empty($paths)) { + $this->synchronizeFiles->execute($paths); + } else { + $this->synchronize->execute(); } } } diff --git a/app/code/Magento/MediaGallerySynchronization/Model/Publish.php b/app/code/Magento/MediaGallerySynchronization/Model/Publish.php index 386798d68d9df..a2db65911b1e4 100644 --- a/app/code/Magento/MediaGallerySynchronization/Model/Publish.php +++ b/app/code/Magento/MediaGallerySynchronization/Model/Publish.php @@ -34,12 +34,13 @@ public function __construct(PublisherInterface $publisher) /** * Publish media content synchronization message to the message queue. + * @param array $paths */ - public function execute() : void + public function execute(array $paths = []) : void { $this->publisher->publish( self::TOPIC_MEDIA_GALLERY_SYNCHRONIZATION, - [self::TOPIC_MEDIA_GALLERY_SYNCHRONIZATION] + $paths ); } } From 95859dc036a6076423f30409f224a6b5d6f5ac84 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Wed, 19 Aug 2020 09:39:17 +0300 Subject: [PATCH 046/195] MC-36548: Invalid Form Key error when adding to cart before page fully refreshes --- .../Magento/Catalog/view/frontend/templates/product/list.phtml | 3 ++- .../Catalog/view/frontend/web/js/catalog-add-to-cart.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index c805941fa0272..6a47978f1e5c6 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -98,7 +98,8 @@ $_helper = $block->getData('outputHelper'); <?= $block->getBlockHtml('formkey') ?> <button type="submit" title="<?= $escaper->escapeHtmlAttr(__('Add to Cart')) ?>" - class="action tocart primary"> + class="action tocart primary" + disabled> <span><?= $escaper->escapeHtml(__('Add to Cart')) ?></span> </button> </form> diff --git a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js index 7d3e4b3280473..fbce6691fd66a 100644 --- a/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js +++ b/app/code/Magento/Catalog/view/frontend/web/js/catalog-add-to-cart.js @@ -34,6 +34,7 @@ define([ if (this.options.bindSubmit) { this._bindSubmit(); } + $(this.options.addToCartButtonSelector).attr('disabled', false); }, /** From 32adddb365c1170b5daea7c24f8e9966e84496e9 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Wed, 19 Aug 2020 20:18:46 +0200 Subject: [PATCH 047/195] Set of improvements for LoginAsCustomer modules --- .../Magento/LoginAsCustomer/Model/Config.php | 18 ++++------- .../Model/Config/Source/StoreViewLogin.php | 7 ----- .../Plugin/Button/ToolbarPlugin.php | 30 +++++++++---------- .../Block/Adminhtml/NotAllowedPopup.php | 4 --- .../Model/Config.php | 3 -- .../Plugin/CustomerDataValidatePlugin.php | 15 +++------- .../Plugin/CustomerExtractorPlugin.php | 13 ++------ .../Plugin/InvalidateExpiredSessionPlugin.php | 20 +++++++------ .../Config/DisablePageCacheIfNeededPlugin.php | 2 +- .../AdminAddCommentOnOrderPlacementPlugin.php | 5 ++-- 10 files changed, 41 insertions(+), 76 deletions(-) diff --git a/app/code/Magento/LoginAsCustomer/Model/Config.php b/app/code/Magento/LoginAsCustomer/Model/Config.php index 2cfafa6ac09a3..bec9527c65f95 100644 --- a/app/code/Magento/LoginAsCustomer/Model/Config.php +++ b/app/code/Magento/LoginAsCustomer/Model/Config.php @@ -10,16 +10,9 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\LoginAsCustomerApi\Api\ConfigInterface; -/** - * @inheritdoc - */ class Config implements ConfigInterface { - /** - * Extension config path - */ - private const XML_PATH_ENABLED - = 'login_as_customer/general/enabled'; + private const XML_PATH_ENABLED = 'login_as_customer/general/enabled'; private const XML_PATH_STORE_VIEW_MANUAL_CHOICE_ENABLED = 'login_as_customer/general/store_view_manual_choice_enabled'; private const XML_PATH_AUTHENTICATION_EXPIRATION_TIME @@ -33,9 +26,8 @@ class Config implements ConfigInterface /** * @param ScopeConfigInterface $scopeConfig */ - public function __construct( - ScopeConfigInterface $scopeConfig - ) { + public function __construct(ScopeConfigInterface $scopeConfig) + { $this->scopeConfig = $scopeConfig; } @@ -44,7 +36,7 @@ public function __construct( */ public function isEnabled(): bool { - return (bool)$this->scopeConfig->getValue(self::XML_PATH_ENABLED); + return $this->scopeConfig->isSetFlag(self::XML_PATH_ENABLED); } /** @@ -52,7 +44,7 @@ public function isEnabled(): bool */ public function isStoreManualChoiceEnabled(): bool { - return (bool)$this->scopeConfig->getValue(self::XML_PATH_STORE_VIEW_MANUAL_CHOICE_ENABLED); + return $this->scopeConfig->isSetFlag(self::XML_PATH_STORE_VIEW_MANUAL_CHOICE_ENABLED); } /** diff --git a/app/code/Magento/LoginAsCustomerAdminUi/Model/Config/Source/StoreViewLogin.php b/app/code/Magento/LoginAsCustomerAdminUi/Model/Config/Source/StoreViewLogin.php index 265c4fedb722d..9d14a4b44d10b 100644 --- a/app/code/Magento/LoginAsCustomerAdminUi/Model/Config/Source/StoreViewLogin.php +++ b/app/code/Magento/LoginAsCustomerAdminUi/Model/Config/Source/StoreViewLogin.php @@ -12,14 +12,7 @@ */ class StoreViewLogin implements \Magento\Framework\Data\OptionSourceInterface { - /** - * @const int - */ private const AUTODETECT = 0; - - /** - * @const int - */ private const MANUAL = 1; /** diff --git a/app/code/Magento/LoginAsCustomerAdminUi/Plugin/Button/ToolbarPlugin.php b/app/code/Magento/LoginAsCustomerAdminUi/Plugin/Button/ToolbarPlugin.php index c67b0d9dd5273..2cdcd5723df4b 100644 --- a/app/code/Magento/LoginAsCustomerAdminUi/Plugin/Button/ToolbarPlugin.php +++ b/app/code/Magento/LoginAsCustomerAdminUi/Plugin/Button/ToolbarPlugin.php @@ -8,6 +8,7 @@ namespace Magento\LoginAsCustomerAdminUi\Plugin\Button; use Magento\Backend\Block\Widget\Button\ButtonList; +use Magento\Backend\Block\Widget\Button\ToolbarInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Escaper; use Magento\Framework\View\Element\AbstractBlock; @@ -61,13 +62,13 @@ public function __construct( /** * Add Login as Customer button. * - * @param \Magento\Backend\Block\Widget\Button\ToolbarInterface $subject - * @param \Magento\Framework\View\Element\AbstractBlock $context - * @param \Magento\Backend\Block\Widget\Button\ButtonList $buttonList + * @param ToolbarInterface $subject + * @param AbstractBlock $context + * @param ButtonList $buttonList * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforePushButtons( - \Magento\Backend\Block\Widget\Button\ToolbarInterface $subject, + ToolbarInterface $subject, AbstractBlock $context, ButtonList $buttonList ): void { @@ -97,18 +98,17 @@ public function beforePushButtons( */ private function getOrder(string $nameInLayout, AbstractBlock $context) { - $order = null; - - if ('sales_order_edit' == $nameInLayout) { - $order = $context->getOrder(); - } elseif ('sales_invoice_view' == $nameInLayout) { - $order = $context->getInvoice()->getOrder(); - } elseif ('sales_shipment_view' == $nameInLayout) { - $order = $context->getShipment()->getOrder(); - } elseif ('sales_creditmemo_view' == $nameInLayout) { - $order = $context->getCreditmemo()->getOrder(); + switch ($nameInLayout) { + case 'sales_order_edit': + return $context->getOrder(); + case 'sales_invoice_view': + return $context->getInvoice()->getOrder(); + case 'sales_shipment_view': + return $context->getShipment()->getOrder(); + case 'sales_creditmemo_view': + return $context->getCreditmemo()->getOrder(); } - return $order; + return null; } } diff --git a/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php b/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php index 547be1de5a008..b98ea203057b1 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Block/Adminhtml/NotAllowedPopup.php @@ -19,15 +19,11 @@ class NotAllowedPopup extends Template { /** - * Config - * * @var ConfigInterface */ private $config; /** - * Json Serializer - * * @var Json */ private $json; diff --git a/app/code/Magento/LoginAsCustomerAssistance/Model/Config.php b/app/code/Magento/LoginAsCustomerAssistance/Model/Config.php index 2fce39cd4e85e..c2244fa3a799c 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Model/Config.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Model/Config.php @@ -16,9 +16,6 @@ */ class Config implements ConfigInterface { - /** - * Extension config path - */ private const XML_PATH_SHOPPING_ASSISTANCE_CHECKBOX_TITLE = 'login_as_customer/general/shopping_assistance_checkbox_title'; private const XML_PATH_SHOPPING_ASSISTANCE_CHECKBOX_TOOLTIP diff --git a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php index 9da329b4a3991..7512936db6ad9 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php @@ -11,6 +11,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Validator\Exception; use Magento\LoginAsCustomerAssistance\Api\IsAssistanceEnabledInterface; use Magento\LoginAsCustomerAssistance\Model\ResourceModel\GetLoginAsCustomerAssistanceAllowed; @@ -46,16 +47,12 @@ public function __construct( * * @param Form $subject * @param RequestInterface $request - * @param null|string $scope - * @param bool $scopeOnly - * @throws \Magento\Framework\Validator\Exception + * @throws Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeExtractData( Form $subject, - RequestInterface $request, - $scope = null, - $scopeOnly = true + RequestInterface $request ): void { if ($this->isSetAssistanceAllowedParam($request) && !$this->authorization->isAllowed('Magento_LoginAsCustomer::allow_shopping_assistance') @@ -74,11 +71,7 @@ public function beforeExtractData( ], ]; - throw new \Magento\Framework\Validator\Exception( - null, - null, - $errorMessages - ); + throw new Exception(null, null, $errorMessages); } } } diff --git a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerExtractorPlugin.php b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerExtractorPlugin.php index 619036da8bb22..156d84bcae9bd 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerExtractorPlugin.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerExtractorPlugin.php @@ -35,27 +35,20 @@ public function __construct( * Add assistance_allowed extension attribute value to Customer instance. * * @param CustomerExtractor $subject - * @param callable $proceed + * @param CustomerInterface $customer * @param string $formCode * @param RequestInterface $request * @param array $attributeValues * @return CustomerInterface * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundExtract( + public function afterExtract( CustomerExtractor $subject, - callable $proceed, + CustomerInterface $customer, string $formCode, RequestInterface $request, array $attributeValues = [] ) { - /** @var CustomerInterface $customer */ - $customer = $proceed( - $formCode, - $request, - $attributeValues - ); - $assistanceAllowedStatus = $request->getParam('assistance_allowed'); if (!empty($assistanceAllowedStatus)) { $extensionAttributes = $customer->getExtensionAttributes(); diff --git a/app/code/Magento/LoginAsCustomerFrontendUi/Plugin/InvalidateExpiredSessionPlugin.php b/app/code/Magento/LoginAsCustomerFrontendUi/Plugin/InvalidateExpiredSessionPlugin.php index c1e035ac9637c..7c0682440b4dc 100644 --- a/app/code/Magento/LoginAsCustomerFrontendUi/Plugin/InvalidateExpiredSessionPlugin.php +++ b/app/code/Magento/LoginAsCustomerFrontendUi/Plugin/InvalidateExpiredSessionPlugin.php @@ -65,15 +65,17 @@ public function __construct( */ public function beforeExecute(ActionInterface $subject) { - if ($this->config->isEnabled()) { - $adminId = $this->getLoggedAsCustomerAdminId->execute(); - $customerId = (int)$this->session->getCustomerId(); - if ($adminId && $customerId) { - if (!$this->isLoginAsCustomerSessionActive->execute($customerId, $adminId)) { - $this->session->clearStorage(); - $this->session->expireSessionCookie(); - $this->session->regenerateId(); - } + if (!$this->config->isEnabled()) { + return; + } + + $adminId = $this->getLoggedAsCustomerAdminId->execute(); + $customerId = (int)$this->session->getCustomerId(); + if ($adminId && $customerId) { + if (!$this->isLoginAsCustomerSessionActive->execute($customerId, $adminId)) { + $this->session->clearStorage(); + $this->session->expireSessionCookie(); + $this->session->regenerateId(); } } } diff --git a/app/code/Magento/LoginAsCustomerPageCache/Plugin/PageCache/Model/Config/DisablePageCacheIfNeededPlugin.php b/app/code/Magento/LoginAsCustomerPageCache/Plugin/PageCache/Model/Config/DisablePageCacheIfNeededPlugin.php index dabf8c62e1dee..2b98c1f6c119e 100644 --- a/app/code/Magento/LoginAsCustomerPageCache/Plugin/PageCache/Model/Config/DisablePageCacheIfNeededPlugin.php +++ b/app/code/Magento/LoginAsCustomerPageCache/Plugin/PageCache/Model/Config/DisablePageCacheIfNeededPlugin.php @@ -54,7 +54,7 @@ public function __construct( public function afterIsEnabled(Config $subject, $isEnabled): bool { if ($isEnabled) { - $disable = $this->scopeConfig->getValue( + $disable = $this->scopeConfig->isSetFlag( 'login_as_customer/general/disable_page_cache', ScopeInterface::SCOPE_STORE ); diff --git a/app/code/Magento/LoginAsCustomerSales/Plugin/AdminAddCommentOnOrderPlacementPlugin.php b/app/code/Magento/LoginAsCustomerSales/Plugin/AdminAddCommentOnOrderPlacementPlugin.php index 2ae982e536f49..3a27a5ef9e561 100644 --- a/app/code/Magento/LoginAsCustomerSales/Plugin/AdminAddCommentOnOrderPlacementPlugin.php +++ b/app/code/Magento/LoginAsCustomerSales/Plugin/AdminAddCommentOnOrderPlacementPlugin.php @@ -25,9 +25,8 @@ class AdminAddCommentOnOrderPlacementPlugin /** * @param Session $session */ - public function __construct( - Session $session - ) { + public function __construct(Session $session) + { $this->userSession = $session; } From 7c6b7f7451f696cb7ce819abd60e6b85c8f3149c Mon Sep 17 00:00:00 2001 From: Lena Orobei <oorobei@adobe.com> Date: Wed, 19 Aug 2020 16:40:55 -0500 Subject: [PATCH 048/195] Update StorefrontCustomerCheckoutWithCustomerGroupTest.xml --- .../StorefrontCustomerCheckoutWithCustomerGroupTest.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml index 53d50b8ea8bf9..39869190aa40e 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml @@ -10,9 +10,9 @@ <test name="StorefrontCustomerCheckoutWithCustomerGroupTest"> <annotations> <features value="Customer Checkout"/> - <stories value="Checkout via Customer Checkout with Customer Group"/> - <title value="Create order by Customer with Customer Group"/> - <description value="Should be assign Customer Group to Order, when enabled setting fir Customer - Auto Group Assign"/> + <stories value="Customer checkout with Customer Group assigned"/> + <title value="Place order by Customer with Customer Group assigned"/> + <description value="Customer Group should be assigned to Order when setting Auto Group Assign is enabled for Customer"/> <severity value="MAJOR"/> <group value="checkout"/> <group value="customer"/> From 2aca78a139b29368f34f4c8d64c51763178856c8 Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Thu, 20 Aug 2020 10:37:52 +0530 Subject: [PATCH 049/195] Added reverted review changes --- .../StorefrontStickyLoginAsCustomerNotificationBannerTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml index 8d30212e183d7..f9dc3863769df 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -23,7 +23,7 @@ <before> <magentoCLI command="config:set {{LoginAsCustomerConfigDataEnabled.path}} 1" stepKey="enableLoginAsCustomer"/> - <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushConfigCache"> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanConfigCache"> <argument name="tags" value="config"/> </actionGroup> <createData entity="Simple_US_Customer" stepKey="createCustomer"/> @@ -34,7 +34,7 @@ <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <magentoCLI command="config:set {{LoginAsCustomerConfigDataEnabled.path}} 0" stepKey="disableLoginAsCustomer"/> - <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushConfigCache"> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanConfigCache"> <argument name="tags" value="config"/> </actionGroup> </after> From eca27c78db1621eac2ec16553f66d4519a00d56b Mon Sep 17 00:00:00 2001 From: Shankar Konar <konar.shankar2013@gmail.com> Date: Thu, 20 Aug 2020 13:37:09 +0530 Subject: [PATCH 050/195] Fixed MFTF test --- .../StorefrontStickyLoginAsCustomerNotificationBannerTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml index f9dc3863769df..611bc1044fd00 100644 --- a/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml +++ b/app/code/Magento/LoginAsCustomer/Test/Mftf/Test/StorefrontStickyLoginAsCustomerNotificationBannerTest.xml @@ -26,7 +26,7 @@ <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanConfigCache"> <argument name="tags" value="config"/> </actionGroup> - <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="Simple_US_Customer_Assistance_Allowed" stepKey="createCustomer"/> <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> From b4d78e7435a65e299fd8230030701c3fc082b146 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 20 Aug 2020 10:31:08 +0200 Subject: [PATCH 051/195] Fix Static tests failure --- .../Plugin/CustomerDataValidatePlugin.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php index 7512936db6ad9..ed2f599c16c63 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php @@ -47,12 +47,16 @@ public function __construct( * * @param Form $subject * @param RequestInterface $request + * @param null|string $scope + * @param bool $scopeOnly * @throws Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeExtractData( Form $subject, - RequestInterface $request + RequestInterface $request, + $scope = null, + $scopeOnly = true ): void { if ($this->isSetAssistanceAllowedParam($request) && !$this->authorization->isAllowed('Magento_LoginAsCustomer::allow_shopping_assistance') From b68e049804bb77e51695aeae2307b7dbf16eb553 Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 20 Aug 2020 12:43:49 +0200 Subject: [PATCH 052/195] Fix Static tests failure --- .../Plugin/CustomerDataValidatePlugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php index ed2f599c16c63..c11326c95bf0e 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php @@ -75,6 +75,7 @@ public function beforeExtractData( ], ]; + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new Exception(null, null, $errorMessages); } } From 2ba01638f1ad0a347f8d2f359c868fc7eb661e9f Mon Sep 17 00:00:00 2001 From: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> Date: Thu, 20 Aug 2020 12:45:07 +0200 Subject: [PATCH 053/195] Better way to fix the false positive --- .../Plugin/CustomerDataValidatePlugin.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php index c11326c95bf0e..4bbf691e2b58e 100644 --- a/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php +++ b/app/code/Magento/LoginAsCustomerAssistance/Plugin/CustomerDataValidatePlugin.php @@ -11,7 +11,7 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\AuthorizationInterface; use Magento\Framework\Message\MessageInterface; -use Magento\Framework\Validator\Exception; +use Magento\Framework\Validator\Exception as ValidatorException; use Magento\LoginAsCustomerAssistance\Api\IsAssistanceEnabledInterface; use Magento\LoginAsCustomerAssistance\Model\ResourceModel\GetLoginAsCustomerAssistanceAllowed; @@ -49,7 +49,7 @@ public function __construct( * @param RequestInterface $request * @param null|string $scope * @param bool $scopeOnly - * @throws Exception + * @throws ValidatorException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function beforeExtractData( @@ -75,8 +75,7 @@ public function beforeExtractData( ], ]; - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new Exception(null, null, $errorMessages); + throw new ValidatorException(null, null, $errorMessages); } } } From bbb99e5a4b3bc82da0aa184ad3635e813f1b3d1c Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 21 Aug 2020 04:05:23 +0800 Subject: [PATCH 054/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - implemented media content sychronization --- .../Model/Consume.php | 18 +++++-- .../Model/Publish.php | 48 +++++++++++++++++-- .../etc/communication.xml | 2 +- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Model/Consume.php b/app/code/Magento/MediaContentSynchronization/Model/Consume.php index d91b426e4f3ee..0136466eaed81 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Consume.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Consume.php @@ -7,8 +7,9 @@ namespace Magento\MediaContentSynchronization\Model; +use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\Exception\LocalizedException; -use Magento\MediaContentApi\Api\Data\ContentIdentityInterface; +use Magento\Framework\Serialize\SerializerInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; @@ -17,6 +18,11 @@ */ class Consume { + /** + * @var SerializerInterface + */ + private $serializer; + /** * @var SynchronizeInterface */ @@ -28,24 +34,30 @@ class Consume private $synchronizeIdentities; /** + * @param SerializerInterface $serializer * @param SynchronizeInterface $synchronize * @param SynchronizeIdentitiesInterface $synchronizeIdentities */ public function __construct( + SerializerInterface $serializer, SynchronizeInterface $synchronize, SynchronizeIdentitiesInterface $synchronizeIdentities ) { + $this->serializer = $serializer; $this->synchronize = $synchronize; $this->synchronizeIdentities = $synchronizeIdentities; } /** * Run media files synchronization. - * @param string[] $identities + * @param OperationInterface $operation * @throws LocalizedException */ - public function execute(array $identities) : void + public function execute(OperationInterface $operation) : void { + $serializedData = $operation->getSerializedData(); + $identities = $this->serializer->unserialize($serializedData); + if (!empty($identities)) { $this->synchronizeIdentities->execute($identities); } else { diff --git a/app/code/Magento/MediaContentSynchronization/Model/Publish.php b/app/code/Magento/MediaContentSynchronization/Model/Publish.php index f94328a3f4468..ee32df47215a9 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Publish.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Publish.php @@ -7,7 +7,11 @@ namespace Magento\MediaContentSynchronization\Model; +use Magento\AsynchronousOperations\Api\Data\OperationInterfaceFactory; +use Magento\Framework\Bulk\OperationInterface; +use Magento\Framework\DataObject\IdentityGeneratorInterface; use Magento\Framework\MessageQueue\PublisherInterface; +use Magento\Framework\Serialize\SerializerInterface; /** * Publish media content synchronization queue. @@ -19,16 +23,41 @@ class Publish */ private const TOPIC_MEDIA_CONTENT_SYNCHRONIZATION = 'media.content.synchronization'; + /** + * @var OperationInterfaceFactory + */ + private $operationFactory; + + /** + * @var IdentityGeneratorInterface + */ + private $identityService; + /** * @var PublisherInterface */ private $publisher; /** + * @var SerializerInterface + */ + private $serializer; + + /** + * @param OperationInterfaceFactory $operationFactory + * @param IdentityGeneratorInterface $identityService * @param PublisherInterface $publisher + * @param SerializerInterface $serializer */ - public function __construct(PublisherInterface $publisher) - { + public function __construct( + OperationInterfaceFactory $operationFactory, + IdentityGeneratorInterface $identityService, + PublisherInterface $publisher, + SerializerInterface $serializer + ) { + $this->operationFactory = $operationFactory; + $this->identityService = $identityService; + $this->serializer = $serializer; $this->publisher = $publisher; } @@ -38,9 +67,22 @@ public function __construct(PublisherInterface $publisher) */ public function execute(array $contentIdentities = []) : void { + $bulkUuid = $this->identityService->generateId(); + $dataToEncode = $this->serializer->serialize($contentIdentities); + + $data = [ + 'data' => [ + 'bulk_uuid' => $bulkUuid, + 'topic_name' => self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION, + 'serialized_data' => $dataToEncode, + 'status' => OperationInterface::STATUS_TYPE_OPEN, + ] + ]; + $operation = $this->operationFactory->create($data); + $this->publisher->publish( self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION, - $contentIdentities + $operation ); } } diff --git a/app/code/Magento/MediaContentSynchronization/etc/communication.xml b/app/code/Magento/MediaContentSynchronization/etc/communication.xml index e3436aee85331..05641b7432564 100644 --- a/app/code/Magento/MediaContentSynchronization/etc/communication.xml +++ b/app/code/Magento/MediaContentSynchronization/etc/communication.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd"> - <topic name="media.content.synchronization" is_synchronous="false" request="string[]"> + <topic name="media.content.synchronization" is_synchronous="false" request="Magento\AsynchronousOperations\Api\Data\OperationInterface"> <handler name="media.content.synchronization.handler" type="Magento\MediaContentSynchronization\Model\Consume" method="execute"/> </topic> From 2917c4b47bb37e55b01bd8850cc903c5e6fd76c7 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 21 Aug 2020 07:26:33 +0800 Subject: [PATCH 055/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - added media content sychronizer and save contents to database --- .../Model/SynchronizeIdentities.php | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php new file mode 100644 index 0000000000000..42815adc7a18b --- /dev/null +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronization\Model; + +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\UpdateContentAssetLinksInterface; +use Magento\MediaContentApi\Model\GetEntityContentsInterface; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; + +class SynchronizeIdentities implements SynchronizeIdentitiesInterface +{ + private const ENTITY_TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + private const MEDIA_CONTENT_TYPE = 'entity_type'; + private const MEDIA_CONTENT_ENTITY_ID = 'entity_id'; + private const MEDIA_CONTENT_FIELD = 'field'; + private const FIELD_CMS_PAGE = 'cms_page'; + private const FIELD_CMS_BLOCK = 'cms_block'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var UpdateContentAssetLinksInterface + */ + private $updateContentAssetLinks; + + /** + * @var GetEntityContentsInterface + */ + private $getEntityContents; + + /** + * @var array + */ + private $fields; + + /** + * @param ContentIdentityInterfaceFactory $contentIdentityFactory + * @param UpdateContentAssetLinksInterface $updateContentAssetLinks + * @param GetEntityContentsInterface $getEntityContents + * @param array $fields + */ + public function __construct( + ContentIdentityInterfaceFactory $contentIdentityFactory, + UpdateContentAssetLinksInterface $updateContentAssetLinks, + GetEntityContentsInterface $getEntityContents, + array $fields = [] + ) { + $this->contentIdentityFactory = $contentIdentityFactory; + $this->updateContentAssetLinks = $updateContentAssetLinks; + $this->getEntityContents = $getEntityContents; + $this->fields = $fields; + } + + /** + * @inheritDoc + */ + public function execute(array $mediaContentIdentities): void + { + foreach ($mediaContentIdentities as $identity) { + $contentIdentity = $this->contentIdentityFactory->create( + [ + self::ENTITY_TYPE => $identity[self::MEDIA_CONTENT_TYPE], + self::ENTITY_ID => $identity[self::MEDIA_CONTENT_ENTITY_ID], + self::FIELD => $identity[self::MEDIA_CONTENT_FIELD] + ] + ); + + if ($identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_PAGE + || $identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_BLOCK + ) { + $content = (string) $identity[self::MEDIA_CONTENT_FIELD]; + } else { + $content = implode(PHP_EOL, $this->getEntityContents->execute($contentIdentity)); + } + + $this->updateContentAssetLinks->execute( + $contentIdentity, + $content + ); + } + } +} From 16dc1fa89136018040542b03fde972180d445ebe Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 21 Aug 2020 19:47:03 +0800 Subject: [PATCH 056/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - added method to sync and save media content to database --- .../Model/SynchronizeIdentities.php | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php index 42815adc7a18b..5726bd9cf5f14 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -7,6 +7,7 @@ namespace Magento\MediaContentSynchronization\Model; +use Magento\Framework\App\ResourceConnection; use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; use Magento\MediaContentApi\Api\UpdateContentAssetLinksInterface; use Magento\MediaContentApi\Model\GetEntityContentsInterface; @@ -17,12 +18,24 @@ class SynchronizeIdentities implements SynchronizeIdentitiesInterface private const ENTITY_TYPE = 'entityType'; private const ENTITY_ID = 'entityId'; private const FIELD = 'field'; + private const MEDIA_CONTENT_TYPE = 'entity_type'; private const MEDIA_CONTENT_ENTITY_ID = 'entity_id'; private const MEDIA_CONTENT_FIELD = 'field'; + private const FIELD_CMS_PAGE = 'cms_page'; private const FIELD_CMS_BLOCK = 'cms_block'; + private const ID_CMS_PAGE = 'page_id'; + private const ID_CMS_BLOCK = 'block_id'; + + private const COLUMN_CMS_CONTENT = 'content'; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + /** * @var ContentIdentityInterfaceFactory */ @@ -39,26 +52,21 @@ class SynchronizeIdentities implements SynchronizeIdentitiesInterface private $getEntityContents; /** - * @var array - */ - private $fields; - - /** + * @param ResourceConnection $resourceConnection * @param ContentIdentityInterfaceFactory $contentIdentityFactory * @param UpdateContentAssetLinksInterface $updateContentAssetLinks * @param GetEntityContentsInterface $getEntityContents - * @param array $fields */ public function __construct( + ResourceConnection $resourceConnection, ContentIdentityInterfaceFactory $contentIdentityFactory, UpdateContentAssetLinksInterface $updateContentAssetLinks, - GetEntityContentsInterface $getEntityContents, - array $fields = [] + GetEntityContentsInterface $getEntityContents ) { + $this->resourceConnection = $resourceConnection; $this->contentIdentityFactory = $contentIdentityFactory; $this->updateContentAssetLinks = $updateContentAssetLinks; $this->getEntityContents = $getEntityContents; - $this->fields = $fields; } /** @@ -78,7 +86,7 @@ public function execute(array $mediaContentIdentities): void if ($identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_PAGE || $identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_BLOCK ) { - $content = (string) $identity[self::MEDIA_CONTENT_FIELD]; + $content = $this->getCmsMediaContent($identity[self::MEDIA_CONTENT_TYPE], $identity[self::MEDIA_CONTENT_ENTITY_ID]); } else { $content = implode(PHP_EOL, $this->getEntityContents->execute($contentIdentity)); } @@ -89,4 +97,25 @@ public function execute(array $mediaContentIdentities): void ); } } + + /** + * Get cms media content from database + * + * @param string $tableName + * @param string $cmsId + * @return string + */ + private function getCmsMediaContent(string $tableName, string $cmsId): string + { + $connection = $this->resourceConnection->getConnection(); + $tableName = $this->resourceConnection->getTableName($tableName); + $idField = $tableName == self::FIELD_CMS_BLOCK ? $idField = self::ID_CMS_BLOCK : self::ID_CMS_PAGE; + + $select = $connection->select() + ->from($tableName, self::COLUMN_CMS_CONTENT) + ->where($idField . '= ?', $cmsId); + $data = $connection->fetchOne($select); + + return (string)$data; + } } From 771dc31f5ef1236db3ac54c3062f663cfee7d8b0 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 21 Aug 2020 17:13:19 +0300 Subject: [PATCH 057/195] Introduce granulated Media Gallery ACL resources and enforce for old media gallery --- .../Adminhtml/Wysiwyg/Images/DeleteFiles.php | 5 ++++ .../Adminhtml/Wysiwyg/Images/DeleteFolder.php | 5 ++++ .../Adminhtml/Wysiwyg/Images/NewFolder.php | 7 ++++- .../Adminhtml/Wysiwyg/Images/OnInsert.php | 5 ++++ .../Adminhtml/Wysiwyg/Images/Upload.php | 7 ++++- .../Adminhtml/Directories/Create.php | 2 +- .../Adminhtml/Directories/Delete.php | 2 +- .../Controller/Adminhtml/Image/Delete.php | 2 +- .../Controller/Adminhtml/Image/Upload.php | 2 +- app/code/Magento/MediaGalleryUi/etc/acl.xml | 26 +++++++++++++++++++ 10 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 app/code/Magento/MediaGalleryUi/etc/acl.xml diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php index fa873930aaade..848a10950e0ba 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php @@ -13,6 +13,11 @@ */ class DeleteFiles extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { + /** + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Cms::delete_assets'; + /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php index 29f84e0b2e534..8a67d9220e11c 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php @@ -17,6 +17,11 @@ */ class DeleteFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { + /** + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Cms::delete_folder'; + /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php index 82d200beb6dc9..de462f82c8911 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php @@ -14,6 +14,11 @@ */ class NewFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { + /** + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Cms::create_folder'; + /** * @var \Magento\Framework\Controller\Result\JsonFactory */ @@ -65,7 +70,7 @@ public function execute() } /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); - + return $resultJson->setData($result); } } diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php index 3244a7d14f0a3..516742ffd2ee8 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php @@ -8,6 +8,11 @@ class OnInsert extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images { + /** + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Cms::insert_assets'; + /** * @var \Magento\Framework\Controller\Result\RawFactory */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php index 9bad371aa84d7..6e48dfe7c5f10 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php @@ -17,6 +17,11 @@ */ class Upload extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { + /** + * @see _isAllowed() + */ + public const ADMIN_RESOURCE = 'Magento_Cms::upload_assets'; + /** * @var \Magento\Framework\Controller\Result\JsonFactory */ @@ -74,7 +79,7 @@ public function execute() } /** @var \Magento\Framework\Controller\Result\Json $resultJson */ $resultJson = $this->resultJsonFactory->create(); - + return $resultJson->setData($response); } } diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php index 3d4af88e4ad67..147fc48910113 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php @@ -29,7 +29,7 @@ class Create extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::create_folder'; /** * @var CreateDirectoriesByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php index 56f12c5139d65..1ca2512826504 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php @@ -30,7 +30,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::delete_folder'; /** * @var DeleteAssetsByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php index a5d1cee7abf41..d630d93688d28 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php @@ -31,7 +31,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::delete_assets'; /** * @var DeleteImage diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php index e965d94b33f0c..0d3f9a78ae3ca 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php @@ -28,7 +28,7 @@ class Upload extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::upload_assets'; /** * @var UploadImage diff --git a/app/code/Magento/MediaGalleryUi/etc/acl.xml b/app/code/Magento/MediaGalleryUi/etc/acl.xml new file mode 100644 index 0000000000000..7c8784e412814 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/etc/acl.xml @@ -0,0 +1,26 @@ +<?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:Acl/etc/acl.xsd"> + <acl> + <resources> + <resource id="Magento_Backend::admin"> + <resource id="Magento_Backend::content"> + <resource id="Magento_Backend::content_elements"> + <resource id="Magento_Cms::media_gallery"> + <resource id="Magento_Cms::upload_assets" title="Upload Assets" translate="title" sortOrder="70"/> + <resource id="Magento_Cms::delete_assets" title="Delete Assets" translate="title" sortOrder="60"/> + <resource id="Magento_Cms::insert_assets" title="Insert Assets into the content" translate="title" sortOrder="50"/> + <resource id="Magento_Cms::create_folder" title="Create Folder" translate="title" sortOrder="40"/> + <resource id="Magento_Cms::delete_folder" title="Delete Folder" translate="title" sortOrder="40"/> + </resource> + </resource> + </resource> + </resource> + </resources> + </acl> +</config> From d5276dd5b66d6c8f8e50d70e91ea515322b1af7b Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 21 Aug 2020 23:37:06 +0800 Subject: [PATCH 058/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - fixed failed static test --- .../Magento/MediaContentSynchronization/Model/Consume.php | 1 + .../Magento/MediaContentSynchronization/Model/Publish.php | 3 ++- .../Model/SynchronizeIdentities.php | 4 +++- app/code/Magento/MediaContentSynchronization/composer.json | 3 ++- app/code/Magento/MediaContentSynchronizationApi/composer.json | 3 ++- .../Magento/MediaGallerySynchronization/Model/Consume.php | 1 + .../Magento/MediaGallerySynchronization/Model/Publish.php | 3 ++- 7 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Model/Consume.php b/app/code/Magento/MediaContentSynchronization/Model/Consume.php index 0136466eaed81..da18d733ff5ab 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Consume.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Consume.php @@ -50,6 +50,7 @@ public function __construct( /** * Run media files synchronization. + * * @param OperationInterface $operation * @throws LocalizedException */ diff --git a/app/code/Magento/MediaContentSynchronization/Model/Publish.php b/app/code/Magento/MediaContentSynchronization/Model/Publish.php index ee32df47215a9..5b7d0768955fc 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Publish.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Publish.php @@ -62,7 +62,8 @@ public function __construct( } /** - * Publish media content synchronization message to the message queue. + * Publish media content synchronization message to the message queue + * * @param array $contentIdentities */ public function execute(array $contentIdentities = []) : void diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php index 5726bd9cf5f14..ad72ec34a4be6 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -86,7 +86,9 @@ public function execute(array $mediaContentIdentities): void if ($identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_PAGE || $identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_BLOCK ) { - $content = $this->getCmsMediaContent($identity[self::MEDIA_CONTENT_TYPE], $identity[self::MEDIA_CONTENT_ENTITY_ID]); + $content = $this->getCmsMediaContent( + $identity[self::MEDIA_CONTENT_TYPE], $identity[self::MEDIA_CONTENT_ENTITY_ID] + ); } else { $content = implode(PHP_EOL, $this->getEntityContents->execute($contentIdentity)); } diff --git a/app/code/Magento/MediaContentSynchronization/composer.json b/app/code/Magento/MediaContentSynchronization/composer.json index 3be5f535487ec..5e46ef371c204 100644 --- a/app/code/Magento/MediaContentSynchronization/composer.json +++ b/app/code/Magento/MediaContentSynchronization/composer.json @@ -6,7 +6,8 @@ "magento/framework": "*", "magento/module-media-content-synchronization-api": "*", "magento/framework-message-queue": "*", - "magento/module-media-content-api": "*" + "magento/module-media-content-api": "*", + "magento/module-asynchronous-operations": "*" }, "suggest": { "magento/module-media-gallery-synchronization": "*" diff --git a/app/code/Magento/MediaContentSynchronizationApi/composer.json b/app/code/Magento/MediaContentSynchronizationApi/composer.json index 1f1e5e4b51c5b..398aaf1de8071 100644 --- a/app/code/Magento/MediaContentSynchronizationApi/composer.json +++ b/app/code/Magento/MediaContentSynchronizationApi/composer.json @@ -3,7 +3,8 @@ "description": "Magento module responsible for the media content synchronization implementation API", "require": { "php": "~7.3.0||~7.4.0", - "magento/framework": "*" + "magento/framework": "*", + "magento/module-media-content-api": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/MediaGallerySynchronization/Model/Consume.php b/app/code/Magento/MediaGallerySynchronization/Model/Consume.php index 981d60c1e8c28..b796d4225d08c 100644 --- a/app/code/Magento/MediaGallerySynchronization/Model/Consume.php +++ b/app/code/Magento/MediaGallerySynchronization/Model/Consume.php @@ -40,6 +40,7 @@ public function __construct( /** * Run media files synchronization. + * * @param array $paths * @throws LocalizedException */ diff --git a/app/code/Magento/MediaGallerySynchronization/Model/Publish.php b/app/code/Magento/MediaGallerySynchronization/Model/Publish.php index a2db65911b1e4..ec314416e36ee 100644 --- a/app/code/Magento/MediaGallerySynchronization/Model/Publish.php +++ b/app/code/Magento/MediaGallerySynchronization/Model/Publish.php @@ -33,7 +33,8 @@ public function __construct(PublisherInterface $publisher) } /** - * Publish media content synchronization message to the message queue. + * Publish media content synchronization message to the message queue + * * @param array $paths */ public function execute(array $paths = []) : void From d9e9075859fdda71bc349f114f2e99b79e5137ae Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Sat, 22 Aug 2020 01:40:36 +0800 Subject: [PATCH 059/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - fixed failed static test --- .../Model/SynchronizeIdentities.php | 3 ++- app/code/Magento/MediaContentSynchronization/composer.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php index ad72ec34a4be6..2715d5e49fd6b 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -87,7 +87,8 @@ public function execute(array $mediaContentIdentities): void || $identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_BLOCK ) { $content = $this->getCmsMediaContent( - $identity[self::MEDIA_CONTENT_TYPE], $identity[self::MEDIA_CONTENT_ENTITY_ID] + $identity[self::MEDIA_CONTENT_TYPE], + $identity[self::MEDIA_CONTENT_ENTITY_ID] ); } else { $content = implode(PHP_EOL, $this->getEntityContents->execute($contentIdentity)); diff --git a/app/code/Magento/MediaContentSynchronization/composer.json b/app/code/Magento/MediaContentSynchronization/composer.json index 5e46ef371c204..f435f311deb46 100644 --- a/app/code/Magento/MediaContentSynchronization/composer.json +++ b/app/code/Magento/MediaContentSynchronization/composer.json @@ -4,6 +4,7 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", + "magento/framework-bulk": "*", "magento/module-media-content-synchronization-api": "*", "magento/framework-message-queue": "*", "magento/module-media-content-api": "*", From c67c5f84fed1fa7a33f9ebeb31da58813541526e Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Mon, 24 Aug 2020 21:25:51 +0300 Subject: [PATCH 060/195] Removed redundant action group --- .../AdminOpentCmsBlockActionGroup.xml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml deleted file mode 100644 index 0f87ee90b7ce0..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ /dev/null @@ -1,16 +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="AdminOpenCmsBlockActionGroup"> - <arguments> - <argument name="block_id" type="string"/> - </arguments> - <amOnPage url="{{AdminEditBlockPage.url(block_id)}}" stepKey="openEditCmsBlock"/> - </actionGroup> -</actionGroups> From bd087ecd950024190fef4e5cb6fe5aa6bb27d2eb Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Wed, 26 Aug 2020 14:00:03 +0300 Subject: [PATCH 061/195] MC-36953: [Magento On-Premise]: Tracking link in email redirects to 404 Page not Found --- .../Sales/Model/ResourceModel/Order.php | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order.php b/app/code/Magento/Sales/Model/ResourceModel/Order.php index fd69f3b1a52a3..1903308466498 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order.php @@ -53,12 +53,12 @@ protected function _construct() /** * @param \Magento\Framework\Model\ResourceModel\Db\Context $context - * @param Attribute $attribute - * @param Manager $sequenceManager * @param Snapshot $entitySnapshot * @param RelationComposite $entityRelationComposite + * @param Attribute $attribute + * @param Manager $sequenceManager * @param StateHandler $stateHandler - * @param string $connectionName + * @param string|null $connectionName */ public function __construct( \Magento\Framework\Model\ResourceModel\Db\Context $context, @@ -137,6 +137,8 @@ protected function calculateItems(\Magento\Sales\Model\Order $object) } /** + * Before save + * * @param \Magento\Framework\Model\AbstractModel $object * @return $this */ @@ -152,15 +154,15 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) ]; $object->setStoreName(implode(PHP_EOL, $name)); $object->setTotalItemCount($this->calculateItems($object)); + $object->setData( + 'protect_code', + substr( + hash('sha256', uniqid(Random::getRandomNumber(), true) . ':' . microtime(true)), + 5, + 32 + ) + ); } - $object->setData( - 'protect_code', - substr( - hash('sha256', uniqid(Random::getRandomNumber(), true) . ':' . microtime(true)), - 5, - 32 - ) - ); $isNewCustomer = !$object->getCustomerId() || $object->getCustomerId() === true; if ($isNewCustomer && $object->getCustomer()) { $object->setCustomerId($object->getCustomer()->getId()); @@ -169,7 +171,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object) } /** - * {@inheritdoc} + * @inheritdoc */ public function save(\Magento\Framework\Model\AbstractModel $object) { From b8e89a1d484fc0af009b57441910ada5a110b471 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Thu, 27 Aug 2020 02:19:17 +0800 Subject: [PATCH 062/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - fixed failed static test and added integration test --- .../Model/SynchronizeIdentities.php | 22 +-- .../Model/SynchronizeIdentitiesTest.php | 161 ++++++++++++++++++ .../MediaContentSynchronization/composer.json | 1 - 3 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php index 2715d5e49fd6b..cc91b7b932483 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -19,10 +19,6 @@ class SynchronizeIdentities implements SynchronizeIdentitiesInterface private const ENTITY_ID = 'entityId'; private const FIELD = 'field'; - private const MEDIA_CONTENT_TYPE = 'entity_type'; - private const MEDIA_CONTENT_ENTITY_ID = 'entity_id'; - private const MEDIA_CONTENT_FIELD = 'field'; - private const FIELD_CMS_PAGE = 'cms_page'; private const FIELD_CMS_BLOCK = 'cms_block'; @@ -77,18 +73,18 @@ public function execute(array $mediaContentIdentities): void foreach ($mediaContentIdentities as $identity) { $contentIdentity = $this->contentIdentityFactory->create( [ - self::ENTITY_TYPE => $identity[self::MEDIA_CONTENT_TYPE], - self::ENTITY_ID => $identity[self::MEDIA_CONTENT_ENTITY_ID], - self::FIELD => $identity[self::MEDIA_CONTENT_FIELD] + self::ENTITY_TYPE => $identity[self::ENTITY_TYPE], + self::ENTITY_ID => $identity[self::ENTITY_ID], + self::FIELD => $identity[self::FIELD] ] ); - if ($identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_PAGE - || $identity[self::MEDIA_CONTENT_TYPE] === self::FIELD_CMS_BLOCK + if ($identity[self::ENTITY_TYPE] === self::FIELD_CMS_PAGE + || $identity[self::ENTITY_TYPE] === self::FIELD_CMS_BLOCK ) { $content = $this->getCmsMediaContent( - $identity[self::MEDIA_CONTENT_TYPE], - $identity[self::MEDIA_CONTENT_ENTITY_ID] + $identity[self::ENTITY_TYPE], + $identity[self::ENTITY_ID] ); } else { $content = implode(PHP_EOL, $this->getEntityContents->execute($contentIdentity)); @@ -105,10 +101,10 @@ public function execute(array $mediaContentIdentities): void * Get cms media content from database * * @param string $tableName - * @param string $cmsId + * @param int $cmsId * @return string */ - private function getCmsMediaContent(string $tableName, string $cmsId): string + private function getCmsMediaContent(string $tableName, int $cmsId): string { $connection = $this->resourceConnection->getConnection(); $tableName = $this->resourceConnection->getTableName($tableName); diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php new file mode 100644 index 0000000000000..eb21723db2c17 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php @@ -0,0 +1,161 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronization\Test\Integration\Model; + +use Magento\Framework\Exception\IntegrationException; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; +use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for SynchronizeIdentities. + */ +class SynchronizeIdentitiesTest extends TestCase +{ + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var GetAssetIdsByContentIdentityInterface + */ + private $getAssetIds; + + /** + * @var GetContentByAssetIdsInterface + */ + private $getContentIdentities; + + /** + * @var SynchronizeIdentitiesInterface + */ + private $synchronizeIdentities; + + /** + * @var SynchronizeInterface + */ + private $synchronize; + + protected function setUp(): void + { + $this->contentIdentityFactory = Bootstrap::getObjectManager()->get(ContentIdentityInterfaceFactory::class); + $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); + $this->synchronizeIdentities = Bootstrap::getObjectManager()->get(SynchronizeIdentitiesInterface::class); + $this->synchronize = Bootstrap::getObjectManager()->get(SynchronizeInterface::class); + $this->getContentIdentities = Bootstrap::getObjectManager()->get(GetContentByAssetIdsInterface::class); + } + + /** + * @dataProvider filesProvider + * @magentoDataFixture Magento/MediaContentCatalog/_files/category_with_asset.php + * @magentoDataFixture Magento/MediaContentCatalog/_files/product_with_asset.php + * @magentoDataFixture Magento/MediaContentCms/_files/page_with_asset.php + * @magentoDataFixture Magento/MediaContentCms/_files/block_with_asset.php + * @magentoDataFixture Magento/MediaGallery/_files/media_asset.php + * @param array $mediaContentIdentities + * @throws IntegrationException + */ + public function testExecute(array $mediaContentIdentities): void + { + $this->assertNotEmpty($mediaContentIdentities); + $this->synchronizeIdentities->execute($mediaContentIdentities); + + foreach ($mediaContentIdentities as $contentIdentity) { + $assetId = 2020; + $categoryId = 28767; + $productId = 1567; + $pageId = 5; + $blockId = 1; + $identity = $this->contentIdentityFactory->create($contentIdentity); + $this->assertEquals([$assetId], $this->getAssetIds->execute($identity)); + + $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); + $this->assertEquals(4, count($synchronizedContentIdentities)); + $this->assertEquals($categoryId, $synchronizedContentIdentities[0]->getEntityId()); + $this->assertEquals($productId, $synchronizedContentIdentities[1]->getEntityId()); + $this->assertEquals($pageId, $synchronizedContentIdentities[2]->getEntityId()); + $this->assertEquals($blockId, $synchronizedContentIdentities[3]->getEntityId()); + } + } + + /** + * Data provider + * + * @return array + */ + public function filesProvider(): array + { + return [ + [ + [ + $this->getCategoryIdentities(), + $this->getProductIdentities(), + $this->getCmsPageIdentities(), + $this->getCmsBlockIdentities() + ] + ] + ]; + } + + /** + * Format category media content identities + */ + public function getCategoryIdentities() + { + $categoryId = 28767; + return $contentIdentity = [ + 'entityType' => 'catalog_category', + 'field' => 'description', + 'entityId' => $categoryId + ]; + } + + /** + * Format product media content identities + */ + public function getProductIdentities() + { + $productId = 1567; + return $contentIdentity = [ + 'entityType' => 'catalog_product', + 'field' => 'description', + 'entityId' => $productId + ]; + } + + /** + * Format cms page media content identities + */ + public function getCmsPageIdentities() + { + $pageId = 5; + return $contentIdentity = [ + 'entityType' => 'cms_page', + 'field' => 'content', + 'entityId' => $pageId + ]; + } + + /** + * Format cms block media content identities + */ + public function getCmsBlockIdentities() + { + $blockId = 1; + return $contentIdentity = [ + 'entityType' => 'cms_block', + 'field' => 'content', + 'entityId' => $blockId + ]; + } +} diff --git a/app/code/Magento/MediaContentSynchronization/composer.json b/app/code/Magento/MediaContentSynchronization/composer.json index f435f311deb46..9f0f4f9588ad6 100644 --- a/app/code/Magento/MediaContentSynchronization/composer.json +++ b/app/code/Magento/MediaContentSynchronization/composer.json @@ -6,7 +6,6 @@ "magento/framework": "*", "magento/framework-bulk": "*", "magento/module-media-content-synchronization-api": "*", - "magento/framework-message-queue": "*", "magento/module-media-content-api": "*", "magento/module-asynchronous-operations": "*" }, From 9fac5f7173db77e933e5a59e671151d9bf0b62c2 Mon Sep 17 00:00:00 2001 From: abdarrahman abouzaid <abdarrahman.abouzaid@gmail.com> Date: Thu, 27 Aug 2020 00:56:28 +0200 Subject: [PATCH 063/195] provide mftf test --- ...erRemoveErrorMessageBeforeApplyFilters.xml | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml new file mode 100644 index 0000000000000..3e4bac32d38e9 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<!-- Test XML Example --> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminGridFilterRemoveErrorMessageBeforeApplyFilters"> + <annotations> + <stories value="Reset Error Messages"/> + <title value="Remove Error Message Before Apply Filters"/> + <description value="Test log in to uI and Remove Error Message Before Apply Filters"/> + <testCaseId value="MC-142721"/> + <severity value="CRITICAL"/> + <group value="uI"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="NewRootCategory" stepKey="rootCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="rootCategory" /> + </createData> + <createData entity="defaultSimpleProduct" stepKey="createProduct2"> + <requiredEntity createDataKey="rootCategory" /> + </createData> + <!-- <createData entity="_defaultStore" stepKey="defaultStore"/> --> + + <!--Create website--> + <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> + <argument name="newWebsiteName" value="{{customWebsite.name}}"/> + <argument name="websiteCode" value="{{customWebsite.code}}"/> + </actionGroup> + <!-- Create second store --> + <actionGroup ref="CreateCustomStoreActionGroup" stepKey="createCustomStore"> + <argument name="website" value="{{customWebsite.name}}"/> + <argument name="store" value="{{customStoreGroup.name}}"/> + <argument name="rootCategory" value="$$rootCategory.name$$"/> + </actionGroup> + <!-- Create second store view --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createCustomStoreView"> + <argument name="StoreGroup" value="customStoreGroup"/> + <argument name="customStore" value="customStoreEN"/> + </actionGroup> + </before> + <after> + <magentoCLI command="config:set system/backup/functionality_enabled 0" stepKey="setEnableBackupToNo"/> + <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteProduct2" createDataKey="createProduct2"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <!--Filter created simple product in grid and add category and website created in create data--> + <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductCatalogPage"/> + <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> + <argument name="product" value="$$createProduct2$$"/> + </actionGroup> + <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowOfCreatedSimpleProduct"/> + <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <actionGroup ref="AddWebsiteToProductActionGroup" stepKey="updateSimpleProductAddingWebsiteCreated"> + <argument name="website" value="{{customWebsite.name}}"/> + </actionGroup> + + <!--Search updated simple product(from above step) in the grid by StoreView and Name--> + <actionGroup ref="FilterProductInGridByStoreViewAndNameActionGroup" stepKey="searchCreatedSimpleProductInGrid"> + <argument name="storeView" value="{{customStoreEN.name}}"/> + <argument name="productName" value="$$createProduct2.name$$"/> + </actionGroup> + + <!--Go to stores and delete website created in create data--> + <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="deleteWebsite"> + <argument name="websiteName" value="{{customWebsite.name}}"/> + </actionGroup> + + <!--Go to grid page and verify AssertErrorMessage--> + <actionGroup ref="AssertErrorMessageAfterDeletingWebsiteActionGroup" stepKey="verifyErrorMessage"> + <argument name="errorMessage" value="Something went wrong with processing the default view and we have restored the filter to its original state."/> + </actionGroup> + + <!--Apply new filters to verify error message is removed --> + <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickFiltersButton"/> + <click selector="{{AdminProductGridFilterSection.storeViewDropdown('Default Store View')}}" stepKey="clickStoreViewDropdown"/> + <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="$$createProduct.name$$" stepKey="fillProductNameInNameFilter"/> + <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> + <see selector="{{AdminProductGridFilterSection.nthRow('1')}}" userInput="$$createProduct.name$$" stepKey="seeFirstRowToVerifyProductVisibleInGrid"/> + <dontSeeElement selector="{{AdminMessagesSection.error}}" stepKey="dontSeeErrorMessage"/> + + </test> +</tests> From 3852c89cebccfef1da7242d73be40e4d831d3a97 Mon Sep 17 00:00:00 2001 From: abdarrahman abouzaid <abdarrahman.abouzaid@gmail.com> Date: Thu, 27 Aug 2020 01:05:10 +0200 Subject: [PATCH 064/195] update test file --- ...GridFilterRemoveErrorMessageBeforeApplyFilters.xml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml index 3e4bac32d38e9..c931e4f2b54c7 100644 --- a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml @@ -1,11 +1,13 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ + * Copyright (©) 2020 Pinpoint Designs LTD. All right's reserved. + * + * Author: abdarrahman.abouzaid@gmail.com + * Website: http://www.pinpointdesigns.co.uk + */ --> -<!-- Test XML Example --> +<!-- Test XML #28687 PR --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminGridFilterRemoveErrorMessageBeforeApplyFilters"> <annotations> @@ -28,7 +30,6 @@ <createData entity="defaultSimpleProduct" stepKey="createProduct2"> <requiredEntity createDataKey="rootCategory" /> </createData> - <!-- <createData entity="_defaultStore" stepKey="defaultStore"/> --> <!--Create website--> <actionGroup ref="AdminCreateWebsiteActionGroup" stepKey="createWebsite"> From 561c84944573aa9a6af120d198bdd95b04d62a1e Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Thu, 27 Aug 2020 16:03:05 +0800 Subject: [PATCH 065/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated integration test --- .../Model/SynchronizeIdentitiesTest.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php index eb21723db2c17..9660d81b34d77 100644 --- a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php @@ -72,19 +72,17 @@ public function testExecute(array $mediaContentIdentities): void foreach ($mediaContentIdentities as $contentIdentity) { $assetId = 2020; - $categoryId = 28767; - $productId = 1567; - $pageId = 5; - $blockId = 1; $identity = $this->contentIdentityFactory->create($contentIdentity); $this->assertEquals([$assetId], $this->getAssetIds->execute($identity)); $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); $this->assertEquals(4, count($synchronizedContentIdentities)); - $this->assertEquals($categoryId, $synchronizedContentIdentities[0]->getEntityId()); - $this->assertEquals($productId, $synchronizedContentIdentities[1]->getEntityId()); - $this->assertEquals($pageId, $synchronizedContentIdentities[2]->getEntityId()); - $this->assertEquals($blockId, $synchronizedContentIdentities[3]->getEntityId()); + + $syncedIds = []; + foreach ($synchronizedContentIdentities as $syncedContentIdentity) { + $syncedIds[] = (int)$syncedContentIdentity->getEntityId(); + } + $this->assertContains($contentIdentity['entityId'], $syncedIds); } } From 16f97b054b55302c6f53bd139b33895f250b908a Mon Sep 17 00:00:00 2001 From: Tu Nguyen <tuna@ecommage.com> Date: Thu, 27 Aug 2020 18:13:31 +0700 Subject: [PATCH 066/195] Remove space between textarea field in contact form --- app/code/Magento/Contact/view/frontend/templates/form.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Contact/view/frontend/templates/form.phtml b/app/code/Magento/Contact/view/frontend/templates/form.phtml index eee9f742a59a4..e9d0c065fd8bf 100644 --- a/app/code/Magento/Contact/view/frontend/templates/form.phtml +++ b/app/code/Magento/Contact/view/frontend/templates/form.phtml @@ -69,8 +69,8 @@ $viewModel = $block->getViewModel(); class="input-text" cols="5" rows="3" - data-validate="{required:true}"><?= $block->escapeHtml($viewModel->getUserComment()) ?> - </textarea> + data-validate="{required:true}" + ><?= $block->escapeHtml($viewModel->getUserComment()) ?></textarea> </div> </div> <?= $block->getChildHtml('form.additional.info') ?> From a36a924e63e55dbdd0423aa4ac9d7dee87053334 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Thu, 27 Aug 2020 20:36:23 +0800 Subject: [PATCH 067/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - fixed failed integration test --- .../Test/Integration/Model/SynchronizeIdentitiesTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php index 9660d81b34d77..4e7debf11d6b7 100644 --- a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php @@ -111,7 +111,7 @@ public function filesProvider(): array public function getCategoryIdentities() { $categoryId = 28767; - return $contentIdentity = [ + return [ 'entityType' => 'catalog_category', 'field' => 'description', 'entityId' => $categoryId @@ -124,7 +124,7 @@ public function getCategoryIdentities() public function getProductIdentities() { $productId = 1567; - return $contentIdentity = [ + return [ 'entityType' => 'catalog_product', 'field' => 'description', 'entityId' => $productId @@ -137,7 +137,7 @@ public function getProductIdentities() public function getCmsPageIdentities() { $pageId = 5; - return $contentIdentity = [ + return [ 'entityType' => 'cms_page', 'field' => 'content', 'entityId' => $pageId @@ -150,7 +150,7 @@ public function getCmsPageIdentities() public function getCmsBlockIdentities() { $blockId = 1; - return $contentIdentity = [ + return [ 'entityType' => 'cms_block', 'field' => 'content', 'entityId' => $blockId From d05b818a3a0b507fb0d97d5524b449db0b87a70a Mon Sep 17 00:00:00 2001 From: Serhiy Yelahin <serhiy.yelahin@transoftgroup.com> Date: Thu, 27 Aug 2020 17:07:41 +0300 Subject: [PATCH 068/195] MC-36953: [Magento On-Premise]: Tracking link in email redirects to 404 Page not Found --- .../Adminhtml/Order/Invoice/SaveTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php index 2dc5f5adc86d2..a09e656b90726 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -74,6 +74,21 @@ public function testAclNoAccess() parent::testAclNoAccess(); } + /** + * Checks that order protect code is not changing after invoice submitting + * + * @return void + */ + public function testOrderProtectCodePreserveAfterInvoiceSave(): void + { + $order = $this->prepareRequest(); + $protectCode = $order->getProtectCode(); + $this->dispatch($this->uri); + $invoicedOrder = $this->getOrder('100000001'); + + $this->assertEquals($protectCode, $invoicedOrder->getProtectCode()); + } + /** * @param array $params * @return \Magento\Sales\Api\Data\OrderInterface|null From 7517b21aa87f9e47aefa1ef0986f0198e6f4d70b Mon Sep 17 00:00:00 2001 From: abdarrahman abouzaid <abdarrahman.abouzaid@gmail.com> Date: Thu, 27 Aug 2020 19:24:07 +0200 Subject: [PATCH 069/195] provide PR requested changes --- ...terRemoveErrorMessageBeforeApplyFilters.xml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml index c931e4f2b54c7..09c87010b5743 100644 --- a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml @@ -1,27 +1,22 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- /** - * Copyright (©) 2020 Pinpoint Designs LTD. All right's reserved. - * - * Author: abdarrahman.abouzaid@gmail.com - * Website: http://www.pinpointdesigns.co.uk - */ + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ --> -<!-- Test XML #28687 PR --> + <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminGridFilterRemoveErrorMessageBeforeApplyFilters"> <annotations> <stories value="Reset Error Messages"/> <title value="Remove Error Message Before Apply Filters"/> - <description value="Test log in to uI and Remove Error Message Before Apply Filters"/> - <testCaseId value="MC-142721"/> + <description value="Test login to Admin UI and Remove Error Message Before Apply Filters"/> <severity value="CRITICAL"/> - <group value="uI"/> - <group value="mtf_migrated"/> + <group value="ui"/> </annotations> <before> - <magentoCLI command="config:set system/backup/functionality_enabled 1" stepKey="setEnableBackupToYes"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="NewRootCategory" stepKey="rootCategory"/> <createData entity="defaultSimpleProduct" stepKey="createProduct"> @@ -49,7 +44,6 @@ </actionGroup> </before> <after> - <magentoCLI command="config:set system/backup/functionality_enabled 0" stepKey="setEnableBackupToNo"/> <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> <deleteData stepKey="deleteProduct2" createDataKey="createProduct2"/> From 6a9f506261c85cc49c71a78c338e3c2dbf3e21af Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 28 Aug 2020 04:05:38 +0800 Subject: [PATCH 070/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated pull request with requested changes --- .../Model/Consume.php | 29 +++- .../Model/Publish.php | 7 +- .../MediaContentSynchronization/etc/di.xml | 1 - .../SynchronizeIdentitiesCatalog.php | 50 +++++++ .../SynchronizeIdentitiesCatalogTest.php} | 108 ++++++--------- .../SynchronizeIdentitiesCms.php} | 44 +----- .../SynchronizeIdentitiesCmsTest.php | 125 ++++++++++++++++++ 7 files changed, 246 insertions(+), 118 deletions(-) create mode 100644 app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php rename app/code/Magento/{MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php => MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php} (54%) rename app/code/Magento/{MediaContentSynchronization/Model/SynchronizeIdentities.php => MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php} (62%) create mode 100644 app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php diff --git a/app/code/Magento/MediaContentSynchronization/Model/Consume.php b/app/code/Magento/MediaContentSynchronization/Model/Consume.php index da18d733ff5ab..6aa8e6e831a25 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Consume.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Consume.php @@ -10,6 +10,7 @@ use Magento\AsynchronousOperations\Api\Data\OperationInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Serialize\SerializerInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; @@ -18,11 +19,20 @@ */ class Consume { + private const ENTITY_TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + /** * @var SerializerInterface */ private $serializer; + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + /** * @var SynchronizeInterface */ @@ -35,15 +45,18 @@ class Consume /** * @param SerializerInterface $serializer + * @param ContentIdentityInterfaceFactory $contentIdentityFactory * @param SynchronizeInterface $synchronize * @param SynchronizeIdentitiesInterface $synchronizeIdentities */ public function __construct( SerializerInterface $serializer, + ContentIdentityInterfaceFactory $contentIdentityFactory, SynchronizeInterface $synchronize, SynchronizeIdentitiesInterface $synchronizeIdentities ) { $this->serializer = $serializer; + $this->contentIdentityFactory = $contentIdentityFactory; $this->synchronize = $synchronize; $this->synchronizeIdentities = $synchronizeIdentities; } @@ -56,11 +69,19 @@ public function __construct( */ public function execute(OperationInterface $operation) : void { - $serializedData = $operation->getSerializedData(); - $identities = $this->serializer->unserialize($serializedData); - + $identities = $this->serializer->unserialize($operation->getSerializedData()); if (!empty($identities)) { - $this->synchronizeIdentities->execute($identities); + $contentIdentity = []; + foreach ($identities as $identity) { + $contentIdentity[] = $this->contentIdentityFactory->create( + [ + self::ENTITY_TYPE => $identity[self::ENTITY_TYPE], + self::ENTITY_ID => $identity[self::ENTITY_ID], + self::FIELD => $identity[self::FIELD] + ] + ); + } + $this->synchronizeIdentities->execute($contentIdentity); } else { $this->synchronize->execute(); } diff --git a/app/code/Magento/MediaContentSynchronization/Model/Publish.php b/app/code/Magento/MediaContentSynchronization/Model/Publish.php index 5b7d0768955fc..d9e89fea7d4d2 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Publish.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Publish.php @@ -68,14 +68,11 @@ public function __construct( */ public function execute(array $contentIdentities = []) : void { - $bulkUuid = $this->identityService->generateId(); - $dataToEncode = $this->serializer->serialize($contentIdentities); - $data = [ 'data' => [ - 'bulk_uuid' => $bulkUuid, + 'bulk_uuid' => $this->identityService->generateId(), 'topic_name' => self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION, - 'serialized_data' => $dataToEncode, + 'serialized_data' => $this->serializer->serialize($contentIdentities), 'status' => OperationInterface::STATUS_TYPE_OPEN, ] ]; diff --git a/app/code/Magento/MediaContentSynchronization/etc/di.xml b/app/code/Magento/MediaContentSynchronization/etc/di.xml index e5347f1a11561..d4615c15206e5 100644 --- a/app/code/Magento/MediaContentSynchronization/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronization/etc/di.xml @@ -7,7 +7,6 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface" type="Magento\MediaContentSynchronization\Model\Synchronize"/> - <preference for="Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface" type="Magento\MediaContentSynchronization\Model\SynchronizeIdentities"/> <type name="Magento\Framework\Console\CommandListInterface"> <arguments> <argument name="commands" xsi:type="array"> diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php b/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php new file mode 100644 index 0000000000000..068d733e14605 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronizationCatalog\Model\Synchronizer; + +use Magento\MediaContentApi\Api\UpdateContentAssetLinksInterface; +use Magento\MediaContentApi\Model\GetEntityContentsInterface; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; + +class SynchronizeIdentitiesCatalog implements SynchronizeIdentitiesInterface +{ + /** + * @var UpdateContentAssetLinksInterface + */ + private $updateContentAssetLinks; + + /** + * @var GetEntityContentsInterface + */ + private $getEntityContents; + + /** + * @param UpdateContentAssetLinksInterface $updateContentAssetLinks + * @param GetEntityContentsInterface $getEntityContents + */ + public function __construct( + UpdateContentAssetLinksInterface $updateContentAssetLinks, + GetEntityContentsInterface $getEntityContents + ) { + $this->updateContentAssetLinks = $updateContentAssetLinks; + $this->getEntityContents = $getEntityContents; + } + + /** + * @inheritDoc + */ + public function execute(array $mediaContentIdentities): void + { + foreach ($mediaContentIdentities as $identity) { + $this->updateContentAssetLinks->execute( + $identity, + implode(PHP_EOL, $this->getEntityContents->execute($identity)) + ); + } + } +} diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php b/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php similarity index 54% rename from app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php rename to app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php index 4e7debf11d6b7..ae4bb77881834 100644 --- a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/SynchronizeIdentitiesTest.php +++ b/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php @@ -5,9 +5,10 @@ */ declare(strict_types=1); -namespace Magento\MediaContentSynchronization\Test\Integration\Model; +namespace Magento\MediaContentSynchronizationCatalog\Test\Integration\Model\Synchronizer; use Magento\Framework\Exception\IntegrationException; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterface; use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; @@ -17,10 +18,14 @@ use PHPUnit\Framework\TestCase; /** - * Test for SynchronizeIdentities. + * Test for catalog SynchronizeIdentities. */ -class SynchronizeIdentitiesTest extends TestCase +class SynchronizeIdentitiesCatalogTest extends TestCase { + private const ENTITY_TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + /** * @var ContentIdentityInterfaceFactory */ @@ -59,30 +64,37 @@ protected function setUp(): void * @dataProvider filesProvider * @magentoDataFixture Magento/MediaContentCatalog/_files/category_with_asset.php * @magentoDataFixture Magento/MediaContentCatalog/_files/product_with_asset.php - * @magentoDataFixture Magento/MediaContentCms/_files/page_with_asset.php - * @magentoDataFixture Magento/MediaContentCms/_files/block_with_asset.php * @magentoDataFixture Magento/MediaGallery/_files/media_asset.php - * @param array $mediaContentIdentities + * @param ContentIdentityInterface[] $mediaContentIdentities * @throws IntegrationException */ public function testExecute(array $mediaContentIdentities): void { - $this->assertNotEmpty($mediaContentIdentities); - $this->synchronizeIdentities->execute($mediaContentIdentities); + $contentIdentities = []; + foreach ($mediaContentIdentities as $mediaContentIdentity) { + $contentIdentities[] = $this->contentIdentityFactory->create( + [ + self::ENTITY_TYPE => $mediaContentIdentity[self::ENTITY_TYPE], + self::ENTITY_ID => $mediaContentIdentity[self::ENTITY_ID], + self::FIELD => $mediaContentIdentity[self::FIELD] + ] + ); + } - foreach ($mediaContentIdentities as $contentIdentity) { - $assetId = 2020; - $identity = $this->contentIdentityFactory->create($contentIdentity); - $this->assertEquals([$assetId], $this->getAssetIds->execute($identity)); + $this->assertNotEmpty($contentIdentities); + $this->synchronizeIdentities->execute($contentIdentities); + foreach ($contentIdentities as $contentIdentity) { + $assetId = 2020; + $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); - $this->assertEquals(4, count($synchronizedContentIdentities)); + $this->assertEquals(2, count($synchronizedContentIdentities)); $syncedIds = []; foreach ($synchronizedContentIdentities as $syncedContentIdentity) { - $syncedIds[] = (int)$syncedContentIdentity->getEntityId(); + $syncedIds[] = $syncedContentIdentity->getEntityId(); } - $this->assertContains($contentIdentity['entityId'], $syncedIds); + $this->assertContains($contentIdentity->getEntityId(), $syncedIds); } } @@ -96,64 +108,18 @@ public function filesProvider(): array return [ [ [ - $this->getCategoryIdentities(), - $this->getProductIdentities(), - $this->getCmsPageIdentities(), - $this->getCmsBlockIdentities() + [ + 'entityType' => 'catalog_category', + 'field' => 'description', + 'entityId' => 28767 + ], + [ + 'entityType' => 'catalog_product', + 'field' => 'description', + 'entityId' => 1567 + ] ] ] ]; } - - /** - * Format category media content identities - */ - public function getCategoryIdentities() - { - $categoryId = 28767; - return [ - 'entityType' => 'catalog_category', - 'field' => 'description', - 'entityId' => $categoryId - ]; - } - - /** - * Format product media content identities - */ - public function getProductIdentities() - { - $productId = 1567; - return [ - 'entityType' => 'catalog_product', - 'field' => 'description', - 'entityId' => $productId - ]; - } - - /** - * Format cms page media content identities - */ - public function getCmsPageIdentities() - { - $pageId = 5; - return [ - 'entityType' => 'cms_page', - 'field' => 'content', - 'entityId' => $pageId - ]; - } - - /** - * Format cms block media content identities - */ - public function getCmsBlockIdentities() - { - $blockId = 1; - return [ - 'entityType' => 'cms_block', - 'field' => 'content', - 'entityId' => $blockId - ]; - } } diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php similarity index 62% rename from app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php rename to app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php index cc91b7b932483..5685e284e20c3 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php +++ b/app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php @@ -5,26 +5,19 @@ */ declare(strict_types=1); -namespace Magento\MediaContentSynchronization\Model; +namespace Magento\MediaContentSynchronizationCms\Model\Synchronizer; use Magento\Framework\App\ResourceConnection; -use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; use Magento\MediaContentApi\Api\UpdateContentAssetLinksInterface; use Magento\MediaContentApi\Model\GetEntityContentsInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -class SynchronizeIdentities implements SynchronizeIdentitiesInterface +class SynchronizeIdentitiesCms implements SynchronizeIdentitiesInterface { - private const ENTITY_TYPE = 'entityType'; - private const ENTITY_ID = 'entityId'; - private const FIELD = 'field'; - private const FIELD_CMS_PAGE = 'cms_page'; private const FIELD_CMS_BLOCK = 'cms_block'; - private const ID_CMS_PAGE = 'page_id'; private const ID_CMS_BLOCK = 'block_id'; - private const COLUMN_CMS_CONTENT = 'content'; /** @@ -32,11 +25,6 @@ class SynchronizeIdentities implements SynchronizeIdentitiesInterface */ private $resourceConnection; - /** - * @var ContentIdentityInterfaceFactory - */ - private $contentIdentityFactory; - /** * @var UpdateContentAssetLinksInterface */ @@ -49,18 +37,15 @@ class SynchronizeIdentities implements SynchronizeIdentitiesInterface /** * @param ResourceConnection $resourceConnection - * @param ContentIdentityInterfaceFactory $contentIdentityFactory * @param UpdateContentAssetLinksInterface $updateContentAssetLinks * @param GetEntityContentsInterface $getEntityContents */ public function __construct( ResourceConnection $resourceConnection, - ContentIdentityInterfaceFactory $contentIdentityFactory, UpdateContentAssetLinksInterface $updateContentAssetLinks, GetEntityContentsInterface $getEntityContents ) { $this->resourceConnection = $resourceConnection; - $this->contentIdentityFactory = $contentIdentityFactory; $this->updateContentAssetLinks = $updateContentAssetLinks; $this->getEntityContents = $getEntityContents; } @@ -71,29 +56,14 @@ public function __construct( public function execute(array $mediaContentIdentities): void { foreach ($mediaContentIdentities as $identity) { - $contentIdentity = $this->contentIdentityFactory->create( - [ - self::ENTITY_TYPE => $identity[self::ENTITY_TYPE], - self::ENTITY_ID => $identity[self::ENTITY_ID], - self::FIELD => $identity[self::FIELD] - ] - ); - - if ($identity[self::ENTITY_TYPE] === self::FIELD_CMS_PAGE - || $identity[self::ENTITY_TYPE] === self::FIELD_CMS_BLOCK + if ($identity->getEntityType() === self::FIELD_CMS_PAGE + || $identity->getEntityType() === self::FIELD_CMS_BLOCK ) { - $content = $this->getCmsMediaContent( - $identity[self::ENTITY_TYPE], - $identity[self::ENTITY_ID] + $this->updateContentAssetLinks->execute( + $identity, + $this->getCmsMediaContent($identity->getEntityType(), (int)$identity->getEntityId()) ); - } else { - $content = implode(PHP_EOL, $this->getEntityContents->execute($contentIdentity)); } - - $this->updateContentAssetLinks->execute( - $contentIdentity, - $content - ); } } diff --git a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php new file mode 100644 index 0000000000000..48841ecf24658 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronizationCms\Test\Integration\Model\Synchronizer; + +use Magento\Framework\Exception\IntegrationException; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; +use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for CMS SynchronizeIdentities. + */ +class SynchronizeIdentitiesCmsTest extends TestCase +{ + private const ENTITY_TYPE = 'entityType'; + private const ENTITY_ID = 'entityId'; + private const FIELD = 'field'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var GetAssetIdsByContentIdentityInterface + */ + private $getAssetIds; + + /** + * @var GetContentByAssetIdsInterface + */ + private $getContentIdentities; + + /** + * @var SynchronizeIdentitiesInterface + */ + private $synchronizeIdentities; + + /** + * @var SynchronizeInterface + */ + private $synchronize; + + protected function setUp(): void + { + $this->contentIdentityFactory = Bootstrap::getObjectManager()->get(ContentIdentityInterfaceFactory::class); + $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); + $this->synchronizeIdentities = Bootstrap::getObjectManager()->get(SynchronizeIdentitiesInterface::class); + $this->synchronize = Bootstrap::getObjectManager()->get(SynchronizeInterface::class); + $this->getContentIdentities = Bootstrap::getObjectManager()->get(GetContentByAssetIdsInterface::class); + } + + /** + * @dataProvider filesProvider + * @magentoDataFixture Magento/MediaContentCms/_files/page_with_asset.php + * @magentoDataFixture Magento/MediaContentCms/_files/block_with_asset.php + * @magentoDataFixture Magento/MediaGallery/_files/media_asset.php + * @param ContentIdentityInterface[] $mediaContentIdentities + * @throws IntegrationException + */ + public function testExecute(array $mediaContentIdentities): void + { + $contentIdentities = []; + foreach ($mediaContentIdentities as $mediaContentIdentity) { + $contentIdentities[] = $this->contentIdentityFactory->create( + [ + self::ENTITY_TYPE => $mediaContentIdentity[self::ENTITY_TYPE], + self::ENTITY_ID => $mediaContentIdentity[self::ENTITY_ID], + self::FIELD => $mediaContentIdentity[self::FIELD] + ] + ); + } + + $this->assertNotEmpty($contentIdentities); + $this->synchronizeIdentities->execute($contentIdentities); + + foreach ($contentIdentities as $contentIdentity) { + $assetId = 2020; + $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); + $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); + $this->assertEquals(2, count($synchronizedContentIdentities)); + + $syncedIds = []; + foreach ($synchronizedContentIdentities as $syncedContentIdentity) { + $syncedIds[] = $syncedContentIdentity->getEntityId(); + } + $this->assertContains($contentIdentity->getEntityId(), $syncedIds); + } + } + + /** + * Data provider + * + * @return array + */ + public function filesProvider(): array + { + return [ + [ + [ + [ + 'entityType' => 'cms_page', + 'field' => 'content', + 'entityId' => 5 + ], + [ + 'entityType' => 'cms_block', + 'field' => 'content', + 'entityId' => 1 + ] + ] + ] + ]; + } +} From bce263f1d8fe5efc7ca8aaa8162cc17a3e1a17b6 Mon Sep 17 00:00:00 2001 From: SmVladyslav <vlatame.tsg@gmail.com> Date: Fri, 28 Aug 2020 13:06:03 +0300 Subject: [PATCH 071/195] MC-37117: Unexpected loading of the "New Review" page in the Admin-panel --- app/code/Magento/Review/Block/Adminhtml/Add.php | 9 ++------- .../Magento/Review/Block/Adminhtml/Add/Form.php | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/Review/Block/Adminhtml/Add.php b/app/code/Magento/Review/Block/Adminhtml/Add.php index 2edd76879d8dc..5f739b2595418 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Add.php +++ b/app/code/Magento/Review/Block/Adminhtml/Add.php @@ -27,15 +27,10 @@ protected function _construct() $this->_mode = 'add'; $this->buttonList->update('save', 'label', __('Save Review')); $this->buttonList->update('save', 'id', 'save_button'); + $this->buttonList->update('save', 'style', 'display: none;'); $this->buttonList->update('reset', 'id', 'reset_button'); + $this->buttonList->update('reset', 'style', 'display: none;'); $this->buttonList->update('reset', 'onclick', 'window.review.formReset()'); - $this->_formScripts[] = ' - require(["prototype"], function(){ - toggleParentVis("add_review_form"); - toggleVis("save_button"); - toggleVis("reset_button"); - }); - '; // @codingStandardsIgnoreStart $this->_formInitScripts[] = ' require(["jquery","Magento_Review/js/rating","prototype"], function(jQuery, rating){ diff --git a/app/code/Magento/Review/Block/Adminhtml/Add/Form.php b/app/code/Magento/Review/Block/Adminhtml/Add/Form.php index 04e6343eb43ca..efffa7a02678a 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Add/Form.php +++ b/app/code/Magento/Review/Block/Adminhtml/Add/Form.php @@ -5,6 +5,9 @@ */ namespace Magento\Review\Block\Adminhtml\Add; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Helper\SecureHtmlRenderer; + /** * Adminhtml add product review form * @@ -26,6 +29,11 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic */ protected $_systemStore; + /** + * @var SecureHtmlRenderer + */ + private $secureRenderer; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Registry $registry @@ -33,6 +41,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic * @param \Magento\Store\Model\System\Store $systemStore * @param \Magento\Review\Helper\Data $reviewData * @param array $data + * @param SecureHtmlRenderer|null $htmlRenderer */ public function __construct( \Magento\Backend\Block\Template\Context $context, @@ -40,10 +49,12 @@ public function __construct( \Magento\Framework\Data\FormFactory $formFactory, \Magento\Store\Model\System\Store $systemStore, \Magento\Review\Helper\Data $reviewData, - array $data = [] + array $data = [], + ?SecureHtmlRenderer $htmlRenderer = null ) { $this->_reviewData = $reviewData; $this->_systemStore = $systemStore; + $this->secureRenderer = $htmlRenderer ?: ObjectManager::getInstance()->get(SecureHtmlRenderer::class); parent::__construct($context, $registry, $formFactory, $data); } @@ -59,6 +70,8 @@ protected function _prepareForm() $form = $this->_formFactory->create(); $fieldset = $form->addFieldset('add_review_form', ['legend' => __('Review Details')]); + $beforeHtml = $this->secureRenderer->renderStyleAsTag('display: none;', '#edit_form'); + $fieldset->setBeforeElementHtml($beforeHtml); $fieldset->addField('product_name', 'note', ['label' => __('Product'), 'text' => 'product_name']); From 2fb949e4cc78f3c7fa10fce5a177ec2ce9dd4bc4 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 28 Aug 2020 18:49:12 +0800 Subject: [PATCH 072/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated pull request with requested changes --- .../Model/SynchronizeIdentities.php | 109 ++++++++++++++++++ .../MediaContentSynchronization/etc/di.xml | 1 + .../Model/SynchronizerIdentitiesPool.php | 47 ++++++++ .../SynchronizeIdentitiesCatalog.php | 15 ++- .../etc/di.xml | 9 ++ .../MediaContentSynchronizationCms/etc/di.xml | 9 ++ 6 files changed, 186 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php create mode 100644 app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php new file mode 100644 index 0000000000000..0cd9b9bca6784 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -0,0 +1,109 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronization\Model; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\FlagManager; +use Magento\Framework\Stdlib\DateTime\DateTimeFactory; +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; +use Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool; +use Psr\Log\LoggerInterface; + +/** + * Batch Synchronize content with assets + */ +class SynchronizeIdentities implements SynchronizeIdentitiesInterface +{ + private const LAST_EXECUTION_TIME_CODE = 'media_content_last_execution'; + + /** + * @var DateTimeFactory + */ + private $dateFactory; + + /** + * @var FlagManager + */ + private $flagManager; + + /** + * @var LoggerInterface + */ + private $log; + + /** + * @var SynchronizerIdentitiesPool + */ + private $synchronizerIdentitiesPool; + + /** + * @var RemoveObsoleteContentAsset + */ + private $removeObsoleteContent; + + /** + * @param RemoveObsoleteContentAsset $removeObsoleteContent + * @param DateTimeFactory $dateFactory + * @param FlagManager $flagManager + * @param LoggerInterface $log + * @param SynchronizerIdentitiesPool $synchronizerIdentitiesPool + */ + public function __construct( + RemoveObsoleteContentAsset $removeObsoleteContent, + DateTimeFactory $dateFactory, + FlagManager $flagManager, + LoggerInterface $log, + SynchronizerIdentitiesPool $synchronizerIdentitiesPool + ) { + $this->removeObsoleteContent = $removeObsoleteContent; + $this->dateFactory = $dateFactory; + $this->flagManager = $flagManager; + $this->log = $log; + $this->synchronizerIdentitiesPool = $synchronizerIdentitiesPool; + } + + /** + * @inheritdoc + */ + public function execute(array $mediaContentIdentities): void + { + $failed = []; + + foreach ($this->synchronizerIdentitiesPool->get() as $name => $synchronizers) { + try { + $synchronizers->execute($mediaContentIdentities); + } catch (\Exception $exception) { + $this->log->critical($exception); + $failed[] = $name; + } + } + + if (!empty($failed)) { + throw new LocalizedException( + __( + 'Failed to execute the following content synchronizers: %synchronizers', + [ + 'synchronizers' => implode(', ', $failed) + ] + ) + ); + } + + $this->setLastExecutionTime(); + $this->removeObsoleteContent->execute(); + } + + /** + * Set last synchronizer execution time + */ + private function setLastExecutionTime(): void + { + $currentTime = $this->dateFactory->create()->gmtDate(); + $this->flagManager->saveFlag(self::LAST_EXECUTION_TIME_CODE, $currentTime); + } +} diff --git a/app/code/Magento/MediaContentSynchronization/etc/di.xml b/app/code/Magento/MediaContentSynchronization/etc/di.xml index d4615c15206e5..e5347f1a11561 100644 --- a/app/code/Magento/MediaContentSynchronization/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronization/etc/di.xml @@ -7,6 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface" type="Magento\MediaContentSynchronization\Model\Synchronize"/> + <preference for="Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface" type="Magento\MediaContentSynchronization\Model\SynchronizeIdentities"/> <type name="Magento\Framework\Console\CommandListInterface"> <arguments> <argument name="commands" xsi:type="array"> diff --git a/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php b/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php new file mode 100644 index 0000000000000..2f4a503625e91 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronizationApi\Model; + +use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; + +class SynchronizerIdentitiesPool +{ + /** + * Content with assets synchronizers + * + * @var SynchronizeIdentitiesInterface[] + */ + private $synchronizers; + + /** + * @param SynchronizeIdentitiesInterface[] $synchronizers + */ + public function __construct( + array $synchronizers = [] + ) { + foreach ($synchronizers as $synchronizer) { + if (!$synchronizer instanceof SynchronizeIdentitiesInterface) { + throw new \InvalidArgumentException( + get_class($synchronizer) . ' must implement ' . SynchronizeIdentitiesInterface::class + ); + } + } + + $this->synchronizers = $synchronizers; + } + + /** + * Get all synchronizers from the pool + * + * @return SynchronizeIdentitiesInterface[] + */ + public function get(): array + { + return $this->synchronizers; + } +} diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php b/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php index 068d733e14605..a57b8eb2041cb 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php +++ b/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php @@ -13,6 +13,9 @@ class SynchronizeIdentitiesCatalog implements SynchronizeIdentitiesInterface { + private const FIELD_CATALOG_PRODUCT = 'catalog_product'; + private const FIELD_CATALOG_CATEGORY = 'catalog_category'; + /** * @var UpdateContentAssetLinksInterface */ @@ -41,10 +44,14 @@ public function __construct( public function execute(array $mediaContentIdentities): void { foreach ($mediaContentIdentities as $identity) { - $this->updateContentAssetLinks->execute( - $identity, - implode(PHP_EOL, $this->getEntityContents->execute($identity)) - ); + if ($identity->getEntityType() === self::FIELD_CATALOG_PRODUCT + || $identity->getEntityType() === self::FIELD_CATALOG_CATEGORY + ) { + $this->updateContentAssetLinks->execute( + $identity, + implode(PHP_EOL, $this->getEntityContents->execute($identity)) + ); + } } } } diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml b/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml index 8cc86fde8fbcd..7c391faf3f955 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml @@ -38,4 +38,13 @@ </argument> </arguments> </type> + <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool"> + <arguments> + <argument name="synchronizers" xsi:type="array"> + <item name="media_content_catalog_synchronizeIdentity" + xsi:type="object">Magento\MediaContentSynchronizationCatalog\Model\Synchronizer\SynchronizeIdentitiesCatalog + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml b/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml index 7def330298789..43a863dc95764 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml @@ -14,6 +14,15 @@ </argument> </arguments> </type> + <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool"> + <arguments> + <argument name="synchronizers" xsi:type="array"> + <item name="media_content_cms_synchronizeIdentity" + xsi:type="object">Magento\MediaContentSynchronizationCms\Model\Synchronizer\SynchronizeIdentitiesCms + </item> + </argument> + </arguments> + </type> <type name="Magento\MediaContentSynchronizationApi\Model\GetEntitiesInterface"> <arguments> <argument name="entities" xsi:type="array"> From 30cd67d88ab3c125da2c0e8a1b00a0182d3a4620 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 28 Aug 2020 14:19:45 +0300 Subject: [PATCH 073/195] MC-37196: Admin: invoice order for customer --- .../Order/Invoice/Create/ItemsTest.php | 86 ++++++++ .../Sales/Block/Adminhtml/Order/ViewTest.php | 115 ++++++++++ .../Invoice/AbstractInvoiceControllerTest.php | 98 +++++---- .../Order/Invoice/AddCommentTest.php | 40 +--- .../Adminhtml/Order/Invoice/NewActionTest.php | 78 +++++++ .../Adminhtml/Order/Invoice/SaveTest.php | 198 +++++++++++++++--- .../Adminhtml/Order/Invoice/StartTest.php | 66 ++++++ .../Adminhtml/Order/Invoice/UpdateQtyTest.php | 123 +++++++++++ 8 files changed, 707 insertions(+), 97 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php new file mode 100644 index 0000000000000..1b5772cec66de --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Invoice/Create/ItemsTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order\Invoice\Create; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\Sales\Model\OrderFactory; +use Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks invoiced items grid appearance + * + * @see \Magento\Sales\Block\Adminhtml\Order\Invoice\Create\Items + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class ItemsTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Items */ + private $block; + + /** @var OrderFactory */ + private $orderFactory; + + /** @var Registry */ + private $registry; + + /** @var CollectionFactory */ + private $invoiceCollectionFactory; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Items::class); + $this->orderFactory = $this->objectManager->get(OrderFactory::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->invoiceCollectionFactory = $this->objectManager->get(CollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->registry->unregister('current_invoice'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Sales/_files/invoice.php + * + * @return void + */ + public function testGetUpdateButtonHtml(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('100000001'); + $invoice = $this->invoiceCollectionFactory->create()->setOrderFilter($order)->setPageSize(1)->getFirstItem(); + $this->registry->unregister('current_invoice'); + $this->registry->register('current_invoice', $invoice); + $this->block->toHtml(); + $button = $this->block->getChildBlock('update_button'); + $this->assertEquals((string)__('Update Qty\'s'), (string)$button->getLabel()); + $this->assertStringContainsString( + sprintf('sales/index/updateQty/order_id/%u/', (int)$order->getEntityId()), + $button->getOnClick() + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php new file mode 100644 index 0000000000000..a78c221cb5f84 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/ViewTest.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order; + +use Magento\Backend\Model\Search\AuthorizationMock; +use Magento\Framework\Authorization; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\OrderFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Checks order create block + * + * @see \Magento\Sales\Block\Adminhtml\Order\View + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class ViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Registry */ + private $registry; + + /** @var OrderFactory */ + private $orderFactory; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->objectManager->addSharedInstance( + $this->objectManager->get(AuthorizationMock::class), + Authorization::class + ); + $this->registry = $this->objectManager->get(Registry::class); + $this->orderFactory = $this->objectManager->get(OrderFactory::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->registry->unregister('sales_order'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testInvoiceButton(): void + { + $this->registerOrder('100000001'); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + '//button[@id=\'order_invoice\']', + $this->layout->createBlock(View::class)->getButtonsHtml() + ) + ); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php + * + * @return void + */ + public function testInvoiceButtonIsNotVisible(): void + { + $this->registerOrder('100000001'); + $this->assertEmpty( + Xpath::getElementsCountForXpath( + '//button[@id=\'order_invoice\']', + $this->layout->createBlock(View::class)->getButtonsHtml() + ) + ); + } + + /** + * Register order + * + * @param OrderInterface $order + * @return void + */ + private function registerOrder(string $orderIncrementId): void + { + $order = $this->orderFactory->create()->loadByIncrementId($orderIncrementId); + $this->registry->unregister('sales_order'); + $this->registry->register('sales_order', $order); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php index 726ba697beb12..3c26a53424d81 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AbstractInvoiceControllerTest.php @@ -7,39 +7,34 @@ namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; -use Magento\Framework\Api\SearchCriteria; use Magento\Framework\Api\SearchCriteriaBuilder; -use Magento\Framework\Data\Form\FormKey; +use Magento\Framework\App\Request\Http; use Magento\Sales\Api\Data\InvoiceInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\OrderRepository; +use Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory; use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\TestCase\AbstractBackendController; /** * Abstract backend invoice test. */ -class AbstractInvoiceControllerTest extends AbstractBackendController +abstract class AbstractInvoiceControllerTest extends AbstractBackendController { - /** - * @var TransportBuilderMock - */ + /** @var TransportBuilderMock */ protected $transportBuilder; - /** - * @var OrderRepository - */ - protected $orderRepository; + /** @var string */ + protected $resource = 'Magento_Sales::sales_invoice'; - /** - * @var FormKey - */ - protected $formKey; + /** @var OrderRepository */ + private $orderRepository; - /** - * @var string - */ - protected $resource = 'Magento_Sales::sales_invoice'; + /** @var SearchCriteriaBuilder */ + private $searchCriteriaBuilder; + + /** @var CollectionFactory */ + private $invoiceCollectionFactory; /** * @inheritdoc @@ -47,46 +42,71 @@ class AbstractInvoiceControllerTest extends AbstractBackendController protected function setUp(): void { parent::setUp(); + $this->transportBuilder = $this->_objectManager->get(TransportBuilderMock::class); $this->orderRepository = $this->_objectManager->get(OrderRepository::class); - $this->formKey = $this->_objectManager->get(FormKey::class); + $this->searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); + $this->invoiceCollectionFactory = $this->_objectManager->get(CollectionFactory::class); } /** + * Retrieve order + * * @param string $incrementalId * @return OrderInterface|null */ - protected function getOrder(string $incrementalId) + protected function getOrder(string $incrementalId): ?OrderInterface { - /** @var SearchCriteria $searchCriteria */ - $searchCriteria = $this->_objectManager->create(SearchCriteriaBuilder::class) - ->addFilter(OrderInterface::INCREMENT_ID, $incrementalId) + $searchCriteria = $this->searchCriteriaBuilder->addFilter(OrderInterface::INCREMENT_ID, $incrementalId) ->create(); - $orders = $this->orderRepository->getList($searchCriteria)->getItems(); - /** @var OrderInterface $order */ - $order = reset($orders); - return $order; + return reset($orders); } /** - * @param OrderInterface $order + * Get firs order invoice + * + * @param OrderInterface|int $order * @return InvoiceInterface */ - protected function getInvoiceByOrder(OrderInterface $order): InvoiceInterface + protected function getInvoiceByOrder($order): InvoiceInterface { - /** @var \Magento\Sales\Model\ResourceModel\Order\Invoice\Collection $invoiceCollection */ - $invoiceCollection = $this->_objectManager->create( - \Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory::class - )->create(); + $invoiceCollection = $this->invoiceCollectionFactory->create(); - /** @var InvoiceInterface $invoice */ - $invoice = $invoiceCollection - ->setOrderFilter($order) - ->setPageSize(1) - ->getFirstItem(); + return $invoiceCollection->setOrderFilter($order)->setPageSize(1)->getFirstItem(); + } - return $invoice; + /** + * Prepare request + * + * @param array $postParams + * @param array $params + * @return void + */ + protected function prepareRequest(array $postParams = [], array $params = []): void + { + $this->getRequest()->setMethod(Http::METHOD_POST); + $this->getRequest()->setParams($params); + $this->getRequest()->setPostValue($postParams); + } + + /** + * Normalize post parameters + * + * @param array $items + * @param string $commentText + * @param bool $doShipment + * @return array + */ + protected function hydratePost(array $items, string $commentText = '', $doShipment = false): array + { + return [ + 'invoice' => [ + 'items' => $items, + 'comment_text' => $commentText, + 'do_shipment' => $doShipment + ], + ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php index ee59a55acd9b1..c7711e8897696 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/AddCommentTest.php @@ -30,10 +30,11 @@ class AddCommentTest extends AbstractInvoiceControllerTest public function testSendEmailOnAddInvoiceComment(): void { $comment = 'Test Invoice Comment'; - $order = $this->prepareRequest( - [ - 'comment' => ['comment' => $comment, 'is_customer_notified' => true], - ] + $order = $this->getOrder('100000001'); + $invoice = $this->getInvoiceByOrder($order); + $this->prepareRequest( + ['comment' => ['comment' => $comment, 'is_customer_notified' => true]], + ['id' => $invoice->getEntityId()] ); $this->dispatch('backend/sales/order_invoice/addComment'); @@ -41,6 +42,7 @@ public function testSendEmailOnAddInvoiceComment(): void $this->assertStringContainsString($comment, $html); $message = $this->transportBuilder->getSentMessage(); + $this->assertNotNull($message); $subject = __('Update to your %1 invoice', $order->getStore()->getFrontendName())->render(); $messageConstraint = $this->logicalAnd( new StringContains($order->getCustomerName()), @@ -55,7 +57,8 @@ public function testSendEmailOnAddInvoiceComment(): void ); $this->assertEquals($message->getSubject(), $subject); - $this->assertThat($message->getBody()->getParts()[0]->getRawContent(), $messageConstraint); + $bodyParts = $message->getBody()->getParts(); + $this->assertThat(reset($bodyParts)->getRawContent(), $messageConstraint); } /** @@ -63,7 +66,7 @@ public function testSendEmailOnAddInvoiceComment(): void */ public function testAclHasAccess() { - $this->prepareRequest(['comment' => ['comment' => 'Comment']]); + $this->prepareRequest(); parent::testAclHasAccess(); } @@ -73,31 +76,8 @@ public function testAclHasAccess() */ public function testAclNoAccess() { - $this->prepareRequest(['comment' => ['comment' => 'Comment']]); + $this->prepareRequest(); parent::testAclNoAccess(); } - - /** - * @param array $params - * @return \Magento\Sales\Api\Data\OrderInterface|null - */ - private function prepareRequest(array $params = []) - { - $order = $this->getOrder('100000001'); - $invoice = $this->getInvoiceByOrder($order); - - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParams( - [ - 'id' => $invoice->getEntityId(), - 'form_key' => $this->formKey->getFormKey(), - ] - ); - - $data = $params ?? []; - $this->getRequest()->setPostValue($data); - - return $order; - } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php new file mode 100644 index 0000000000000..c8444c827d2e4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/NewActionTest.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\Sales\Model\OrderFactory; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test for new invoice action + * + * @see \Magento\Sales\Controller\Adminhtml\Order\Invoice\NewAction + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class NewActionTest extends AbstractBackendController +{ + /** @var OrderFactory */ + private $orderFactory; + + /** @var Escaper */ + private $escaper; + + /** + * @inheridoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->orderFactory = $this->_objectManager->get(OrderFactory::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @return void + */ + public function testWithNoExistingOrder(): void + { + $this->dispatchWithOrderId(863521); + $expectedMessage = (string)__("The entity that was requested doesn't exist. Verify the entity and try again."); + $this->assertSessionMessages($this->containsEqual($this->escaper->escapeHtml($expectedMessage))); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php + * + * @return void + */ + public function testCanNotInvoice(): void + { + $expectedMessage = __('The order does not allow an invoice to be created.'); + $order = $this->orderFactory->create()->loadByIncrementId('100000001'); + $this->dispatchWithOrderId((int)$order->getEntityId()); + $this->assertSessionMessages($this->containsEqual((string)$expectedMessage), MessageInterface::TYPE_ERROR); + } + + /** + * Dispatch request with order_id param + * + * @param int $orderId + * @return void + */ + private function dispatchWithOrderId(int $orderId): void + { + $this->getRequest()->setMethod(Http::METHOD_GET) + ->setParams(['order_id' => $orderId]); + $this->dispatch('backend/sales/order_invoice/new'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php index 2dc5f5adc86d2..755d20196aa95 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -7,38 +7,53 @@ namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; +use Magento\Framework\Escaper; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Model\Order; use PHPUnit\Framework\Constraint\StringContains; /** - * Class tests invoice creation in backend. + * Class tests invoice creation in admin panel. + * + * @see \Magento\Sales\Controller\Adminhtml\Order\Invoice\Save * * @magentoDbIsolation enabled * @magentoAppArea adminhtml - * @magentoDataFixture Magento/Sales/_files/order.php */ class SaveTest extends AbstractInvoiceControllerTest { + /** @var string */ + protected $uri = 'backend/sales/order_invoice/save'; + + /** @var Escaper */ + private $escaper; + /** - * @var string + * @inheritdoc */ - protected $uri = 'backend/sales/order_invoice/save'; + protected function setUp(): void + { + parent::setUp(); + + $this->escaper = $this->_objectManager->get(Escaper::class); + } /** + * @magentoDataFixture Magento/Sales/_files/order.php + * * @return void */ public function testSendEmailOnInvoiceSave(): void { - $order = $this->prepareRequest(); + $order = $this->getOrder('100000001'); + $itemId = $order->getItemsCollection()->getFirstItem()->getId(); + $post = $this->hydratePost([$itemId => 2]); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); $this->dispatch('backend/sales/order_invoice/save'); - - $this->assertSessionMessages( - $this->equalTo([(string)__('The invoice has been created.')]), - \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS - ); - $this->assertRedirect($this->stringContains('sales/order/view/order_id/' . $order->getEntityId())); - $invoice = $this->getInvoiceByOrder($order); + $this->checkSuccess($invoice, 2); $message = $this->transportBuilder->getSentMessage(); + $this->assertNotNull($message); $subject = __('Invoice for your %1 order', $order->getStore()->getFrontendName())->render(); $messageConstraint = $this->logicalAnd( new StringContains($invoice->getBillingAddress()->getName()), @@ -49,9 +64,113 @@ public function testSendEmailOnInvoiceSave(): void "Your Invoice #{$invoice->getIncrementId()} for Order #{$order->getIncrementId()}" ) ); - $this->assertEquals($message->getSubject(), $subject); - $this->assertThat($message->getBody()->getParts()[0]->getRawContent(), $messageConstraint); + $bodyParts = $message->getBody()->getParts(); + $this->assertThat(reset($bodyParts)->getRawContent(), $messageConstraint); + } + + /** + * @magentoConfigFixture current_store sales_email/invoice/enabled 0 + * + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testSendEmailOnInvoiceSaveWithDisabledConfig(): void + { + $order = $this->getOrder('100000001'); + $post = $this->hydratePost([$order->getItemsCollection()->getFirstItem()->getId() => 2]); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/save'); + $this->checkSuccess($this->getInvoiceByOrder($order), 2); + $this->assertNull($this->transportBuilder->getSentMessage()); + } + + /** + * @dataProvider invoiceDataProvider + * + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @param int $invoicedItemsQty + * @param string $commentMessage + * @param bool $doShipment + * @return void + */ + public function testSuccessfulInvoice( + int $invoicedItemsQty, + string $commentMessage = '', + bool $doShipment = false + ): void { + $order = $this->getOrder('100000001'); + $post = $this->hydratePost( + [$order->getItemsCollection()->getFirstItem()->getId() => $invoicedItemsQty], + $commentMessage, + $doShipment + ); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/save'); + $this->checkSuccess($this->getInvoiceByOrder($order), $invoicedItemsQty, $commentMessage, $doShipment); + } + + /** + * @return array + */ + public function invoiceDataProvider(): array + { + return [ + 'with_comment_message' => [ + 'invoiced_items_qty' => 2, + 'comment_message' => 'test comment message', + ], + 'partial_invoice' => [ + 'invoiced_items_qty' => 1, + ], + 'with_do_shipment' => [ + 'invoiced_items_qty' => 2, + 'comment_message' => '', + 'do_shipment' => true, + ], + ]; + } + + /** + * @return void + */ + public function testWitNoExistingOrder(): void + { + $expectedMessage = (string)__('The order no longer exists.'); + $this->prepareRequest(['order_id' => 899989]); + $this->dispatch('backend/sales/order_invoice/save'); + $this->assertErrorResponse($expectedMessage); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php + * + * @return void + */ + public function testCanNotInvoiceOrder(): void + { + $expectedMessage = (string)__('The order does not allow an invoice to be created.'); + $order = $this->getOrder('100000001'); + $this->prepareRequest([], ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/save'); + $this->assertErrorResponse($expectedMessage); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testInvoiceWithoutQty(): void + { + $expectedMessage = (string)__('The invoice can\'t be created without products. Add products and try again.'); + $order = $this->getOrder('100000001'); + $post = $this->hydratePost([$order->getItemsCollection()->getFirstItem()->getId() => '0']); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/save'); + $this->assertErrorResponse($this->escaper->escapeHtml($expectedMessage)); } /** @@ -75,23 +194,46 @@ public function testAclNoAccess() } /** - * @param array $params - * @return \Magento\Sales\Api\Data\OrderInterface|null + * Check error response + * + * @param string $expectedMessage + * @return void */ - private function prepareRequest(array $params = []) + private function assertErrorResponse(string $expectedMessage): void { - $order = $this->getOrder('100000001'); - $this->getRequest()->setMethod('POST'); - $this->getRequest()->setParams( - [ - 'order_id' => $order->getEntityId(), - 'form_key' => $this->formKey->getFormKey(), - ] - ); + $this->assertRedirect($this->stringContains('sales/order_invoice/new')); + $this->assertSessionMessages($this->containsEqual($expectedMessage)); + } + + /** + * Check that invoice was successfully created + * + * @param InvoiceInterface $invoice + * @param int $invoicedItemsQty + * @param string|null $commentMessage + * @param bool $doShipment + * @return void + */ + private function checkSuccess( + InvoiceInterface $invoice, + int $invoicedItemsQty, + ?string $commentMessage = null, + bool $doShipment = false + ): void { + $message = $doShipment ? 'You created the invoice and shipment.' : 'The invoice has been created.'; + $expectedState = $doShipment ? Order::STATE_COMPLETE : Order::STATE_PROCESSING; + $this->assertNotNull($invoice->getEntityId()); + $this->assertEquals($invoicedItemsQty, (int)$invoice->getTotalQty()); + $order = $invoice->getOrder(); + $this->assertEquals($expectedState, $order->getState()); - $data = $params ?? []; - $this->getRequest()->setPostValue($data); + if ($commentMessage) { + $this->assertEquals($commentMessage, $invoice->getCustomerNote()); + } - return $order; + $this->assertRedirect( + $this->stringContains(sprintf('sales/order/view/order_id/%u', (int)$order->getEntityId())) + ); + $this->assertSessionMessages($this->containsEqual((string)__($message))); } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php new file mode 100644 index 0000000000000..5eb554ef937d5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/StartTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use Magento\Backend\Model\Session; +use Magento\Framework\App\Request\Http; +use Magento\Sales\Model\OrderFactory; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Test for invoice start action + * + * @see \Magento\Sales\Controller\Adminhtml\Order\Invoice\Start + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class StartTest extends AbstractBackendController +{ + /** @var OrderFactory */ + private $orderFactory; + + /** @var Session */ + private $session; + + /** + * @inheridoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->orderFactory = $this->_objectManager->get(OrderFactory::class); + $this->session = $this->_objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->session->getInvoiceItemQtys(true); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testExecute(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('100000001'); + $this->session->setInvoiceItemQtys('test'); + $this->getRequest()->setMethod(Http::METHOD_GET)->setParams(['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/start'); + $this->assertRedirect($this->stringContains('sales/order_invoice/new')); + $this->assertNull($this->session->getInvoiceItemQtys()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php new file mode 100644 index 0000000000000..2b91c5d04fd6f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/UpdateQtyTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Invoice; + +use Magento\Framework\Serialize\SerializerInterface; +use Magento\TestFramework\Helper\Xpath; + +/** + * Class tests invoice items qty update. + * + * @magentoDbIsolation enabled + * @magentoAppArea adminhtml + */ +class UpdateQtyTest extends AbstractInvoiceControllerTest +{ + /** @var SerializerInterface */ + private $json; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->json = $this->_objectManager->get(SerializerInterface::class); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testSuccess(): void + { + $order = $this->getOrder('100000001'); + $itemId = $order->getItemsCollection()->getFirstItem()->getId(); + $qtyToInvoice = 1; + $invoicedItemsXpath = sprintf( + "//input[contains(@class, 'qty-input') and @name='invoice[items][%u]' and @value='%u']", + $itemId, + $qtyToInvoice + ); + $post = $this->hydratePost([$itemId => $qtyToInvoice]); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/updateQty'); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($invoicedItemsXpath, $this->getResponse()->getContent()) + ); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_with_bundle_and_invoiced.php + * + * @return void + */ + public function testCanNotInvoice(): void + { + $order = $this->getOrder('100000001'); + $itemId = $order->getItemsCollection()->getFirstItem()->getId(); + $post = $this->hydratePost([$itemId => '1']); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/updateQty'); + $this->assertErrorResponse('The order does not allow an invoice to be created.'); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testWithoutQty(): void + { + $order = $this->getOrder('100000001'); + $itemId = $order->getItemsCollection()->getFirstItem()->getId(); + $post = $this->hydratePost([$itemId => '0']); + $this->prepareRequest($post, ['order_id' => $order->getEntityId()]); + $this->dispatch('backend/sales/order_invoice/updateQty'); + $this->assertErrorResponse( + 'The invoice can\'t be created without products. Add products and try again.' + ); + } + + /** + * @return void + */ + public function testWithNoExistingOrderId(): void + { + $post = $this->hydratePost([ + 'invoice' => [ + 'items' => [ + '1' => '3', + ], + ], + ]); + $this->prepareRequest($post, ['order_id' => 6543265]); + $this->dispatch('backend/sales/order_invoice/updateQty'); + $this->assertErrorResponse('The order no longer exists.'); + } + + /** + * Check error response + * + * @param string $expectedMessage + * @return void + */ + private function assertErrorResponse(string $expectedMessage): void + { + $expectedResponse = [ + 'error' => true, + 'message' => (string)__($expectedMessage), + ]; + $response = $this->getResponse()->getContent(); + $this->assertNotEmpty($response); + $this->assertEquals($expectedResponse, $this->json->unserialize($response)); + } +} From 33ffe08a4d5efd67b15c6c9e832c8df11692a9af Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Fri, 28 Aug 2020 14:48:34 +0300 Subject: [PATCH 074/195] MC-36914: Storefront: Customer Product reviews tab on customer profile --- .../Magento/Review/Block/Account/LinkTest.php | 80 ++++++++++ .../Block/Customer/ListCustomerTest.php | 135 +++++++++++++++++ .../Review/Block/Customer/ViewTest.php | 138 ++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php new file mode 100644 index 0000000000000..df5f5f8336303 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Block/Account/LinkTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Block\Account; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Checks "My Product Reviews" link displaying in customer account dashboard + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ +class LinkTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Page */ + private $page; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->page = $this->objectManager->get(PageFactory::class)->create(); + } + + /** + * @return void + */ + public function testMyProductReviewsLink(): void + { + $this->preparePage(); + $block = $this->page->getLayout()->getBlock('customer-account-navigation-product-reviews-link'); + $this->assertNotFalse($block); + $html = $block->toHtml(); + $this->assertStringContainsString('/review/customer/', $html); + $this->assertEquals((string)__('My Product Reviews'), strip_tags($html)); + } + + /** + * @magentoConfigFixture current_store catalog/review/active 0 + * + * @return void + */ + public function testMyProductReviewsLinkDisabled(): void + { + $this->preparePage(); + $block = $this->page->getLayout()->getBlock('customer-account-navigation-product-reviews-link'); + $this->assertFalse($block); + } + + /** + * Prepare page before render + * + * @return void + */ + private function preparePage(): void + { + $this->page->addHandle([ + 'default', + 'customer_account', + ]); + $this->page->getLayout()->generateXml(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php new file mode 100644 index 0000000000000..24cb2fe76a6d4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ListCustomerTest.php @@ -0,0 +1,135 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Block\Customer; + +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Review\Model\ResourceModel\Review\Product\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Test for customer product reviews grid. + * + * @see \Magento\Review\Block\Customer\ListCustomer + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class ListCustomerTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $customerSession; + + /** @var ListCustomer */ + private $block; + + /** @var CollectionFactory */ + private $collectionFactory; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(Session::class); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(ListCustomer::class) + ->setTemplate('Magento_Review::customer/list.phtml'); + $this->collectionFactory = $this->objectManager->get(CollectionFactory::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Review/_files/customer_review_with_rating.php + * + * @return void + */ + public function testCustomerProductReviewsGrid(): void + { + $this->customerSession->setCustomerId(1); + $review = $this->collectionFactory->create()->addCustomerFilter(1)->addReviewSummary()->getFirstItem(); + $this->assertNotNull($review->getReviewId()); + $blockHtml = $this->block->toHtml(); + $createdDate = $this->block->dateFormat($review->getReviewCreatedAt()); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//td[contains(@class, 'date') and contains(text(), '%s')]", $createdDate), + $blockHtml + ), + sprintf('Created date wasn\'t found or not equals to %s.', $createdDate) + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//td[contains(@class, 'item')]//a[contains(text(), '%s')]", $review->getName()), + $blockHtml + ), + 'Product name wasn\'t found.' + ); + $rating = $review->getSum() / $review->getCount(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//td[contains(@class, 'summary')]//span[contains(text(), '%s%%')]", $rating), + $blockHtml + ), + sprintf('Rating wasn\'t found or not equals to %s%%.', $rating) + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//td[contains(@class, 'description') and contains(text(), '%s')]", $review->getDetail()), + $blockHtml + ), + 'Review description wasn\'t found.' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//td[contains(@class, 'actions')]//a[contains(@href, '%s')]/span[contains(text(), '%s')]", + $this->block->getReviewUrl($review), + __('See Details') + ), + $blockHtml + ), + sprintf('%s button wasn\'t found.', __('See Details')) + ); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testCustomerWithoutReviews(): void + { + $this->customerSession->setCustomerId(1); + $this->assertStringContainsString( + (string)__('You have submitted no reviews.'), + strip_tags($this->block->toHtml()) + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php new file mode 100644 index 0000000000000..31a342ad8ac54 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Block/Customer/ViewTest.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Block\Customer; + +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Review\Model\ResourceModel\Review\Product\CollectionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Test for displaying customer product review block. + * + * @see \Magento\Review\Block\Customer\View + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class ViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $customerSession; + + /** @var CollectionFactory */ + private $collectionFactory; + + /** @var View */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(Session::class); + $this->collectionFactory = $this->objectManager->get(CollectionFactory::class); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(View::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Review/_files/customer_review_with_rating.php + * + * @return void + */ + public function testCustomerProductReviewBlock(): void + { + $this->customerSession->setCustomerId(1); + $review = $this->collectionFactory->create()->addCustomerFilter(1)->getFirstItem(); + $this->assertNotNull($review->getReviewId()); + $blockHtml = $this->block->setReviewId($review->getReviewId())->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//div[contains(@class, 'product-info')]/h2[contains(text(), '%s')]", $review->getName()), + $blockHtml + ), + 'Product name wasn\'t found.' + ); + $ratings = $this->block->getRating(); + $this->assertCount(2, $ratings); + foreach ($ratings as $rating) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//div[contains(@class, 'rating-summary')]//span[contains(text(), '%s')]" + . "/../..//span[contains(text(), '%s%%')]", + $rating->getRatingCode(), + $rating->getPercent() + ), + $blockHtml + ), + sprintf('Rating %s was not found or not equals to %s.', $rating->getRatingCode(), $rating->getPercent()) + ); + } + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//div[contains(@class, 'review-title') and contains(text(), '%s')]", $review->getTitle()), + $blockHtml + ), + 'Review title wasn\'t found.' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf("//div[contains(@class, 'review-content') and contains(text(), '%s')]", $review->getDetail()), + $blockHtml + ), + 'Review description wasn\'t found.' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//div[contains(@class, 'review-date') and contains(text(), '%s')]/time[contains(text(), '%s')]", + __('Submitted on'), + $this->block->dateFormat($review->getCreatedAt()) + ), + $blockHtml + ), + 'Created date wasn\'t found.' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + "//a[contains(@href, '/review/customer/')]/span[contains(text(), '%s')]", + __('Back to My Reviews') + ), + $blockHtml + ), + sprintf('%s button wasn\'t found.', __('Back to My Reviews')) + ); + } +} From dd9bdda933e36b519a7f0a8d52579bfb48bf2a33 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 28 Aug 2020 16:09:12 +0300 Subject: [PATCH 075/195] Improve acl resources for new media gallery --- .../Adminhtml/Wysiwyg/Images/DeleteFiles.php | 5 -- .../Adminhtml/Wysiwyg/Images/DeleteFolder.php | 6 --- .../Adminhtml/Wysiwyg/Images/NewFolder.php | 5 -- .../Adminhtml/Wysiwyg/Images/OnInsert.php | 4 -- .../Adminhtml/Wysiwyg/Images/Upload.php | 5 -- app/code/Magento/MediaGallery/etc/acl.xml | 26 ++++++++++ .../Adminhtml/Directories/Create.php | 2 +- .../Adminhtml/Directories/Delete.php | 2 +- .../Controller/Adminhtml/Image/Delete.php | 2 +- .../Controller/Adminhtml/Image/Upload.php | 2 +- .../Ui/Component/Control/CreateFolder.php | 50 ++++++++++++++++++ .../Ui/Component/Control/DeleteAssets.php | 50 ++++++++++++++++++ .../Ui/Component/Control/DeleteFolder.php | 51 +++++++++++++++++++ .../Ui/Component/Control/UploadAssets.php | 51 +++++++++++++++++++ app/code/Magento/MediaGalleryUi/etc/acl.xml | 26 ---------- .../ui_component/media_gallery_listing.xml | 29 ++--------- .../standalone_media_gallery_listing.xml | 28 ++-------- 17 files changed, 240 insertions(+), 104 deletions(-) create mode 100644 app/code/Magento/MediaGallery/etc/acl.xml create mode 100644 app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php create mode 100644 app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php create mode 100644 app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php create mode 100644 app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php delete mode 100644 app/code/Magento/MediaGalleryUi/etc/acl.xml diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php index 848a10950e0ba..fa873930aaade 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFiles.php @@ -13,11 +13,6 @@ */ class DeleteFiles extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { - /** - * @see _isAllowed() - */ - public const ADMIN_RESOURCE = 'Magento_Cms::delete_assets'; - /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php index 8a67d9220e11c..1f991bb47c6fd 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/DeleteFolder.php @@ -10,18 +10,12 @@ namespace Magento\Cms\Controller\Adminhtml\Wysiwyg\Images; use Magento\Framework\App\Action\HttpPostActionInterface; -use Magento\Framework\App\Filesystem\DirectoryList; /** * Delete image folder. */ class DeleteFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { - /** - * @see _isAllowed() - */ - public const ADMIN_RESOURCE = 'Magento_Cms::delete_folder'; - /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php index de462f82c8911..706718455a523 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/NewFolder.php @@ -14,11 +14,6 @@ */ class NewFolder extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { - /** - * @see _isAllowed() - */ - public const ADMIN_RESOURCE = 'Magento_Cms::create_folder'; - /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php index 516742ffd2ee8..c86f6970d2495 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php @@ -8,10 +8,6 @@ class OnInsert extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images { - /** - * @see _isAllowed() - */ - public const ADMIN_RESOURCE = 'Magento_Cms::insert_assets'; /** * @var \Magento\Framework\Controller\Result\RawFactory diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php index 6e48dfe7c5f10..260755ea7d562 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/Upload.php @@ -17,11 +17,6 @@ */ class Upload extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images implements HttpPostActionInterface { - /** - * @see _isAllowed() - */ - public const ADMIN_RESOURCE = 'Magento_Cms::upload_assets'; - /** * @var \Magento\Framework\Controller\Result\JsonFactory */ diff --git a/app/code/Magento/MediaGallery/etc/acl.xml b/app/code/Magento/MediaGallery/etc/acl.xml new file mode 100644 index 0000000000000..a90d43e2a6cd2 --- /dev/null +++ b/app/code/Magento/MediaGallery/etc/acl.xml @@ -0,0 +1,26 @@ +<?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:Acl/etc/acl.xsd"> + <acl> + <resources> + <resource id="Magento_Backend::admin"> + <resource id="Magento_Backend::content"> + <resource id="Magento_Backend::content_elements"> + <resource id="Magento_Cms::media_gallery" title="Media Gallery" translate="title"> + <resource id="Magento_MediaGallery::upload_assets" title="Upload Assets" translate="title" sortOrder="80"/> + <resource id="Magento_MediaGallery::delete_assets" title="Delete Assets" translate="title" sortOrder="70"/> + <resource id="Magento_MediaGallery::insert_assets" title="Insert Assets into the content" translate="title" sortOrder="60"/> + <resource id="Magento_MediaGallery::create_folder" title="Create Folder" translate="title" sortOrder="50"/> + <resource id="Magento_MediaGallery::delete_folder" title="Delete Folder" translate="title" sortOrder="40"/> + </resource> + </resource> + </resource> + </resource> + </resources> + </acl> +</config> diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php index 147fc48910113..f303cd4b7d5e5 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php @@ -29,7 +29,7 @@ class Create extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::create_folder'; + public const ADMIN_RESOURCE = 'Magento_MediaGallery::create_folder'; /** * @var CreateDirectoriesByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php index 1ca2512826504..f11fa36c0e499 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php @@ -30,7 +30,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::delete_folder'; + public const ADMIN_RESOURCE = 'Magento_MediaGallery::delete_folder'; /** * @var DeleteAssetsByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php index d630d93688d28..30fc9a772cafd 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php @@ -31,7 +31,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::delete_assets'; + public const ADMIN_RESOURCE = 'Magento_MediaGallery::delete_assets'; /** * @var DeleteImage diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php index 0d3f9a78ae3ca..11df39bd2b1dc 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php @@ -28,7 +28,7 @@ class Upload extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::upload_assets'; + public const ADMIN_RESOURCE = 'Magento_MediaGallery::upload_assets'; /** * @var UploadImage diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php new file mode 100644 index 0000000000000..e4cc00e0bb107 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MediaGalleryUi\Ui\Component\Control; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Framework\AuthorizationInterface; + +/** + * Create Folder button + */ +class CreateFolder implements ButtonProviderInterface +{ + private const ACL_CREATE_FOLDER = 'Magento_MediaGallery::create_folder'; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * Constructor. + * + * @param AuthorizationInterface $authorization + */ + public function __construct( + AuthorizationInterface $authorization + ) { + $this->authorization = $authorization; + } + + /** + * @inheritdoc + */ + public function getButtonData() + { + if (!$this->authorization->isAllowed(self::ACL_CREATE_FOLDER)) { + return []; + } + + return [ + 'label' => __('Create Folder'), + 'on_click' => 'jQuery("#create_folder").trigger("create_folder");', + 'class' => 'action-default scalable add media-gallery-actions-buttons', + 'sort_order' => 10, + ]; + } +} diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php new file mode 100644 index 0000000000000..e31f35e9e28fb --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MediaGalleryUi\Ui\Component\Control; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Framework\AuthorizationInterface; + +/** + * Delete images button + */ +class DeleteAssets implements ButtonProviderInterface +{ + private const ACL_DELETE_ASSETS= 'Magento_MediaGallery::delete_assets'; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * Constructor. + * + * @param AuthorizationInterface $authorization + */ + public function __construct( + AuthorizationInterface $authorization + ) { + $this->authorization = $authorization; + } + + /** + * @return array + */ + public function getButtonData() + { + if (!$this->authorization->isAllowed(self::ACL_DELETE_ASSETS)) { + return []; + } + + return [ + 'label' => __('Delete Images...'), + 'on_click' => 'jQuery(window).trigger("massAction.MediaGallery")', + 'class' => 'action-default scalable add media-gallery-actions-buttons', + 'sort_order' => 50, + ]; + } +} diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php new file mode 100644 index 0000000000000..97c336de21cd6 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MediaGalleryUi\Ui\Component\Control; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Framework\AuthorizationInterface; + +/** + * Delete Folder button + */ +class DeleteFolder implements ButtonProviderInterface +{ + private const ACL_DELETE_FOLDER = 'Magento_MediaGallery::delete_folder'; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * Constructor. + * + * @param AuthorizationInterface $authorization + */ + public function __construct( + AuthorizationInterface $authorization + ) { + $this->authorization = $authorization; + } + + /** + * @return array + */ + public function getButtonData() + { + if (!$this->authorization->isAllowed(self::ACL_DELETE_FOLDER)) { + return []; + } + + return [ + 'label' => __('Delete Folder'), + 'disabled' => 'disabled', + 'on_click' => 'jQuery("#delete_folder").trigger("delete_folder");', + 'class' => 'action-default scalable add media-gallery-actions-buttons', + 'sort_order' => 30, + ]; + } +} diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php new file mode 100644 index 0000000000000..5be3263f63316 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MediaGalleryUi\Ui\Component\Control; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Framework\AuthorizationInterface; + +/** + * Upload Image button + */ +class UploadAssets implements ButtonProviderInterface +{ + private const ACL_UPLOAD_ASSETS= 'Magento_MediaGallery::upload_assets'; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * Constructor. + * + * @param AuthorizationInterface $authorization + */ + public function __construct( + AuthorizationInterface $authorization + ) { + $this->authorization = $authorization; + } + + /** + * @return array + */ + public function getButtonData() + { + if (!$this->authorization->isAllowed(self::ACL_UPLOAD_ASSETS)) { + return []; + } + + return [ + 'label' => __('Upload Image'), + 'disabled' => 'disabled', + 'on_click' => 'jQuery("#image-uploader-input").click();', + 'class' => 'action-default scalable add media-gallery-actions-buttons', + 'sort_order' => 20, + ]; + } +} diff --git a/app/code/Magento/MediaGalleryUi/etc/acl.xml b/app/code/Magento/MediaGalleryUi/etc/acl.xml deleted file mode 100644 index 7c8784e412814..0000000000000 --- a/app/code/Magento/MediaGalleryUi/etc/acl.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?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:Acl/etc/acl.xsd"> - <acl> - <resources> - <resource id="Magento_Backend::admin"> - <resource id="Magento_Backend::content"> - <resource id="Magento_Backend::content_elements"> - <resource id="Magento_Cms::media_gallery"> - <resource id="Magento_Cms::upload_assets" title="Upload Assets" translate="title" sortOrder="70"/> - <resource id="Magento_Cms::delete_assets" title="Delete Assets" translate="title" sortOrder="60"/> - <resource id="Magento_Cms::insert_assets" title="Insert Assets into the content" translate="title" sortOrder="50"/> - <resource id="Magento_Cms::create_folder" title="Create Folder" translate="title" sortOrder="40"/> - <resource id="Magento_Cms::delete_folder" title="Delete Folder" translate="title" sortOrder="40"/> - </resource> - </resource> - </resource> - </resource> - </resources> - </acl> -</config> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml index 49206043725f9..a886bcc1c3374 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -28,31 +28,10 @@ <class>cancel action-quaternary</class> <label translate="true">Cancel</label> </button> - <button name="upload_image"> - <param name="on_click" xsi:type="string">jQuery('#image-uploader-input').click();</param> - <class>action-add scalable media-gallery-actions-buttons</class> - <param name="sort_order" xsi:type="number">20</param> - <label translate="true">Upload Image</label> - </button> - <button name="delete_folder"> - <param name="on_click" xsi:type="string">jQuery('#delete_folder').trigger('delete_folder');</param> - <param name="disabled" xsi:type="string">disabled</param> - <param name="sort_order" xsi:type="number">30</param> - <class>action-default scalable media-gallery-actions-buttons</class> - <label translate="true">Delete Folder</label> - </button> - <button name="create_folder"> - <param name="on_click" xsi:type="string">jQuery('#create_folder').trigger('create_folder');</param> - <param name="sort_order" xsi:type="number">10</param> - <class>action-default scalable add media-gallery-actions-buttons</class> - <label translate="true">Create Folder</label> - </button> - <button name="delete_massaction"> - <param name="on_click" xsi:type="string">jQuery(window).trigger('massAction.MediaGallery')</param> - <param name="sort_order" xsi:type="number">50</param> - <class>action-default scalable add media-gallery-actions-buttons</class> - <label translate="true">Delete Images...</label> - </button> + <button name="upload_image" class="Magento\MediaGalleryUi\Ui\Component\Control\UploadAssets"/> + <button name="delete_folder" class="Magento\MediaGalleryUi\Ui\Component\Control\DeleteFolder"/> + <button name="create_folder" class="Magento\MediaGalleryUi\Ui\Component\Control\CreateFolder"/> + <button name="delete_massaction" class="Magento\MediaGalleryUi\Ui\Component\Control\DeleteAssets"/> </buttons> <spinner>media_gallery_columns</spinner> <deps> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml index 655178c104492..bbb62a53f0af3 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml @@ -20,30 +20,10 @@ <dep>standalone_media_gallery_listing.media_gallery_listing_data_source</dep> </deps> <buttons> - <button name="delete_folder"> - <param name="on_click" xsi:type="string">jQuery('#delete_folder').trigger('delete_folder');</param> - <param name="disabled" xsi:type="string">disabled</param> - <param name="sort_order" xsi:type="number">20</param> - <class>action-default scalable add media-gallery-actions-buttons</class> - <label translate="true">Delete Folder</label> - </button> - <button name="create_folder"> - <param name="on_click" xsi:type="string">jQuery('#create_folder').trigger('create_folder');</param> - <param name="sort_order" xsi:type="number">30</param> - <class>action-default scalable add media-gallery-actions-buttons</class> - <label translate="true">Create Folder</label> - </button> - <button name="delete_massaction"> - <param name="on_click" xsi:type="string">jQuery(window).trigger('massAction.MediaGallery')</param> - <param name="sort_order" xsi:type="number">50</param> - <class>action-default scalable add media-gallery-actions-buttons</class> - <label translate="true">Delete Images...</label> - </button> - <button name="upload_image"> - <param name="on_click" xsi:type="string">jQuery('#image-uploader-input').click();</param> - <class>action-default scalable add media-gallery-actions-buttons</class> - <label translate="true">Upload Image</label> - </button> + <button name="upload_image" class="Magento\MediaGalleryUi\Ui\Component\Control\UploadAssets"/> + <button name="delete_folder" class="Magento\MediaGalleryUi\Ui\Component\Control\DeleteFolder"/> + <button name="create_folder" class="Magento\MediaGalleryUi\Ui\Component\Control\CreateFolder"/> + <button name="delete_massaction" class="Magento\MediaGalleryUi\Ui\Component\Control\DeleteAssets"/> </buttons> </settings> <dataSource name="media_gallery_listing_data_source" component="Magento_Ui/js/grid/provider"> From ae3d3df4752dfcac5256ddb5014ce02b19e8a529 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Fri, 28 Aug 2020 16:28:54 +0300 Subject: [PATCH 076/195] fix widget with special char page tittle --- .../Widget/Block/Adminhtml/Widget/Options.php | 5 - app/code/Magento/Widget/Model/Widget.php | 197 ++++++++---------- 2 files changed, 85 insertions(+), 117 deletions(-) diff --git a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php index 44c43055df8b9..32bae10c801c8 100644 --- a/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php +++ b/app/code/Magento/Widget/Block/Adminhtml/Widget/Options.php @@ -136,7 +136,6 @@ public function addFields() * @return \Magento\Framework\Data\Form\Element\AbstractElement * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ protected function _addField($parameter) { @@ -159,10 +158,6 @@ protected function _addField($parameter) $data['value'] = $parameter->getValue(); } - if ($parameter->getType() == 'text' && $data['value'] != '') { - $data['value'] = $this->_widget->decodeReservedChars($data['value']); - } - //prepare unique id value if ($fieldName == 'unique_id' && $data['value'] == '') { $data['value'] = hash('sha256', microtime(1)); diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 3ca52cec43f40..6d9bb1878708a 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -5,6 +5,16 @@ */ namespace Magento\Widget\Model; +use Magento\Framework\App\Cache\Type\Config; +use Magento\Framework\DataObject; +use Magento\Framework\Escaper; +use Magento\Framework\Math\Random; +use Magento\Framework\View\Asset\Repository; +use Magento\Framework\View\Asset\Source; +use Magento\Framework\View\FileSystem; +use Magento\Widget\Helper\Conditions; +use Magento\Widget\Model\Config\Data; + /** * Widget model for different purposes * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -15,32 +25,32 @@ class Widget { /** - * @var \Magento\Widget\Model\Config\Data + * @var Data */ protected $dataStorage; /** - * @var \Magento\Framework\App\Cache\Type\Config + * @var Config */ protected $configCacheType; /** - * @var \Magento\Framework\View\Asset\Repository + * @var Repository */ protected $assetRepo; /** - * @var \Magento\Framework\View\Asset\Source + * @var Source */ protected $assetSource; /** - * @var \Magento\Framework\View\FileSystem + * @var FileSystem */ protected $viewFileSystem; /** - * @var \Magento\Framework\Escaper + * @var Escaper */ protected $escaper; @@ -50,30 +60,35 @@ class Widget protected $widgetsArray = []; /** - * @var \Magento\Widget\Helper\Conditions + * @var Conditions */ protected $conditionsHelper; /** - * @var \Magento\Framework\Math\Random + * @var Random */ private $mathRandom; /** - * @param \Magento\Framework\Escaper $escaper - * @param \Magento\Widget\Model\Config\Data $dataStorage - * @param \Magento\Framework\View\Asset\Repository $assetRepo - * @param \Magento\Framework\View\Asset\Source $assetSource - * @param \Magento\Framework\View\FileSystem $viewFileSystem - * @param \Magento\Widget\Helper\Conditions $conditionsHelper + * @var string[] + */ + private $reservedChars = ['}', '{']; + + /** + * @param Escaper $escaper + * @param Data $dataStorage + * @param Repository $assetRepo + * @param Source $assetSource + * @param FileSystem $viewFileSystem + * @param Conditions $conditionsHelper */ public function __construct( - \Magento\Framework\Escaper $escaper, - \Magento\Widget\Model\Config\Data $dataStorage, - \Magento\Framework\View\Asset\Repository $assetRepo, - \Magento\Framework\View\Asset\Source $assetSource, - \Magento\Framework\View\FileSystem $viewFileSystem, - \Magento\Widget\Helper\Conditions $conditionsHelper + Escaper $escaper, + Data $dataStorage, + Repository $assetRepo, + Source $assetSource, + FileSystem $viewFileSystem, + Conditions $conditionsHelper ) { $this->escaper = $escaper; $this->dataStorage = $dataStorage; @@ -110,14 +125,11 @@ public function getWidgetByClassType($type) $widgets = $this->getWidgets(); /** @var array $widget */ foreach ($widgets as $widget) { - if (isset($widget['@'])) { - if (isset($widget['@']['type'])) { - if ($type === $widget['@']['type']) { - return $widget; - } - } + if (isset($widget['@']['type']) && $type === $widget['@']['type']) { + return $widget; } } + return null; } @@ -131,8 +143,7 @@ public function getWidgetByClassType($type) */ public function getConfigAsXml($type) { - // phpstan:ignore - return $this->getXmlElementByType($type); + return $this->getWidgetByClassType($type); } /** @@ -294,48 +305,71 @@ public function getWidgetsArray($filters = []) * @param array $params Pre-configured Widget Params * @param bool $asIs Return result as widget directive(true) or as placeholder image(false) * @return string Widget directive ready to parse - * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function getWidgetDeclaration($type, $params = [], $asIs = true) { - $directive = '{{widget type="' . $type . '"'; $widget = $this->getConfigAsObject($type); + $directiveParams = ''; foreach ($params as $name => $value) { // Retrieve default option value if pre-configured - if ($name == 'conditions') { - $name = 'conditions_encoded'; - $value = $this->conditionsHelper->encode($value); - } elseif ($this->isTextType($widget, $name)) { - $value = $this->encodeReservedChars($value); - } elseif (is_array($value)) { - $value = implode(',', $value); - } elseif (trim($value) == '') { - $parameters = $widget->getParameters(); - if (isset($parameters[$name]) && is_object($parameters[$name])) { - $value = $parameters[$name]->getValue(); - } - } - if (isset($value)) { - $directive .= sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false)); - } + $directiveParams .= $this->getDirectiveParam($widget, $name, $value); } - $directive .= $this->getWidgetPageVarName($params); - - $directive .= '}}'; + $directive = sprintf('{{widget type="%s"%s%s}}', $type, $directiveParams, $this->getWidgetPageVarName($params)); if ($asIs) { return $directive; } - $html = sprintf( + return sprintf( '<img id="%s" src="%s" title="%s">', $this->idEncode($directive), $this->getPlaceholderImageUrl($type), $this->escaper->escapeUrl($directive) ); - return $html; + } + + /** + * Returns directive param with prepared value + * + * @param DataObject $widget + * @param string $name + * @param string|array $value + * @return string + */ + private function getDirectiveParam(DataObject $widget, string $name, $value): string + { + if ($name === 'conditions') { + $name = 'conditions_encoded'; + $value = $this->conditionsHelper->encode($value); + } elseif (is_array($value)) { + $value = implode(',', $value); + } elseif (trim($value) === '') { + $parameters = $widget->getParameters(); + if (isset($parameters[$name]) && is_object($parameters[$name])) { + $value = $parameters[$name]->getValue(); + } + } else { + $value = $this->getPreparedValue($value); + } + + return $value !== null + ? sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false)) + : ''; + } + + /** + * Returns encoded value if it contains reserved chars + * + * @param string $value + * @return string + */ + private function getPreparedValue(string $value): string + { + $pattern = sprintf('/%s/', implode('|', $this->reservedChars)); + + return preg_match($pattern, $value) ? rawurlencode($value) : $value; } /** @@ -460,65 +494,4 @@ protected function sortParameters($firstElement, $secondElement) $bOrder = (int)$secondElement->getData('sort_order'); return $aOrder < $bOrder ? -1 : ($aOrder > $bOrder ? 1 : 0); } - - /** - * Encode reserved chars - * - * @param string $string - * @return string|string[] - */ - private function encodeReservedChars($string) - { - $map = [ - '{' => urlencode('{'), - '}' => urlencode('}') - ]; - - return str_replace( - array_keys($map), - array_values($map), - $string - ); - } - - /** - * Decode reserved chars - * - * @param string $string - * @return array - */ - public function decodeReservedChars($string) - { - $map = [ - '{' => urlencode('{'), - '}' => urlencode('}') - ]; - - return str_replace( - array_values($map), - array_keys($map), - $string - ); - } - - /** - * Is text type Widget parameter - * - * @param \Magento\Framework\DataObject $widget - * @param string $name - * @return bool - */ - private function isTextType($widget, $name) - { - $parameters = $widget->getParameters(); - - if (isset($parameters[$name]) && is_object($parameters[$name])) { - $type = $parameters[$name]->getType(); - if ($type == 'text') { - return true; - } - } - - return false; - } } From 989139f0fc77bbb1c1eac28985955145b83ae3fb Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 28 Aug 2020 17:04:00 +0300 Subject: [PATCH 077/195] Improve button visibility --- .../Ui/Component/Control/CreateFolder.php | 12 +++-- .../Ui/Component/Control/DeleteAssets.php | 14 ++--- .../Ui/Component/Control/DeleteFolder.php | 11 ++-- .../Ui/Component/Control/InsertAsstes.php | 52 +++++++++++++++++++ .../Ui/Component/Control/UploadAssets.php | 12 +++-- .../Ui/Component/DirectoriesTree.php | 1 + .../ui_component/media_gallery_listing.xml | 7 +-- .../adminhtml/web/js/directory/directories.js | 4 ++ .../web/js/grid/massaction/massactions.js | 5 ++ 9 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php index e4cc00e0bb107..a0f7ebb623991 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php @@ -36,15 +36,17 @@ public function __construct( */ public function getButtonData() { - if (!$this->authorization->isAllowed(self::ACL_CREATE_FOLDER)) { - return []; - } - - return [ + $buttonData = [ 'label' => __('Create Folder'), 'on_click' => 'jQuery("#create_folder").trigger("create_folder");', 'class' => 'action-default scalable add media-gallery-actions-buttons', 'sort_order' => 10, ]; + + if (!$this->authorization->isAllowed(self::ACL_CREATE_FOLDER)) { + $buttonData['disabled'] = 'disabled'; + } + + return $buttonData; } } diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php index e31f35e9e28fb..8e3b033d4d927 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php @@ -34,17 +34,19 @@ public function __construct( /** * @return array */ - public function getButtonData() + public function getButtonData(): array { - if (!$this->authorization->isAllowed(self::ACL_DELETE_ASSETS)) { - return []; - } - - return [ + $buttonData = [ 'label' => __('Delete Images...'), 'on_click' => 'jQuery(window).trigger("massAction.MediaGallery")', 'class' => 'action-default scalable add media-gallery-actions-buttons', 'sort_order' => 50, ]; + + if (!$this->authorization->isAllowed(self::ACL_DELETE_ASSETS)) { + $buttonData['disabled'] = 'disabled'; + } + + return $buttonData; } } diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php index 97c336de21cd6..65986f9ed4b98 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php @@ -36,16 +36,17 @@ public function __construct( */ public function getButtonData() { - if (!$this->authorization->isAllowed(self::ACL_DELETE_FOLDER)) { - return []; - } - - return [ + $buttonData = [ 'label' => __('Delete Folder'), 'disabled' => 'disabled', 'on_click' => 'jQuery("#delete_folder").trigger("delete_folder");', 'class' => 'action-default scalable add media-gallery-actions-buttons', 'sort_order' => 30, ]; + if (!$this->authorization->isAllowed(self::ACL_DELETE_FOLDER)) { + $buttonData['disabled'] = 'disabled'; + } + + return $buttonData; } } diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php new file mode 100644 index 0000000000000..25846851ed2a6 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MediaGalleryUi\Ui\Component\Control; + +use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; +use Magento\Framework\AuthorizationInterface; + +/** + * Add selected button + */ +class InsertAsstes implements ButtonProviderInterface +{ + private const ACL_INSERT_ASSETS = 'Magento_MediaGallery::insert_assets'; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * Constructor. + * + * @param AuthorizationInterface $authorization + */ + public function __construct( + AuthorizationInterface $authorization + ) { + $this->authorization = $authorization; + } + + /** + * @inheritdoc + */ + public function getButtonData() + { + $buttonData = [ + 'label' => __('Add Selected'), + 'on_click' => 'return false;");', + 'class' => 'action-primary no-display media-gallery-add-selected', + 'sort_order' => 110, + ]; + + if (!$this->authorization->isAllowed(self::ACL_INSERT_ASSETS)) { + $buttonData['disabled'] = 'disabled'; + } + + return $buttonData; + } +} diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php index 5be3263f63316..08cfe2386ad92 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php @@ -36,16 +36,18 @@ public function __construct( */ public function getButtonData() { - if (!$this->authorization->isAllowed(self::ACL_UPLOAD_ASSETS)) { - return []; - } - - return [ + $buttonData = [ 'label' => __('Upload Image'), 'disabled' => 'disabled', 'on_click' => 'jQuery("#image-uploader-input").click();', 'class' => 'action-default scalable add media-gallery-actions-buttons', 'sort_order' => 20, ]; + + if (!$this->authorization->isAllowed(self::ACL_UPLOAD_ASSETS)) { + $buttonData['disabled'] = 'disabled'; + } + + return $buttonData; } } diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoriesTree.php b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoriesTree.php index 4047a4fcb98d8..5798a788a03f4 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoriesTree.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoriesTree.php @@ -50,6 +50,7 @@ public function prepare(): void array_replace_recursive( (array) $this->getData('config'), [ + 'allowedActions' => [], 'getDirectoryTreeUrl' => $this->url->getUrl("media_gallery/directories/gettree"), 'deleteDirectoryUrl' => $this->url->getUrl("media_gallery/directories/delete"), 'createDirectoryUrl' => $this->url->getUrl("media_gallery/directories/create") diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml index a886bcc1c3374..58e5435faaf4d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -16,12 +16,7 @@ </argument> <settings> <buttons> - <button name="add_selected"> - <param name="on_click" xsi:type="string">return false;</param> - <param name="sort_order" xsi:type="number">110</param> - <class>action-primary no-display media-gallery-add-selected</class> - <label translate="true">Add Selected</label> - </button> + <button name="add_selected" class="Magento\MediaGalleryUi\Ui\Component\Control\InsertAsstes"/> <button name="cancel"> <param name="on_click" xsi:type="string">MediabrowserUtility.closeDialog();</param> <param name="sort_order" xsi:type="number">1</param> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js index d7f756d8bbd90..5e82f05a3c261 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js @@ -179,6 +179,10 @@ define([ * @param {String} folderId */ setActive: function (folderId) { + if (!$.inArray('delete_folder', this.allowedActions)) { + return; + } + this.selectedFolder(folderId); $(this.deleteButtonSelector).removeAttr('disabled').removeClass('disabled'); } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js index 4f09854005f23..cd8a598b9ef67 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js @@ -16,6 +16,7 @@ define([ return Component.extend({ defaults: { + allowedActions: [], deleteButtonSelector: '#delete_selected_massaction', deleteImagesSelector: '#delete_massaction', mediaGalleryImageDetailsName: 'mediaGalleryImageDetails', @@ -106,6 +107,10 @@ define([ * If images records less than one, disable "delete images" button */ checkButtonVisibility: function () { + if (!$.inArray('delete_assets', this.allowedActions)) { + return; + } + if (this.imageItems.length < 1) { $(this.deleteImagesSelector).addClass('disabled'); } else { From 239314d98653081f02752ee5742136b1f3ba07e2 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 28 Aug 2020 19:16:17 +0300 Subject: [PATCH 078/195] Improve button visibility per allowed actions for user --- .../Ui/Component/Control/CreateFolder.php | 2 +- .../Ui/Component/Control/DeleteAssets.php | 2 +- .../Ui/Component/Control/DeleteFolder.php | 4 +-- .../Ui/Component/Control/InsertAsstes.php | 4 +-- .../Ui/Component/Control/UploadAssets.php | 4 +-- .../Ui/Component/Listing/Columns/Url.php | 30 +++++++++++++++++++ .../adminhtml/templates/image_details.phtml | 2 -- .../templates/image_details_standalone.phtml | 2 -- .../adminhtml/web/css/source/_module.less | 3 ++ .../adminhtml/web/js/directory/directories.js | 3 +- .../adminhtml/web/js/grid/columns/image.js | 13 ++++++-- .../web/js/grid/columns/image/actions.js | 15 +++++++++- .../web/js/grid/massaction/massactions.js | 2 +- .../template/grid/columns/image/actions.html | 6 ++-- 14 files changed, 72 insertions(+), 20 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php index a0f7ebb623991..b05bf116e61c6 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php @@ -34,7 +34,7 @@ public function __construct( /** * @inheritdoc */ - public function getButtonData() + public function getButtonData(): array { $buttonData = [ 'label' => __('Create Folder'), diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php index 8e3b033d4d927..fd5564c44ff89 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php @@ -32,7 +32,7 @@ public function __construct( } /** - * @return array + * @inheritdoc */ public function getButtonData(): array { diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php index 65986f9ed4b98..2fa14f19b197d 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php @@ -32,9 +32,9 @@ public function __construct( } /** - * @return array + * @inheritdoc */ - public function getButtonData() + public function getButtonData(): array { $buttonData = [ 'label' => __('Delete Folder'), diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php index 25846851ed2a6..4bd901da823fd 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php @@ -34,11 +34,11 @@ public function __construct( /** * @inheritdoc */ - public function getButtonData() + public function getButtonData(): array { $buttonData = [ 'label' => __('Add Selected'), - 'on_click' => 'return false;");', + 'on_click' => 'return false;', 'class' => 'action-primary no-display media-gallery-add-selected', 'sort_order' => 110, ]; diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php index 08cfe2386ad92..3e7756d319c8a 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php @@ -32,9 +32,9 @@ public function __construct( } /** - * @return array + * @inheritdoc */ - public function getButtonData() + public function getButtonData(): array { $buttonData = [ 'label' => __('Upload Image'), diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php index 481f8ab861f0f..7a6f59a75f1cf 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php @@ -15,12 +15,18 @@ use Magento\Framework\View\Element\UiComponentFactory; use Magento\Store\Model\StoreManagerInterface; use Magento\Ui\Component\Listing\Columns\Column; +use Magento\Framework\AuthorizationInterface; /** * Overlay column */ class Url extends Column { + private const ACL_IMAGE_ACTIONS = [ + 'insert_assets' => 'Magento_MediaGallery::insert_assets', + 'delete_assets' => 'Magento_MediaGallery::delete_assets' + ]; + /** * @var StoreManagerInterface */ @@ -41,6 +47,11 @@ class Url extends Column */ private $storage; + /** + * @var AuthorizationInterface + */ + private $authorization; + /** * @param ContextInterface $context * @param UiComponentFactory $uiComponentFactory @@ -48,6 +59,7 @@ class Url extends Column * @param UrlInterface $urlInterface * @param Images $images * @param Storage $storage + * @param AuthorizationInterface $authorization * @param array $components * @param array $data */ @@ -58,6 +70,7 @@ public function __construct( UrlInterface $urlInterface, Images $images, Storage $storage, + AuthorizationInterface $authorization, array $components = [], array $data = [] ) { @@ -66,6 +79,7 @@ public function __construct( $this->urlInterface = $urlInterface; $this->images = $images; $this->storage = $storage; + $this->authorization = $authorization; } /** @@ -98,6 +112,7 @@ public function prepare(): void array_replace_recursive( (array)$this->getData('config'), [ + 'allowedActions' => $this->getAllowedActions(), 'onInsertUrl' => $this->urlInterface->getUrl('cms/wysiwyg_images/oninsert'), 'storeId' => $this->storeManager->getStore()->getId() ] @@ -105,6 +120,21 @@ public function prepare(): void ); } + /** + * Return allowed actions for media gallery image + */ + private function getAllowedActions(): array + { + $allowedActions = []; + foreach (self::ACL_IMAGE_ACTIONS as $key => $action) { + if ($this->authorization->isAllowed($action)) { + $allowedActions[] = $key; + } + } + + return $allowedActions; + } + /** * Get URL for the provided media asset path * diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml index ba2033478afa1..67733b2c6855d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml @@ -105,5 +105,3 @@ use Magento\Framework\Escaper; } } </script> - - diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml index 9fc0e749ac888..f0b653cb8cd14 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml @@ -97,5 +97,3 @@ use Magento\Backend\Block\Template; } } </script> - - diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less index fc8bd49126d8e..71677380c1bcc 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less @@ -99,6 +99,9 @@ .media-gallery-container { + .action-disabled { + opacity: 0.5; + } .masonry-image-grid .no-data-message-container, .masonry-image-grid .error-message-container { left: 50%; diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js index 5e82f05a3c261..d81c05d805378 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directories.js @@ -19,6 +19,7 @@ define([ return Component.extend({ defaults: { + allowedActions: [], directoryTreeSelector: '#media-gallery-directory-tree', deleteButtonSelector: '#delete_folder', createFolderButtonSelector: '#create_folder', @@ -179,7 +180,7 @@ define([ * @param {String} folderId */ setActive: function (folderId) { - if (!$.inArray('delete_folder', this.allowedActions)) { + if (!this.allowedActions.includes('delete_folder')) { return; } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js index bf852d0ddae68..ca731e693bd2d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js @@ -17,6 +17,7 @@ define([ addSelectedBtnSelector: '#add_selected', deleteSelectedBtnSelector: '#delete_selected', selected: null, + allowedActions: [], fields: { id: 'id', url: 'url', @@ -39,7 +40,8 @@ define([ { component: 'Magento_MediaGalleryUi/js/grid/columns/image/actions', name: '${ $.name }_actions', - imageModelName: '${ $.name }' + imageModelName: '${ $.name }', + allowedActions: '${ $.allowedActions }' } ] }, @@ -222,8 +224,15 @@ define([ toggleAddSelectedButton: function () { if (this.selected() === null) { this.hideAddSelectedAndDeleteButon(); - } else { + + return; + } + + if (this.allowedActions.includes('insert_assets')) { $(this.addSelectedBtnSelector).removeClass('no-display'); + } + + if (this.allowedActions.includes('delete_assets')) { $(this.deleteSelectedBtnSelector).removeClass('no-display'); } }, diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js index 38743c8d83d3b..74e9b6ac8ecb7 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js @@ -8,7 +8,8 @@ define([ 'uiComponent', 'Magento_MediaGalleryUi/js/action/deleteImageWithDetailConfirmation', 'Magento_MediaGalleryUi/js/grid/columns/image/insertImageAction', - 'mage/translate' + 'mage/translate', + 'Magento_Ui/js/lib/view/utils/async' ], function ($, _, Component, deleteImageWithDetailConfirmation, image, $t) { 'use strict'; @@ -17,20 +18,24 @@ define([ template: 'Magento_MediaGalleryUi/grid/columns/image/actions', mediaGalleryImageDetailsName: 'mediaGalleryImageDetails', mediaGalleryEditDetailsName: 'mediaGalleryEditDetails', + allowedActions: [], actionsList: [ { name: 'image-details', title: $t('View Details'), + classes: 'action-menu-item', handler: 'viewImageDetails' }, { name: 'edit', title: $t('Edit'), + classes: 'action-menu-item', handler: 'editImageDetails' }, { name: 'delete', title: $t('Delete'), + classes: 'action-menu-item media-gallery-delete-assets', handler: 'deleteImageAction' } ], @@ -50,6 +55,14 @@ define([ this._super(); this.initEvents(); + if (!this.allowedActions.includes('delete_assets')) { + $.async('.media-gallery-delete-assets', function () { + $('.media-gallery-delete-assets').attr('click', '#') + .unbind('click') + .addClass('action-disabled'); + }); + } + return this; }, diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js index cd8a598b9ef67..b16db2873cfc8 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/massaction/massactions.js @@ -107,7 +107,7 @@ define([ * If images records less than one, disable "delete images" button */ checkButtonVisibility: function () { - if (!$.inArray('delete_assets', this.allowedActions)) { + if (!this.allowedActions.includes('delete_assets')) { return; } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image/actions.html b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image/actions.html index 042e119b9f40e..72447196cea55 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image/actions.html +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/template/grid/columns/image/actions.html @@ -7,9 +7,9 @@ <each args="{ data: actionsList, as: 'action' }"> <li> - <a class="action-menu-item" href="" text="action.title" + <a href="#" text="action.title" click="$parent[action.handler].bind($parent, $row())" - attr="{'data-action': 'item-' + action.name}"> + attr="{'data-action': 'item-' + action.name, class: action.classes}"> </a> </li> -</each> \ No newline at end of file +</each> From 8cdcefd99c6d8d5ac20d6d430302eac97aaed6f2 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 28 Aug 2020 21:53:51 +0300 Subject: [PATCH 079/195] Improve buttons visibility per user permission --- .../Ui/Component/DirectoryTree.php | 27 ++++++- .../Listing/Massactions/Massaction.php | 77 +++++++++++++++++++ .../ui_component/media_gallery_listing.xml | 1 + .../standalone_media_gallery_listing.xml | 1 + .../web/js/directory/directoryTree.js | 4 +- 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php index c1429595faf56..c7c12d0225504 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php @@ -10,33 +10,44 @@ use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; use Magento\Ui\Component\Container; +use Magento\Framework\AuthorizationInterface; /** * Directories tree component */ class DirectoryTree extends Container { + private const ACL_DELETE_FOLDER = 'Magento_MediaGallery::delete_folder'; + /** * @var UrlInterface */ private $url; + /** + * @var AuthorizationInterface + */ + private $authorization; + /** * Constructor * * @param ContextInterface $context * @param UrlInterface $url + * @param AuthorizationInterface $authorization * @param array $components * @param array $data */ public function __construct( ContextInterface $context, UrlInterface $url, + AuthorizationInterface $authorization, array $components = [], array $data = [] ) { parent::__construct($context, $components, $data); $this->url = $url; + $this->authorization = $authorization; } /** @@ -50,7 +61,7 @@ public function prepare(): void array_replace_recursive( (array) $this->getData('config'), [ - 'allowedActions' => [], + 'allowedActions' => $this->getAllowedActions(), 'getDirectoryTreeUrl' => $this->url->getUrl('media_gallery/directories/gettree'), 'deleteDirectoryUrl' => $this->url->getUrl('media_gallery/directories/delete'), 'createDirectoryUrl' => $this->url->getUrl('media_gallery/directories/create') @@ -58,4 +69,18 @@ public function prepare(): void ) ); } + + /** + * Return allowed actions for media gallery + */ + private function getAllowedActions(): array + { + $allowedActions = []; + + if ($this->authorization->isAllowed(self::ACL_DELETE_FOLDER)) { + $allowedActions[] = 'delete_folder'; + } + + return $allowedActions; + } } diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php new file mode 100644 index 0000000000000..c16d90116bca0 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryUi\Ui\Component\Listing\Massactions; + +use Magento\Ui\Component\Container; +use Magento\Framework\View\Element\UiComponent\ContextInterface; +use Magento\Framework\AuthorizationInterface; + +/** + * Massaction comntainer + */ +class Massaction extends Container +{ + private const ACL_IMAGE_ACTIONS = [ + 'delete_assets' => 'Magento_MediaGallery::delete_assets' + ]; + + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * Constructor + * + * @param ContextInterface $context + * @param AuthorizationInterface $authorization + * @param array $components + * @param array $data + */ + public function __construct( + ContextInterface $context, + AuthorizationInterface $authorization, + array $components = [], + array $data = [] + ) { + parent::__construct($context, $components, $data); + $this->authorization = $authorization; + } + + /** + * @inheritdoc + */ + public function prepare(): void + { + parent::prepare(); + $this->setData( + 'config', + array_replace_recursive( + (array)$this->getData('config'), + [ + 'allowedActions' => $this->getAllowedActions() + ] + ) + ); + } + + /** + * Return allowed actions for media gallery + */ + private function getAllowedActions(): array + { + $allowedActions = []; + foreach (self::ACL_IMAGE_ACTIONS as $key => $action) { + if ($this->authorization->isAllowed($action)) { + $allowedActions[] = $key; + } + } + + return $allowedActions; + } +} diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml index 7ea952280207d..b7307f9a74fae 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -181,6 +181,7 @@ <container name="media_gallery_massactions" displayArea="sorting" sortOrder="10" + class="Magento\MediaGalleryUi\Ui\Component\Listing\Massactions\Massaction" component="Magento_MediaGalleryUi/js/grid/massaction/massactions" template="Magento_MediaGalleryUi/grid/massactions/count" > <argument name="data" xsi:type="array"> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml index 66fbd91507ae7..a53a46c61f75d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml @@ -174,6 +174,7 @@ <container name="media_gallery_massactions" displayArea="sorting" sortOrder="10" + class="Magento\MediaGalleryUi\Ui\Component\Listing\Massactions\Massaction" component="Magento_MediaGalleryUi/js/grid/massaction/massactions" template="Magento_MediaGalleryUi/grid/massactions/count" > <argument name="data" xsi:type="array"> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js index 2e1e9a980cd59..84f253da826a8 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js @@ -17,6 +17,7 @@ define([ return Component.extend({ defaults: { + allowedActions: [], filterChipsProvider: 'componentType = filters, ns = ${ $.ns }', directoryTreeSelector: '#media-gallery-directory-tree', getDirectoryTreeUrl: 'media_gallery/directories/gettree', @@ -32,7 +33,8 @@ define([ }, viewConfig: [{ component: 'Magento_MediaGalleryUi/js/directory/directories', - name: '${ $.name }_directories' + name: '${ $.name }_directories', + allowedActions: '${ $.allowedActions }' }] }, From 43fd0573739a014277f99141f0cc58376fc8217a Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Fri, 28 Aug 2020 22:01:15 +0300 Subject: [PATCH 080/195] Code style improvements --- .../MediaGalleryUi/Ui/Component/DirectoryTree.php | 12 ++++++++---- .../adminhtml/web/js/grid/columns/image/actions.js | 4 +--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php index c7c12d0225504..2fc662a66e14c 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php @@ -17,7 +17,9 @@ */ class DirectoryTree extends Container { - private const ACL_DELETE_FOLDER = 'Magento_MediaGallery::delete_folder'; + private const ACL_IMAGE_ACTIONS = [ + 'delete_folder' => 'Magento_MediaGallery::delete_folder' + ]; /** * @var UrlInterface @@ -70,15 +72,17 @@ public function prepare(): void ); } + /** * Return allowed actions for media gallery */ private function getAllowedActions(): array { $allowedActions = []; - - if ($this->authorization->isAllowed(self::ACL_DELETE_FOLDER)) { - $allowedActions[] = 'delete_folder'; + foreach (self::ACL_IMAGE_ACTIONS as $key => $action) { + if ($this->authorization->isAllowed($action)) { + $allowedActions[] = $key; + } } return $allowedActions; diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js index 74e9b6ac8ecb7..ec959a06e1cce 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js @@ -57,9 +57,7 @@ define([ if (!this.allowedActions.includes('delete_assets')) { $.async('.media-gallery-delete-assets', function () { - $('.media-gallery-delete-assets').attr('click', '#') - .unbind('click') - .addClass('action-disabled'); + $('.media-gallery-delete-assets').unbind('click').addClass('action-disabled'); }); } From dfa67b0d6c6dd4050ad4b21b2f7c23c81ef8b7e5 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Sat, 29 Aug 2020 00:05:51 +0300 Subject: [PATCH 081/195] Static test fixes --- .../Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php | 1 - app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php | 1 - app/code/Magento/MediaGalleryUi/composer.json | 3 ++- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php index c86f6970d2495..3244a7d14f0a3 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Wysiwyg/Images/OnInsert.php @@ -8,7 +8,6 @@ class OnInsert extends \Magento\Cms\Controller\Adminhtml\Wysiwyg\Images { - /** * @var \Magento\Framework\Controller\Result\RawFactory */ diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php index 2fc662a66e14c..b4c791822c59c 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php @@ -72,7 +72,6 @@ public function prepare(): void ); } - /** * Return allowed actions for media gallery */ diff --git a/app/code/Magento/MediaGalleryUi/composer.json b/app/code/Magento/MediaGalleryUi/composer.json index f4701306eb369..0f73fa5d21023 100644 --- a/app/code/Magento/MediaGalleryUi/composer.json +++ b/app/code/Magento/MediaGalleryUi/composer.json @@ -12,7 +12,8 @@ "magento/module-media-gallery-metadata-api": "*", "magento/module-media-gallery-synchronization-api": "*", "magento/module-media-content-api": "*", - "magento/module-cms": "*" + "magento/module-cms": "*", + "magento/module-media-gallery": "*", }, "type": "magento2-module", "license": [ From 8ef07f464a550f54d14428c438429a1df5597e1b Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Sat, 29 Aug 2020 00:09:57 +0300 Subject: [PATCH 082/195] fix css static tests --- .../MediaGalleryUi/view/adminhtml/web/css/source/_module.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less index a1f733849c5da..4b0d8f7dec89e 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/css/source/_module.less @@ -100,7 +100,7 @@ .media-gallery-container { .action-disabled { - opacity: 0.5; + opacity: .5; } .masonry-image-grid .no-data-message-container, .masonry-image-grid .error-message-container { From 3af84161abd031f4bb9054f1efbcc4439efc9be5 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Sat, 29 Aug 2020 11:21:00 +0300 Subject: [PATCH 083/195] Fix composer json --- app/code/Magento/MediaGalleryUi/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/composer.json b/app/code/Magento/MediaGalleryUi/composer.json index 0f73fa5d21023..6008bb1579c49 100644 --- a/app/code/Magento/MediaGalleryUi/composer.json +++ b/app/code/Magento/MediaGalleryUi/composer.json @@ -13,7 +13,7 @@ "magento/module-media-gallery-synchronization-api": "*", "magento/module-media-content-api": "*", "magento/module-cms": "*", - "magento/module-media-gallery": "*", + "magento/module-media-gallery": "*" }, "type": "magento2-module", "license": [ From 7940f5952380c9f239b43940c6d607c9d2872177 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Sun, 30 Aug 2020 23:42:17 +0300 Subject: [PATCH 084/195] cover changes with mftf tests --- ...leryButtonNotDisabledOnPageActionGroup.xml | 25 ++++++ ...dminEnhancedMediaGalleryActionsSection.xml | 1 + .../AdminMediaGalleryCreateFolderAclTest.xml | 90 +++++++++++++++++++ .../AdminMediaGalleryDeleteAssetsAclTest.xml | 90 +++++++++++++++++++ .../AdminMediaGalleryDeleteFolderAclTest.xml | 90 +++++++++++++++++++ .../AdminMediaGalleryUploadAssetsAclTest.xml | 90 +++++++++++++++++++ .../Ui/Component/Control/UploadAssets.php | 1 - 7 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup.xml new file mode 100644 index 0000000000000..af2b383143f62 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup.xml @@ -0,0 +1,25 @@ +<?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="AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup"> + <annotations> + <description>Validates that the provided elemen present on page but have attribute disabled.</description> + </annotations> + <arguments> + <argument name="buttonName" type="string"/> + </arguments> + + <grabMultiple selector="{{AdminEnhancedMediaGalleryActionsSection.notDisabledButtons}}" stepKey="verifyDisabledAttribute"/> + + <assertEquals stepKey="assertSelectedCategories"> + <actualResult type="variable">verifyDisabledAttribute</actualResult> + <expectedResult type="array">[{{buttonName}}]</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryActionsSection.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryActionsSection.xml index 7f9a5aefdf69c..907f2c3116800 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryActionsSection.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryActionsSection.xml @@ -12,6 +12,7 @@ <element name="deleteViewButton" type="button" selector="//div[@data-bind='afterRender: \$data.setToolbarNode']//input/following-sibling::div/button[@class='action-delete']"/> <element name="upload" type="input" selector="#image-uploader-input"/> <element name="cancel" type="button" selector="[data-ui-id='cancel-button']"/> + <element name="notDisabledButtons" type="button" selector="//div[@class='page-actions floating-header']/button[not(@disabled='disabled') and not(@id='cancel')]"/> <element name="createFolder" type="button" selector="[data-ui-id='create-folder-button']"/> <element name="deleteFolder" type="button" selector="[data-ui-id='delete-folder-button']"/> <element name="imageSrc" type="text" selector="//div[@class='masonry-image-column' and contains(@data-repeat-index, '0')]//img[contains(@src,'{{src}}')]" parameterized="true"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml new file mode 100644 index 0000000000000..d2c980cfc923f --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMediaGalleryCreateFolderAclTest"> + <annotations> + <features value="MediaGallery"/> + <stories value="[Story 60] User manages ACL rules for Media Gallery"/> + <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1485"/> + <title value="User manages ACL rules for Media Gallery cretae folder functionality"/> + <description value="User manages ACL rules for Media Gallery cretae folder functionality"/> + <testCaseId value="https://app.hiptest.com/projects/131313/test-plan/folders/943908/scenarios/3218882"/> + <severity value="MAJOR"/> + <group value="media_gallery_ui"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminBefore"/> + </before> + <after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminAfter"/> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> + <waitForPageLoad stepKey="waitForRolesGridLoad" /> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> + <argument name="role" value="adminRole"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="goToAllUsersPage"/> + <waitForPageLoad stepKey="waitForUsersGridLoad" /> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="deleteUser"> + <argument name="userName" value="{{admin2.username}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Delete Folder"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Insert Assets into the content"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Upload Assets"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Delete Assets"/> + </actionGroup> + + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Pages"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + + <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> + <argument name="role" value="adminRole"/> + <argument name="User" value="admin2"/> + </actionGroup> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="openNewPage"/> + <actionGroup ref="AdminOpenMediaGalleryFromPageNoEditorActionGroup" stepKey="openMediaGalleryForPage"/> + <actionGroup ref="AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup" stepKey="assertCreateButtonEnabledAllOthersDisabled"> + <argument name="buttonName" value="Create Folder"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml new file mode 100644 index 0000000000000..971d08692676d --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMediaGalleryDeleteAssetsAclTest"> + <annotations> + <features value="MediaGallery"/> + <stories value="[Story 60] User manages ACL rules for Media Gallery"/> + <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1485"/> + <title value="User manages ACL rules for Media Gallery delete assets functionality"/> + <description value="User manages ACL rules for Media Gallery delete assets functionality"/> + <testCaseId value="https://app.hiptest.com/projects/131313/test-plan/folders/943908/scenarios/3218882"/> + <severity value="MAJOR"/> + <group value="media_gallery_ui"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminBefore"/> + </before> + <after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminAfter"/> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> + <waitForPageLoad stepKey="waitForRolesGridLoad" /> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> + <argument name="role" value="adminRole"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="goToAllUsersPage"/> + <waitForPageLoad stepKey="waitForUsersGridLoad" /> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="deleteUser"> + <argument name="userName" value="{{admin2.username}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Create Folder"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Insert Assets into the content"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Upload Assets"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Delete Folder"/> + </actionGroup> + + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Pages"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + + <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> + <argument name="role" value="adminRole"/> + <argument name="User" value="admin2"/> + </actionGroup> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="openNewPage"/> + <actionGroup ref="AdminOpenMediaGalleryFromPageNoEditorActionGroup" stepKey="openMediaGalleryForPage"/> + <actionGroup ref="AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup" stepKey="assertCreateButtonEnabledAllOthersDisabled"> + <argument name="buttonName" value="Delete Images..."/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml new file mode 100644 index 0000000000000..22e834e9c5fd5 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMediaGalleryDeleteFolderAclTest"> + <annotations> + <features value="MediaGallery"/> + <stories value="[Story 60] User manages ACL rules for Media Gallery"/> + <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1485"/> + <title value="User manages ACL rules for Media Gallery delete folder functionality"/> + <description value="User manages ACL rules for Media Gallery delete folder functionality"/> + <testCaseId value="https://app.hiptest.com/projects/131313/test-plan/folders/943908/scenarios/3218882"/> + <severity value="MAJOR"/> + <group value="media_gallery_ui"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminBefore"/> + </before> + <after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminAfter"/> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> + <waitForPageLoad stepKey="waitForRolesGridLoad" /> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> + <argument name="role" value="adminRole"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="goToAllUsersPage"/> + <waitForPageLoad stepKey="waitForUsersGridLoad" /> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="deleteUser"> + <argument name="userName" value="{{admin2.username}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Create Folder"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Insert Assets into the content"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Upload Assets"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Delete Assets"/> + </actionGroup> + + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Pages"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + + <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> + <argument name="role" value="adminRole"/> + <argument name="User" value="admin2"/> + </actionGroup> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="openNewPage"/> + <actionGroup ref="AdminOpenMediaGalleryFromPageNoEditorActionGroup" stepKey="openMediaGalleryForPage"/> + <actionGroup ref="AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup" stepKey="assertCreateButtonEnabledAllOthersDisabled"> + <argument name="buttonName" value="Delete Folder"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml new file mode 100644 index 0000000000000..1ddf2a5a23263 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMediaGalleryUploadAssetsAclTest"> + <annotations> + <features value="MediaGallery"/> + <stories value="[Story 60] User manages ACL rules for Media Gallery"/> + <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1485"/> + <title value="User manages ACL rules for Media Gallery upload assets functionality"/> + <description value="User manages ACL rules for Media Gallery upload assets functionality"/> + <testCaseId value="https://app.hiptest.com/projects/131313/test-plan/folders/943908/scenarios/3218882"/> + <severity value="MAJOR"/> + <group value="media_gallery_ui"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminBefore"/> + </before> + <after> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdminAfter"/> + <amOnPage url="{{AdminRolesPage.url}}" stepKey="navigateToUserRoleGrid" /> + <waitForPageLoad stepKey="waitForRolesGridLoad" /> + <actionGroup ref="AdminDeleteRoleActionGroup" stepKey="deleteUserRole"> + <argument name="role" value="adminRole"/> + </actionGroup> + <amOnPage url="{{AdminUsersPage.url}}" stepKey="goToAllUsersPage"/> + <waitForPageLoad stepKey="waitForUsersGridLoad" /> + <actionGroup ref="AdminDeleteNewUserActionGroup" stepKey="deleteUser"> + <argument name="userName" value="{{admin2.username}}"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </after> + + <actionGroup ref="AdminFillUserRoleRequiredDataActionGroup" stepKey="fillUserRoleRequiredData"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Media Gallery"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Create Folder"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Insert Assets into the content"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Delete Assets"/> + </actionGroup> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Delete Folder"/> + </actionGroup> + + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <argument name="User" value="adminRole"/> + <argument name="restrictedRole" value="Pages"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + + <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> + <argument name="role" value="adminRole"/> + <argument name="User" value="admin2"/> + </actionGroup> + + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> + <argument name="username" value="{{admin2.username}}"/> + <argument name="password" value="{{admin2.password}}"/> + </actionGroup> + <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="openNewPage"/> + <actionGroup ref="AdminOpenMediaGalleryFromPageNoEditorActionGroup" stepKey="openMediaGalleryForPage"/> + <actionGroup ref="AdminAssertMediaGalleryButtonNotDisabledOnPageActionGroup" stepKey="assertCreateButtonEnabledAllOthersDisabled"> + <argument name="buttonName" value="Upload Image"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> + </test> +</tests> diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php index 3e7756d319c8a..b74e3ebebe5cc 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php @@ -38,7 +38,6 @@ public function getButtonData(): array { $buttonData = [ 'label' => __('Upload Image'), - 'disabled' => 'disabled', 'on_click' => 'jQuery("#image-uploader-input").click();', 'class' => 'action-default scalable add media-gallery-actions-buttons', 'sort_order' => 20, From e690a8d113389a05659c83b0bf75f1dcf49186b8 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 31 Aug 2020 09:25:07 +0300 Subject: [PATCH 085/195] Refactor mftf tests --- .../AdminMediaGalleryCreateFolderAclTest.xml | 25 +++--------------- .../AdminMediaGalleryDeleteAssetsAclTest.xml | 24 +++-------------- .../AdminMediaGalleryDeleteFolderAclTest.xml | 26 ++++--------------- .../AdminMediaGalleryUploadAssetsAclTest.xml | 24 +++-------------- 4 files changed, 17 insertions(+), 82 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml index d2c980cfc923f..8e1e4bbfbd48a 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml @@ -44,30 +44,13 @@ <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Media Gallery"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Folder"/> + <argument name="restrictedRole" value="Create Folder"/> </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Insert Assets into the content"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Upload Assets"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Assets"/> - </actionGroup> - - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Pages"/> - </actionGroup> - <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> <argument name="role" value="adminRole"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml index 971d08692676d..bdfb5c475b3d8 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml @@ -42,32 +42,16 @@ <argument name="restrictedRole" value="Media Gallery"/> </actionGroup> <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Media Gallery"/> - </actionGroup> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Create Folder"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Insert Assets into the content"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Upload Assets"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Folder"/> + <argument name="restrictedRole" value="Delete Assets"/> </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Pages"/> - </actionGroup> - <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> <argument name="role" value="adminRole"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml index 22e834e9c5fd5..f261e1bd4601c 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml @@ -44,30 +44,14 @@ <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Media Gallery"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Create Folder"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Insert Assets into the content"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Upload Assets"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Assets"/> + <argument name="restrictedRole" value="Delete Folder"/> </actionGroup> - - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Pages"/> - </actionGroup> - <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> <argument name="role" value="adminRole"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml index 1ddf2a5a23263..4dae0b1ec5510 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml @@ -42,32 +42,16 @@ <argument name="restrictedRole" value="Media Gallery"/> </actionGroup> <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Media Gallery"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Create Folder"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckAddSelected"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Insert Assets into the content"/> - </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="UncheckUploadAssets"> - <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Assets"/> - </actionGroup> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Folder"/> + <argument name="restrictedRole" value="Upload Assets"/> </actionGroup> - <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Pages"/> - </actionGroup> - <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> + </actionGroup> + <actionGroup ref="AdminUserSaveRoleActionGroup" stepKey="saveRole"/> <actionGroup ref="AdminCreateUserActionGroup" stepKey="createAdminUser"> <argument name="role" value="adminRole"/> From a2346f4697a76f1d9905dc9968a79b57a9909e2b Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Mon, 31 Aug 2020 10:53:52 +0300 Subject: [PATCH 086/195] refactor --- app/code/Magento/Widget/Model/Widget.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 6d9bb1878708a..ec948063234da 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -313,7 +313,7 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) $directiveParams = ''; foreach ($params as $name => $value) { // Retrieve default option value if pre-configured - $directiveParams .= $this->getDirectiveParam($widget, $name, $value); + $directiveParams .= $value === null ? '' : $this->getDirectiveParam($widget, $name, $value); } $directive = sprintf('{{widget type="%s"%s%s}}', $type, $directiveParams, $this->getWidgetPageVarName($params)); @@ -354,9 +354,7 @@ private function getDirectiveParam(DataObject $widget, string $name, $value): st $value = $this->getPreparedValue($value); } - return $value !== null - ? sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false)) - : ''; + return sprintf(' %s="%s"', $name, $this->escaper->escapeHtmlAttr($value, false)); } /** From 4c085d7504691c2ac458edb47a73f8b8cf16c4a2 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 31 Aug 2020 11:11:02 +0300 Subject: [PATCH 087/195] Fix flaky mftf test --- .../Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml index 3dd294fa50605..77ca32be269c2 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml @@ -36,7 +36,7 @@ <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGalleryFromImageUploader"/> - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminAdobeStockMediaGalleryClearFiltersActionGroup" stepKey="clearFilters"/> <actionGroup ref="AdminMediaGalleryAssertImageInGridActionGroup" stepKey="assertImageInGrid"> <argument name="title" value="ProductImage.filename"/> </actionGroup> From 0e28c9181301034653ac1dc8a26bc728a36fcc3b Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 31 Aug 2020 11:38:48 +0300 Subject: [PATCH 088/195] fix actiongroup to clear filters --- .../Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml index 77ca32be269c2..5a72c43b2f3ed 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadCategoryImageTest.xml @@ -36,7 +36,7 @@ <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGalleryFromImageUploader"/> - <actionGroup ref="AdminAdobeStockMediaGalleryClearFiltersActionGroup" stepKey="clearFilters"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/> <actionGroup ref="AdminMediaGalleryAssertImageInGridActionGroup" stepKey="assertImageInGrid"> <argument name="title" value="ProductImage.filename"/> </actionGroup> From cbf98debe1c1bc00f3ff54267ab5ea5f88ebe6b7 Mon Sep 17 00:00:00 2001 From: SmVladyslav <vlatame.tsg@gmail.com> Date: Mon, 31 Aug 2020 15:53:02 +0300 Subject: [PATCH 089/195] MC-36777: Incorrect conversion plane text to HTML --- .../Magento/Email/view/adminhtml/templates/template/edit.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml b/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml index a16a3aae14b49..a377cd8ae6722 100644 --- a/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml +++ b/app/code/Magento/Email/view/adminhtml/templates/template/edit.phtml @@ -135,7 +135,7 @@ require([ content: "{$block->escapeJs(__('Are you sure you want to strip tags?'))}", actions: { confirm: function () { - this.unconvertedText = $('template_text').value; + self.unconvertedText = $('template_text').value; $('convert_button').hide(); $('template_text').value = $('template_text').value.stripScripts().replace( new RegExp('<style[^>]*>[\\S\\s]*?</style>', 'img'), '' From 000120d7d088900086d2cf07df355f1968ec119f Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Mon, 31 Aug 2020 23:10:14 +0800 Subject: [PATCH 090/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - added integration test for publisher --- .../Test/Integration/Model/PublisherTest.php | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php new file mode 100644 index 0000000000000..b96a6c58834f5 --- /dev/null +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php @@ -0,0 +1,141 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaContentSynchronization\Test\Integration\Model; + +use Magento\Framework\Exception\IntegrationException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\MessageQueue\ConsumerInterface; +use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; +use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; +use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; +use Magento\MediaContentSynchronization\Model\Publish; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; +use Magento\Framework\MessageQueue\ConsumerFactory; + +/** + * Test for media content Publisher + */ +class PublisherTest extends TestCase +{ + private const TOPIC_MEDIA_CONTENT_SYNCHRONIZATION = 'media.content.synchronization'; + + /** + * @var ContentIdentityInterfaceFactory + */ + private $contentIdentityFactory; + + /** + * @var GetAssetIdsByContentIdentityInterface + */ + private $getAssetIds; + + /** + * @var GetContentByAssetIdsInterface + */ + private $getContentIdentities; + + /** + * @var Publish + */ + private $publish; + + /** + * @var ConsumerInterface + */ + private $consumer; + + /** + * @var ConsumerFactory + */ + private $consumerFactory; + + protected function setUp(): void + { + $this->contentIdentityFactory = Bootstrap::getObjectManager()->get(ContentIdentityInterfaceFactory::class); + $this->publish = Bootstrap::getObjectManager()->get(Publish::class); + $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); + $this->getContentIdentities = Bootstrap::getObjectManager()->get(GetContentByAssetIdsInterface::class); + $this->consumerFactory = Bootstrap::getObjectManager()->get(ConsumerFactory::class); + $this->consumer = Bootstrap::getObjectManager()->get(ConsumerInterface::class); + } + + /** + * @dataProvider filesProvider + * @magentoDataFixture Magento/MediaContentCatalog/_files/category_with_asset.php + * @magentoDataFixture Magento/MediaContentCatalog/_files/product_with_asset.php + * @magentoDataFixture Magento/MediaContentCms/_files/page_with_asset.php + * @magentoDataFixture Magento/MediaContentCms/_files/block_with_asset.php + * @magentoDataFixture Magento/MediaGallery/_files/media_asset.php + * @param array $contentIdentities + * @throws IntegrationException + * @throws LocalizedException + */ + public function testExecute(array $contentIdentities): void + { + // publish message to the queue + $this->publish->execute($contentIdentities); + + // run and process message + $consumerName = self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION; + $this->consumerFactory->get(self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION); + $this->consumer->process(); + + // verify synchronized media content + foreach ($contentIdentities as $contentIdentity) { + $assetId = 2020; + $contentIdentityObject = $this->contentIdentityFactory->create($contentIdentity); + var_dump($this->getAssetIds->execute($contentIdentityObject)); + $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentityObject)); + $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); + var_dump(count($synchronizedContentIdentities)); + $this->assertEquals(4, count($synchronizedContentIdentities)); + + $syncedIds = []; + foreach ($synchronizedContentIdentities as $syncedContentIdentity) { + $syncedIds[] = $syncedContentIdentity->getEntityId(); + } + $this->assertContains($contentIdentity->getEntityId(), $syncedIds); + } + } + + /** + * Data provider + * + * @return array + */ + public function filesProvider(): array + { + return [ + [ + [ + [ + 'entityType' => 'catalog_category', + 'field' => 'description', + 'entityId' => 28767 + ], + [ + 'entityType' => 'catalog_product', + 'field' => 'description', + 'entityId' => 1567 + ], + [ + 'entityType' => 'cms_page', + 'field' => 'content', + 'entityId' => 5 + ], + [ + 'entityType' => 'cms_block', + 'field' => 'content', + 'entityId' => 1 + ] + ] + ] + ]; + } +} From 79151d69ba9ba8bffc919861f6a7aa650b04079e Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Tue, 1 Sep 2020 02:25:06 +0800 Subject: [PATCH 091/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated publisher integration test file --- .../Test/Integration/Model/PublisherTest.php | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php index b96a6c58834f5..69cb2853c7d11 100644 --- a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php @@ -9,14 +9,13 @@ use Magento\Framework\Exception\IntegrationException; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\MessageQueue\ConsumerInterface; +use Magento\Framework\MessageQueue\ConsumerFactory; use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; use Magento\MediaContentSynchronization\Model\Publish; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; -use Magento\Framework\MessageQueue\ConsumerFactory; /** * Test for media content Publisher @@ -26,14 +25,14 @@ class PublisherTest extends TestCase private const TOPIC_MEDIA_CONTENT_SYNCHRONIZATION = 'media.content.synchronization'; /** - * @var ContentIdentityInterfaceFactory + * @var ConsumerFactory */ - private $contentIdentityFactory; + private $consumerFactory; /** - * @var GetAssetIdsByContentIdentityInterface + * @var ContentIdentityInterfaceFactory */ - private $getAssetIds; + private $contentIdentityFactory; /** * @var GetContentByAssetIdsInterface @@ -41,28 +40,22 @@ class PublisherTest extends TestCase private $getContentIdentities; /** - * @var Publish - */ - private $publish; - - /** - * @var ConsumerInterface + * @var GetAssetIdsByContentIdentityInterface */ - private $consumer; + private $getAssetIds; /** - * @var ConsumerFactory + * @var Publish */ - private $consumerFactory; + private $publish; protected function setUp(): void { + $this->consumerFactory = Bootstrap::getObjectManager()->get(ConsumerFactory::class); $this->contentIdentityFactory = Bootstrap::getObjectManager()->get(ContentIdentityInterfaceFactory::class); - $this->publish = Bootstrap::getObjectManager()->get(Publish::class); - $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); $this->getContentIdentities = Bootstrap::getObjectManager()->get(GetContentByAssetIdsInterface::class); - $this->consumerFactory = Bootstrap::getObjectManager()->get(ConsumerFactory::class); - $this->consumer = Bootstrap::getObjectManager()->get(ConsumerInterface::class); + $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); + $this->publish = Bootstrap::getObjectManager()->get(Publish::class); } /** @@ -82,25 +75,26 @@ public function testExecute(array $contentIdentities): void $this->publish->execute($contentIdentities); // run and process message - $consumerName = self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION; - $this->consumerFactory->get(self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION); - $this->consumer->process(); + $batchSize = 1; + $maxNumberOfMessages = 1; + $consumer = $this->consumerFactory->get(self::TOPIC_MEDIA_CONTENT_SYNCHRONIZATION, $batchSize); + $consumer->process($maxNumberOfMessages); // verify synchronized media content foreach ($contentIdentities as $contentIdentity) { $assetId = 2020; $contentIdentityObject = $this->contentIdentityFactory->create($contentIdentity); - var_dump($this->getAssetIds->execute($contentIdentityObject)); $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentityObject)); $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); - var_dump(count($synchronizedContentIdentities)); $this->assertEquals(4, count($synchronizedContentIdentities)); $syncedIds = []; foreach ($synchronizedContentIdentities as $syncedContentIdentity) { $syncedIds[] = $syncedContentIdentity->getEntityId(); } - $this->assertContains($contentIdentity->getEntityId(), $syncedIds); + foreach ($contentIdentityObject as $identityObject) { + $this->assertContains($identityObject->getEntityId(), $syncedIds); + } } } From 80dae458e82ab8f55f502d5149bddfae8c722b99 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Mon, 31 Aug 2020 23:20:44 +0300 Subject: [PATCH 092/195] Fix flaky mftf tests --- ...diaGalleryAssertUsedInLinkPagesGridTest.xml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml b/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml index de8517eedae0e..4230640e76303 100644 --- a/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml +++ b/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml @@ -21,10 +21,6 @@ <before> <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> </before> - <after> - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - </after> - <actionGroup ref="AdminOpenCreateNewCMSPageActionGroup" stepKey="navigateToCreateNewPage"/> <actionGroup ref="FillOutCustomCMSPageContentActionGroup" stepKey="fillBasicPageDataForPageWithDefaultStore"> <argument name="title" value="Unique page title MediaGalleryUi"/> @@ -37,9 +33,13 @@ <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload3"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryClickImageInGridActionGroup" stepKey="selectContentImageInGrid"> - <argument name="imageName" value="{{ImageMetadata.title}}"/> + <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="viewImageDetails"/> + <actionGroup ref="AdminEnhancedMediaGalleryImageDetailsEditActionGroup" stepKey="editImage"/> + <actionGroup ref="AdminEnhancedMediaGalleryImageDetailsSaveActionGroup" stepKey="saveImage"> + <argument name="image" value="UpdatedImageDetails"/> </actionGroup> + <actionGroup ref="AdminEnhancedMediaGalleryCloseViewDetailsActionGroup" stepKey="closeViewDetails"/> + <actionGroup ref="AdminMediaGalleryClickAddSelectedActionGroup" stepKey="clickAddSelectedContentImage"/> <click selector="{{CmsNewPagePageActionsSection.saveAndContinueEdit}}" stepKey="savePage"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGallery"/> @@ -48,13 +48,11 @@ <argument name="entityName" value="Pages"/> </actionGroup> <actionGroup ref="AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup" stepKey="assertFilterApplied"> - <argument name="filterPlaceholder" value="{{ImageMetadata.title}}"/> + <argument name="filterPlaceholder" value="{{UpdatedImageDetails.title}}"/> </actionGroup> - <actionGroup ref="AdminDeleteCmsPageFromGridActionGroup" stepKey="deleteCmsPage"> <argument name="urlKey" value="test-page-1"/> </actionGroup> - <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openMediaGallery"/> <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetailsToVerfifyEmptyUsedIn"/> <actionGroup ref="AssertAdminEnhancedMediaGalleryUsedInSectionNotDisplayedActionGroup" stepKey="assertThereIsNoUsedInSection"/> @@ -62,7 +60,7 @@ <actionGroup ref="AdminEnhancedMediaGalleryEnableMassActionModeActionGroup" stepKey="enableMassActionToDeleteImages"/> <actionGroup ref="AdminEnhancedMediaGallerySelectImageForMassActionActionGroup" stepKey="selectFirstImageToDelete"> - <argument name="imageName" value="{{ImageMetadata.title}}"/> + <argument name="imageName" value="{{UpdatedImageDetails.title}}"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clikDeleteSelectedButton"/> <actionGroup ref="AdminEnhancedMediaGalleryConfirmDeleteImagesActionGroup" stepKey="deleteImages"/> From 818a7fa969b2c7bf99ca3444048d8d9b42c574cd Mon Sep 17 00:00:00 2001 From: "taras.gamanov" <engcom-vendorworker-hotel@adobe.com> Date: Tue, 1 Sep 2020 11:03:27 +0300 Subject: [PATCH 093/195] Annotations have been updated. --- .../StorefrontCustomerCheckoutWithCustomerGroupTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml index 39869190aa40e..28e779f802cde 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest/StorefrontCustomerCheckoutWithCustomerGroupTest.xml @@ -13,6 +13,7 @@ <stories value="Customer checkout with Customer Group assigned"/> <title value="Place order by Customer with Customer Group assigned"/> <description value="Customer Group should be assigned to Order when setting Auto Group Assign is enabled for Customer"/> + <testCaseId value="MC-37259"/> <severity value="MAJOR"/> <group value="checkout"/> <group value="customer"/> From 9bd2b73afe8e8b1835b4fdc6e4c3f725076c8171 Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Tue, 1 Sep 2020 12:27:57 +0300 Subject: [PATCH 094/195] MC-36907: Storefront: Create reorder from customer profile page. --- .../_files/customer_quote_ready_for_order.php | 55 +++++++ ...ustomer_quote_ready_for_order_rollback.php | 26 ++++ .../Sales/Controller/Guest/ReorderTest.php | 139 ++++++++++++++++++ .../Sales/Controller/Order/ReorderTest.php | 139 ++++++++++++++++++ .../Magento/Sales/Helper/ReorderTest.php | 106 +++++++++++++ .../customer_order_with_simple_product.php | 22 +++ ...mer_order_with_simple_product_rollback.php | 33 +++++ .../customer_order_with_taxable_product.php | 30 ++++ ...er_order_with_taxable_product_rollback.php | 35 +++++ .../order_by_guest_with_simple_product.php | 31 ++++ ..._by_guest_with_simple_product_rollback.php | 33 +++++ 11 files changed, 649 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php new file mode 100644 index 0000000000000..5cca93ce3478c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Checkout\Model\Type\Onepage; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\CartInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer.php'); +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_address.php'); +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_duplicated.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var AddressInterface $quoteShippingAddress */ +$quoteShippingAddress = $objectManager->get(AddressInterfaceFactory::class)->create(); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +/** @var AddressRepositoryInterface $addressRepository */ +$addressRepository = $objectManager->get(AddressRepositoryInterface::class); +$quoteShippingAddress->importCustomerAddressData($addressRepository->getById(1)); +$customer = $customerRepository->getById(1); + +/** @var CartInterface $quote */ +$quote = $objectManager->get(CartInterfaceFactory::class)->create(); +$quote->setStoreId(1) + ->setIsActive(true) + ->setIsMultiShipping(0) + ->assignCustomerWithAddressChange($customer) + ->setShippingAddress($quoteShippingAddress) + ->setBillingAddress($quoteShippingAddress) + ->setCheckoutMethod(Onepage::METHOD_CUSTOMER) + ->setReservedOrderId('55555555') + ->setEmail($customer->getEmail()); +$quote->addProduct($productRepository->get('simple-1'), 55); +$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate'); +$quote->getShippingAddress()->setCollectShippingRates(true); +$quote->getShippingAddress()->collectShippingRates(); +$quote->getPayment()->setMethod('checkmo'); +$quoteRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php new file mode 100644 index 0000000000000..a599d008cf89c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var GetQuoteByReservedOrderId $getQuoteByReservedOrderId */ +$getQuoteByReservedOrderId = $objectManager->get(GetQuoteByReservedOrderId::class); +$quote = $getQuoteByReservedOrderId->execute('55555555'); +if ($quote) { + $quoteRepository->delete($quote); +} + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_duplicated_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_duplicated_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_address_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php new file mode 100644 index 0000000000000..cffdda80cc897 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Guest/ReorderTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Guest; + +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Customer\Model\Session; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Helper\Guest; +use Magento\TestFramework\Request; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test for guest reorder controller. + * + * @see \Magento\Sales\Controller\Guest\Reorder + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class ReorderTest extends AbstractController +{ + /** @var CheckoutSession */ + private $checkoutSession; + + /** @var OrderInterfaceFactory */ + private $orderFactory; + + /** @var CookieManagerInterface */ + private $cookieManager; + + /** @var Session */ + private $customerSession; + + /** @var CartRepositoryInterface */ + private $quoteRepository; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->checkoutSession = $this->_objectManager->get(CheckoutSession::class); + $this->orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class); + $this->cookieManager = $this->_objectManager->get(CookieManagerInterface::class); + $this->customerSession = $this->_objectManager->get(Session::class); + $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $createdQuoteId = $this->checkoutSession->getQuoteId(); + + if ($createdQuoteId !== null) { + try { + $this->quoteRepository->delete($this->quoteRepository->get($createdQuoteId)); + } catch (NoSuchEntityException $e) { + //already deleted + } + } + + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDbIsolation disabled + * + * @magentoDataFixture Magento/Sales/_files/order_by_guest_with_simple_product.php + * + * @return void + */ + public function testReorderSimpleProduct(): void + { + $orderIncrementId = 'test_order_1'; + $order = $this->orderFactory->create()->loadByIncrementId($orderIncrementId); + $cookieValue = base64_encode($order->getProtectCode() . ':' . $orderIncrementId); + $this->cookieManager->setPublicCookie(Guest::COOKIE_NAME, $cookieValue); + $this->dispatchReorderRequest(); + $this->assertRedirect($this->stringContains('checkout/cart')); + $quoteId = $this->checkoutSession->getQuoteId(); + $this->assertNotNull($quoteId); + $quoteItemsCollection = $this->quoteRepository->get((int)$quoteId)->getItemsCollection(); + $this->assertCount(1, $quoteItemsCollection); + $this->assertEquals( + $order->getItemsCollection()->getFirstItem()->getSku(), + $quoteItemsCollection->getFirstItem()->getSku() + ); + } + + /** + * @return void + */ + public function testReorderWithoutParamsAndCookie(): void + { + $this->dispatchReorderRequest(); + $this->assertRedirect($this->stringContains('sales/guest/form')); + $this->assertSessionMessages( + $this->containsEqual((string)__('You entered incorrect data. Please try again.')), + MessageInterface::TYPE_ERROR + ); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testReorderGuestOrderByCustomer(): void + { + $this->customerSession->setCustomerId(1); + $this->dispatchReorderRequest(); + $this->assertRedirect($this->stringContains('sales/order/history')); + } + + /** + * Dispatch reorder request. + * + * @return void + */ + private function dispatchReorderRequest(): void + { + $this->getRequest()->setMethod(Request::METHOD_POST); + $this->dispatch('sales/guest/reorder/'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php new file mode 100644 index 0000000000000..647dc557485d2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Order; + +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Customer\Model\Session; +use Magento\Framework\Escaper; +use Magento\Framework\Message\MessageInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\TestFramework\Request; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Test for reorder controller. + * + * @see \Magento\Sales\Controller\Order\Reorder + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class ReorderTest extends AbstractController +{ + /** @var CheckoutSession */ + private $checkoutSession; + + /** @var OrderInterfaceFactory */ + private $orderFactory; + + /** @var Session */ + private $customerSession; + + /** @var CartRepositoryInterface */ + private $quoteRepository; + + /** @var CartInterface */ + private $quote; + + /** @var Escaper */ + private $escaper; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->checkoutSession = $this->_objectManager->get(CheckoutSession::class); + $this->orderFactory = $this->_objectManager->get(OrderInterfaceFactory::class); + $this->customerSession = $this->_objectManager->get(Session::class); + $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $this->escaper = $this->_objectManager->get(Escaper::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + if ($this->quote instanceof CartInterface) { + $this->quoteRepository->delete($this->quote); + } + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Sales/_files/customer_order_with_taxable_product.php + * + * @return void + */ + public function testReorder(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('test_order_with_taxable_product'); + $this->customerSession->setCustomerId($order->getCustomerId()); + $this->dispatchReorderRequest((int)$order->getId()); + $this->assertRedirect($this->stringContains('checkout/cart')); + $this->quote = $this->checkoutSession->getQuote(); + $quoteItemsCollection = $this->quote->getItemsCollection(); + $this->assertCount(1, $quoteItemsCollection); + $this->assertEquals( + $order->getItemsCollection()->getFirstItem()->getSku(), + $quoteItemsCollection->getFirstItem()->getSku() + ); + } + + /** + * @magentoDataFixture Magento/Sales/_files/customer_order_with_simple_product.php + * + * @return void + */ + public function testReorderProductLowQty(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('55555555'); + $this->customerSession->setCustomerId($order->getCustomerId()); + $this->dispatchReorderRequest((int)$order->getId()); + $origMessage = (string)__('The requested qty is not available'); + $message = $this->escaper->escapeHtml( + __('Could not add the product with SKU "%1" to the shopping cart: %2', 'simple-1', $origMessage) + ); + $constraint = $this->logicalOr($this->containsEqual($origMessage), $this->containsEqual($message)); + $this->assertThat($this->getMessages(MessageInterface::TYPE_ERROR), $constraint); + $this->quote = $this->checkoutSession->getQuote(); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php + * + * @return void + */ + public function testReorderByAnotherCustomer(): void + { + $this->customerSession->setCustomerId(1); + $order = $this->orderFactory->create()->loadByIncrementId('100000555'); + $this->dispatchReorderRequest((int)$order->getId()); + $this->assertRedirect($this->stringContains('sales/order/history')); + } + + /** + * Dispatch reorder request. + * + * @param null|int $orderId + * @return void + */ + private function dispatchReorderRequest(?int $orderId = null): void + { + $this->getRequest()->setMethod(Request::METHOD_POST); + $this->getRequest()->setParam('order_id', $orderId); + $this->dispatch('sales/order/reorder/'); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php new file mode 100644 index 0000000000000..5a21f551ff1a7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Helper/ReorderTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Helper; + +use Magento\Customer\Model\Session; +use Magento\Framework\ObjectManagerInterface; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for reorder helper. + * + * @see \Magento\Sales\Helper\Reorder + * @magentoDbIsolation enabled + */ +class ReorderTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Reorder */ + private $helper; + + /** @var OrderInterfaceFactory */ + private $orderFactory; + + /** @var Session */ + private $customerSession; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->helper = $this->objectManager->get(Reorder::class); + $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class); + $this->customerSession = $this->objectManager->get(Session::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->customerSession->setCustomerId(null); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testCanReorderForGuest(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('100000001'); + $this->assertTrue($this->helper->canReorder($order->getId())); + } + + /** + * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php + * + * @return void + */ + public function testCanReorderForLoggedCustomer(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('100000555'); + $this->customerSession->setCustomerId($order->getCustomerId()); + $this->assertTrue($this->helper->canReorder($order->getId())); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Sales/_files/order_state_hold.php + * + * @return void + */ + public function testCanReorderHoldOrderForLoggedCustomer(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('100000001'); + $this->customerSession->setCustomerId(1); + $this->assertFalse($this->helper->canReorder($order->getId())); + } + + /** + * @magentoConfigFixture current_store sales/reorder/allow 0 + * @magentoDataFixture Magento/Sales/_files/order.php + * + * @return void + */ + public function testCanReorderConfigDisabled(): void + { + $order = $this->orderFactory->create()->loadByIncrementId('100000001'); + $this->assertFalse($this->helper->canReorder($order->getId())); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php new file mode 100644 index 0000000000000..ca102b0fabf89 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/customer_quote_ready_for_order.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var CartManagementInterface $quoteManagement */ +$quoteManagement = $objectManager->get(CartManagementInterface::class); + +$quote = $quoteRepository->getActiveForCustomer(1); +$quoteManagement->placeOrder($quote->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php new file mode 100644 index 0000000000000..46cabc2e3fd9b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_simple_product_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var OrderInterfaceFactory $orderFactory */ +$orderFactory = $objectManager->get(OrderInterfaceFactory::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$order = $orderFactory->create()->loadByIncrementId('55555555'); +if ($order->getId()) { + $orderRepository->delete($order); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/customer_quote_ready_for_order_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php new file mode 100644 index 0000000000000..59ec4182ac870 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_taxable_product_and_customer.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var CartManagementInterface $quoteManagement */ +$quoteManagement = $objectManager->get(CartManagementInterface::class); +/** @var PaymentInterface $payment */ +$payment = $objectManager->get(PaymentInterface::class); +$payment->setMethod('checkmo'); + +$quote = $quoteRepository->getActiveForCustomer(1); +$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate'); +$quote->getShippingAddress()->setCollectShippingRates(true); +$quote->getShippingAddress()->collectShippingRates(); +$quoteRepository->save($quote); +$quoteManagement->placeOrder($quote->getId(), $payment); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php new file mode 100644 index 0000000000000..d42f6a1140286 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_taxable_product_rollback.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var OrderInterfaceFactory $orderFactory */ +$orderFactory = $objectManager->get(OrderInterfaceFactory::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$order = $orderFactory->create()->loadByIncrementId('test_order_with_taxable_product'); +if ($order->getId()) { + $orderRepository->delete($order); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +Resolver::getInstance()->requireDataFixture( + 'Magento/Checkout/_files/quote_with_taxable_product_and_customer_rollback.php' +); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php new file mode 100644 index 0000000000000..c3bab9acca27b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_address_saved.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var CartManagementInterface $quoteManagement */ +$quoteManagement = $objectManager->get(CartManagementInterface::class); +/** @var PaymentInterface $payment */ +$payment = $objectManager->get(PaymentInterface::class); + +$quote = $objectManager->get(GetQuoteByReservedOrderId::class)->execute('test_order_1'); +$quote->getShippingAddress()->setShippingMethod('flatrate_flatrate'); +$quote->getShippingAddress()->setCollectShippingRates(true); +$quote->getShippingAddress()->collectShippingRates(); +$quoteRepository->save($quote); +$payment->setMethod('checkmo'); +$quoteManagement->placeOrder($quote->getId(), $payment); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php new file mode 100644 index 0000000000000..b4ec514d1311e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_by_guest_with_simple_product_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var OrderInterfaceFactory $orderFactory */ +$orderFactory = $objectManager->get(OrderInterfaceFactory::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$order = $orderFactory->create()->loadByIncrementId('test_order_1'); +if ($order->getId()) { + $orderRepository->delete($order); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_address_saved_rollback.php'); From 6e0864065b760a1aa72d53a6d453c299f9c50e87 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Tue, 1 Sep 2020 13:44:50 +0300 Subject: [PATCH 095/195] MC-37263: View and edit shopping cart --- .../_files/inactive_quote_with_customer.php | 39 +++ .../inactive_quote_with_customer_rollback.php | 24 ++ .../Adminhtml/Order/Create/Items/GridTest.php | 69 ++++ .../Order/Create/Sidebar/CartTest.php | 80 +++++ .../Adminhtml/Order/Create/LoadBlockTest.php | 301 ++++++++++++++++++ 5 files changed, 513 insertions(+) create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php create mode 100644 dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php new file mode 100644 index 0000000000000..c74e76f74115f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer.php @@ -0,0 +1,39 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Checkout\Model\Type\Onepage; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\CartInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer.php'); +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/taxable_simple_product.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +$customer = $customerRepository->get('customer@example.com'); + +/** @var CartInterface $quote */ +$quote = $objectManager->get(CartInterfaceFactory::class)->create(); +$quote->setStoreId(1) + ->setIsActive(false) + ->setIsMultiShipping(0) + ->setCustomer($customer) + ->setCheckoutMethod(Onepage::METHOD_CUSTOMER) + ->setReservedOrderId('test_order_with_customer_inactive_quote') + ->addProduct($productRepository->get('taxable_product'), 1); +$quoteRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php new file mode 100644 index 0000000000000..d45cbb547d29d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/inactive_quote_with_customer_rollback.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var CartRepositoryInterface $quoteRepository */ +$quoteRepository = $objectManager->get(CartRepositoryInterface::class); +/** @var GetQuoteByReservedOrderId $getQuoteByReservedOrderId */ +$getQuoteByReservedOrderId = $objectManager->get(GetQuoteByReservedOrderId::class); +$quote = $getQuoteByReservedOrderId->execute('test_order_with_customer_inactive_quote'); +if ($quote !== null) { + $quoteRepository->delete($quote); +} + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/taxable_simple_product_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php new file mode 100644 index 0000000000000..b26b71803848f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Items/GridTest.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order\Create\Items; + +use Magento\Backend\Model\Session\Quote; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Sales\Block\Adminhtml\Order\Create\Items; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use PHPUnit\Framework\TestCase; + +/** + * Checks order items grid + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class GridTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Grid */ + private $block; + + /** @var Quote */ + private $session; + + /** @var GetQuoteByReservedOrderId */ + private $getQuoteByReservedOrderId; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class); + $this->session = $this->objectManager->get(Quote::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Grid::class); + $this->layout->createBlock(Items::class)->setChild('items_grid', $this->block); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * + * @return void + */ + public function testGetItems(): void + { + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->session->setQuoteId($quote->getId()); + $items = $this->block->getItems(); + $this->assertCount(1, $items); + $this->assertEquals('simple2', reset($items)->getSku()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php new file mode 100644 index 0000000000000..291fda6e2494f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/CartTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Block\Adminhtml\Order\Create\Sidebar; + +use Magento\Backend\Model\Session\Quote; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Check sidebar shopping cart section block + * + * @see \Magento\Sales\Block\Adminhtml\Order\Create\Sidebar\Cart + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class CartTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Cart */ + private $block; + + /** @var Quote */ + private $session; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Cart::class); + $this->session = $this->objectManager->get(Quote::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->session->clearStorage(); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * + * @return void + */ + public function testGetItemCollection(): void + { + $this->session->setCustomerId(1); + $items = $this->block->getItemCollection(); + $this->assertCount(1, $items); + $this->assertEquals('simple2', reset($items)->getSku()); + } + + /** + * @return void + */ + public function testClearShoppingCartButton(): void + { + $confirmation = __('Are you sure you want to delete all items from shopping cart?'); + $button = $this->block->getChildBlock('empty_customer_cart_button'); + $this->assertEquals(sprintf("order.clearShoppingCart('%s')", $confirmation), $button->getOnclick()); + $this->assertEquals(__('Clear Shopping Cart'), $button->getLabel()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php new file mode 100644 index 0000000000000..b6aa44bac1c4d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/LoadBlockTest.php @@ -0,0 +1,301 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Controller\Adminhtml\Order\Create; + +use Magento\Backend\Model\Session\Quote; +use Magento\Framework\App\Request\Http; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\View\LayoutInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use Magento\TestFramework\TestCase\AbstractBackendController; + +/** + * Class checks create order load block controller. + * + * @see \Magento\Sales\Controller\Adminhtml\Order\Create\LoadBlock + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + */ +class LoadBlockTest extends AbstractBackendController +{ + /** @var LayoutInterface */ + private $layout; + + /** @var GetQuoteByReservedOrderId */ + private $getQuoteByReservedOrderId; + + /** @var Quote */ + private $session; + + /** @var CartRepositoryInterface */ + private $quoteRepository; + + /** @var StoreManagerInterface */ + private $storeManager; + + /** @var array */ + private $quoteIdsToRemove; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->layout = $this->_objectManager->get(LayoutInterface::class); + $this->getQuoteByReservedOrderId = $this->_objectManager->get(GetQuoteByReservedOrderId::class); + $this->session = $this->_objectManager->get(Quote::class); + $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->quoteIdsToRemove[] = $this->session->getQuote()->getId(); + foreach ($this->quoteIdsToRemove as $quoteId) { + try { + $this->quoteRepository->delete($this->quoteRepository->get($quoteId)); + } catch (NoSuchEntityException $e) { + //do nothing + } + } + + $this->session->clearStorage(); + + parent::tearDown(); + } + + /** + * @dataProvider responseFlagsProvider + * + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * + * @param bool $asJson + * @param bool $asJsVarname + * @return void + */ + public function testAddProductToOrderFromShoppingCart(bool $asJson, bool $asJsVarname): void + { + $oldQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $params = $this->hydrateParams([ + 'json' => $asJson, + 'as_js_varname' => $asJsVarname, + ]); + $post = $this->hydratePost([ + 'sidebar' => [ + 'add_cart_item' => [ + $oldQuote->getItemsCollection()->getFirstItem()->getId() => 1, + ], + ], + ]); + + $this->dispatchWitParams($params, $post); + + $this->checkHandles(explode(',', $params['block']), $asJson); + $this->checkQuotes($oldQuote, 'simple2'); + + if ($asJsVarname) { + $this->assertRedirect($this->stringContains('sales/order_create/showUpdateResult')); + } + } + + /** + * @return array + */ + public function responseFlagsProvider(): array + { + return [ + 'as_json' => [ + 'as_json' => true, + 'as_js_varname' => false, + ], + 'as_plain' => [ + 'as_json' => false, + 'as_js_varname' => true, + ], + ]; + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * + * @return void + */ + public function testRemoveProductFromShoppingCart(): void + { + $oldQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $post = $this->hydratePost([ + 'sidebar' => [ + 'remove' => [ + $oldQuote->getItemsCollection()->getFirstItem()->getId() => 'cart', + ], + ], + ]); + $params = $this->hydrateParams(); + + $this->dispatchWitParams($params, $post); + + $this->checkHandles(explode(',', $params['block'])); + $this->checkQuotes($oldQuote); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * + * @return void + */ + public function testClearShoppingCart(): void + { + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $post = $this->hydratePost([ + 'sidebar' => [ + 'empty_customer_cart' => '1', + ], + ]); + $params = $this->hydrateParams(); + + $this->dispatchWitParams($params, $post); + + $this->checkHandles(explode(',', $params['block'])); + $this->assertEmpty($quote->getItemsCollection(false)->getItems()); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/inactive_quote_with_customer.php + * + * @return void + */ + public function testMoveFromOrderToShoppingCart(): void + { + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_inactive_quote'); + $this->session->setQuoteId($quote->getId()); + $post = $this->hydratePost([ + 'update_items' => '1', + 'item' => [ + $quote->getItemsCollection()->getFirstItem()->getId() => [ + 'qty' => '1', + 'use_discount' => '1', + 'action' => 'cart', + ], + ], + ]); + $params = $this->hydrateParams(['blocks' => null]); + $this->dispatchWitParams($params, $post); + $customerCart = $this->quoteRepository->getForCustomer(1); + $cartItems = $customerCart->getItemsCollection(); + $this->assertCount(1, $cartItems->getItems()); + $this->assertEquals('taxable_product', $cartItems->getFirstItem()->getSku()); + $this->quoteIdsToRemove[] = $customerCart->getId(); + } + + /** + * Check customer quotes + * + * @param CartInterface $oldQuote + * @param string|null $expectedSku + * @return void + */ + private function checkQuotes(CartInterface $oldQuote, ?string $expectedSku = null): void + { + $newQuote = $this->session->getQuote(); + $oldQuoteItemCollection = $oldQuote->getItemsCollection(false); + $this->assertEmpty($oldQuoteItemCollection->getItems()); + $newQuoteItemsCollection = $newQuote->getItemsCollection(false); + + if ($expectedSku !== null) { + $this->assertNotNull($newQuoteItemsCollection->getItemByColumnValue('sku', $expectedSku)); + } else { + $this->assertEmpty($newQuoteItemsCollection->getItems()); + } + } + + /** + * Check that all required handles were applied + * + * @param array $blocks + * @param bool $asJson + * @return void + */ + private function checkHandles(array $blocks, bool $asJson = true): void + { + $handles = $this->layout->getUpdate()->getHandles(); + + if ($asJson) { + $this->assertContains('sales_order_create_load_block_message', $handles); + $this->assertContains('sales_order_create_load_block_json', $handles); + } else { + $this->assertContains('sales_order_create_load_block_plain', $handles); + } + + foreach ($blocks as $block) { + $this->assertContains( + 'sales_order_create_load_block_' . $block, + $handles + ); + } + } + + /** + * Fill post params array to proper state + * + * @param array $inputArray + * @return array + */ + private function hydratePost(array $inputArray = []): array + { + return array_merge( + [ + 'customer_id' => 1, + 'store_id' => $this->storeManager->getStore('default')->getId(), + 'sidebar' => [], + ], + $inputArray + ); + } + + /** + * Fill params array to proper state + * + * @param array $inputArray + * @return array + */ + private function hydrateParams(array $inputArray = []): array + { + return array_merge( + [ + 'json' => true, + 'block' => 'sidebar,items,shipping_method,billing_method,totals,giftmessage', + 'as_js_varname' => true, + ], + $inputArray + ); + } + + /** + * Dispatch request with params + * + * @param array $params + * @param array $postParams + * @return void + */ + private function dispatchWitParams(array $params, array $postParams): void + { + $this->getRequest()->setMethod(Http::METHOD_POST) + ->setPostValue($postParams) + ->setParams($params); + $this->dispatch('backend/sales/order_create/loadBlock'); + } +} From 70042a6efc387c951860a98ede79246dc5beb053 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Tue, 1 Sep 2020 18:53:36 +0800 Subject: [PATCH 096/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated publisher integration test file --- .../Test/Integration/Model/PublisherTest.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php index 69cb2853c7d11..595c6d895bed0 100644 --- a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php @@ -86,7 +86,7 @@ public function testExecute(array $contentIdentities): void $contentIdentityObject = $this->contentIdentityFactory->create($contentIdentity); $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentityObject)); $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); - $this->assertEquals(4, count($synchronizedContentIdentities)); + $this->assertEquals(2, count($synchronizedContentIdentities)); $syncedIds = []; foreach ($synchronizedContentIdentities as $syncedContentIdentity) { @@ -117,16 +117,6 @@ public function filesProvider(): array 'entityType' => 'catalog_product', 'field' => 'description', 'entityId' => 1567 - ], - [ - 'entityType' => 'cms_page', - 'field' => 'content', - 'entityId' => 5 - ], - [ - 'entityType' => 'cms_block', - 'field' => 'content', - 'entityId' => 1 ] ] ] From c1cac89621caa92996ee719eecbeede407b13fa1 Mon Sep 17 00:00:00 2001 From: Tu Nguyen <tuna@ecommage.com> Date: Tue, 1 Sep 2020 22:07:36 +0700 Subject: [PATCH 097/195] removed redundant and clean code less --- .../css/source/module/components/_currency-addon.less | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less index fa158589feb96..654236e143a29 100644 --- a/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less +++ b/app/design/adminhtml/Magento/backend/Magento_ConfigurableProduct/web/css/source/module/components/_currency-addon.less @@ -18,15 +18,10 @@ // _____________________________________________ .currency-addon { + .lib-vendor-prefix-display(inline-flex); border: 1px solid rgb(173,173,173); - position: relative; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; flex-flow: row nowrap; + position: relative; width: 100%; .admin__control-text { From 04a9f44e2e000d180a2d02bef81768df660812e6 Mon Sep 17 00:00:00 2001 From: Nazar Klovanych <nazarn96@gmail.com> Date: Tue, 1 Sep 2020 21:54:04 +0300 Subject: [PATCH 098/195] Fix issue with saving filters from url-applier to default view of bookmark --- .../Ui/view/base/web/js/grid/url-filter-applier.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js b/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js index be9044143c5a4..3c5e72d4d66ed 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/url-filter-applier.js @@ -13,10 +13,12 @@ define([ return Component.extend({ defaults: { listingNamespace: null, + bookmarkProvider: 'componentType = bookmark, ns = ${ $.listingNamespace }', filterProvider: 'componentType = filters, ns = ${ $.listingNamespace }', filterKey: 'filters', searchString: location.search, modules: { + bookmarks: '${ $.bookmarkProvider }', filterComponent: '${ $.filterProvider }' } }, @@ -49,6 +51,16 @@ define([ return; } + if (!_.isUndefined(this.bookmarks())) { + if (!_.size(this.bookmarks().getViewData(this.bookmarks().defaultIndex))) { + setTimeout(function () { + this.apply(); + }.bind(this), 500); + + return; + } + } + if (Object.keys(urlFilter).length) { applied = this.filterComponent().get('applied'); filters = $.extend({}, applied, urlFilter); From 82db3c71e236cdacb7a47c9e5990d7c22cc272c8 Mon Sep 17 00:00:00 2001 From: Nikita Shcherbatykh <nikita.shcherbatykh@transoftgroup.com> Date: Wed, 2 Sep 2020 11:39:22 +0300 Subject: [PATCH 099/195] MC-37101: Can not delete leading 0 in custom customer attribute --- app/code/Magento/Eav/Model/Entity/AbstractEntity.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php index f2f767b4e41fa..b3737f67705d1 100644 --- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php +++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php @@ -626,6 +626,8 @@ protected function _isApplicableAttribute($object, $attribute) public function walkAttributes($partMethod, array $args = [], $collectExceptionMessages = null) { $methodArr = explode('/', $partMethod); + $part = ''; + $method = ''; switch (count($methodArr)) { case 1: $part = 'attribute'; @@ -642,6 +644,7 @@ public function walkAttributes($partMethod, array $args = [], $collectExceptionM } $results = []; $suffix = $this->getAttributesCacheSuffix($args[0]); + $instance = null; foreach ($this->getAttributesByScope($suffix) as $attrCode => $attribute) { if (isset($args[0]) && is_object($args[0]) && !$this->_isApplicableAttribute($args[0], $attribute)) { continue; @@ -1337,7 +1340,9 @@ protected function _collectSaveData($newObject) if ($this->_canUpdateAttribute($attribute, $v, $origData)) { if ($this->_isAttributeValueEmpty($attribute, $v)) { $this->_aggregateDeleteData($delete, $attribute, $newObject); - } elseif (!is_numeric($v) && $v !== $origData[$k] || is_numeric($v) && $v != $origData[$k]) { + } elseif (!is_numeric($v) && $v !== $origData[$k] + || is_numeric($v) && ($v != $origData[$k] || strlen($v) !== strlen($origData[$k])) + ) { $update[$attrId] = [ 'value_id' => $attribute->getBackend()->getEntityValueId($newObject), 'value' => is_array($v) ? array_shift($v) : $v,//@TODO: MAGETWO-44182, @@ -1739,6 +1744,7 @@ public function delete($object) { try { $connection = $this->transactionManager->start($this->getConnection()); + $id = 0; if (is_numeric($object)) { $id = (int) $object; } elseif ($object instanceof \Magento\Framework\Model\AbstractModel) { From 798e61ffa7c242c9ab84b3b7ffd01a72623e71ed Mon Sep 17 00:00:00 2001 From: Olga Zakharchuk <olgaz@ven.com> Date: Wed, 2 Sep 2020 12:10:42 +0300 Subject: [PATCH 100/195] Fixed issue with critical error: The following tag(s) are not allowed: img Details: https://github.com/magento/magento2/pull/25418#discussion_r478456345 --- .../templates/order/creditmemo/items/renderer/default.phtml | 2 +- .../templates/order/invoice/items/renderer/default.phtml | 2 +- .../view/frontend/templates/order/items/renderer/default.phtml | 2 +- .../templates/order/shipment/items/renderer/default.phtml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml index b2e84691a45cf..c7a1b4b79b91a 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml @@ -17,7 +17,7 @@ <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml index 0176582f0fcd7..d05d6d01a5340 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml @@ -17,7 +17,7 @@ <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml index 51e43476238be..f0a0f46265a3e 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml @@ -17,7 +17,7 @@ $_item = $block->getItem(); <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml index 26fe74b0fc454..773100601d2b8 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml @@ -16,7 +16,7 @@ <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> - <?= $block->escapeHtml($_formatedOptionValue['value'], ['a', 'img']) ?> + <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> From 7f311a3a4c72de571f1baca61f28800299f6ede2 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Wed, 2 Sep 2020 12:53:42 +0300 Subject: [PATCH 101/195] Set action Group as deprecation --- .../AdminOpenCmsPageActionGroup.xml | 3 +++ .../AdminOpentCmsBlockActionGroup.xml | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml index 7e907b5b395a4..68eca3b429e2b 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsPageActionGroup.xml @@ -8,6 +8,9 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOpenCmsPageActionGroup"> + <annotations> + <description>Open CMS edit page.</description> + </annotations> <arguments> <argument name="page_id" type="string"/> </arguments> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml new file mode 100644 index 0000000000000..29e100a7e9c37 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.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="AdminOpenCmsBlockActionGroup"> + <annotations> + <description>DEPRECATED. Use AdminOpenCmsPageActionGroup instead.</description> + </annotations> + <arguments> + <argument name="block_id" type="string"/> + </arguments> + <amOnPage url="{{AdminEditBlockPage.url(block_id)}}" stepKey="openEditCmsBlock"/> + </actionGroup> +</actionGroups> From a48c133dd40bda9536512875173eb8b47e556d77 Mon Sep 17 00:00:00 2001 From: Myroslav Dobra <dmaraptor@gmail.com> Date: Wed, 2 Sep 2020 14:20:28 +0300 Subject: [PATCH 102/195] MC-36615: [MFTF] AdminProductGridUrlFilterApplierTest fails because of bad design --- .../Mftf/Test/AdminProductGridUrlFilterApplierTest.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml index 2eda7b8d02481..bfa80c2e24b48 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridUrlFilterApplierTest.xml @@ -20,8 +20,14 @@ </annotations> <before> - <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductIndex"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearGridFilter"/> + <!-- Should wait a bit for filters really cleared because waitForPageLoad does not wait for javascripts to be finished --> + <!-- Without this test will fail sometimes --> + <wait time="5" stepKey="waitFilterReallyCleared"/> + <reloadPage stepKey="reloadPage"/> </before> <after> From 67636e7e840923ddbe7cf58812a553239367b59e Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 2 Sep 2020 12:32:24 +0100 Subject: [PATCH 103/195] magento/magento2#29715: Moved ACL to MediaGalleryApi --- app/code/Magento/MediaGallery/etc/acl.xml | 26 ------------------- app/code/Magento/MediaGalleryApi/etc/acl.xml | 26 +++++++++++++++++++ .../Controller/Adminhtml/Category/Index.php | 2 +- .../MediaGalleryCatalogUi/composer.json | 1 - .../media_gallery_category_listing.xml | 2 +- .../Controller/Adminhtml/Asset/Search.php | 2 +- .../Adminhtml/Directories/Create.php | 2 +- .../Adminhtml/Directories/Delete.php | 2 +- .../Adminhtml/Directories/GetTree.php | 2 +- .../Controller/Adminhtml/Image/Delete.php | 2 +- .../Controller/Adminhtml/Image/Details.php | 2 +- .../Adminhtml/Image/SaveDetails.php | 2 +- .../Controller/Adminhtml/Image/Upload.php | 2 +- .../Controller/Adminhtml/Index/Index.php | 2 +- .../Controller/Adminhtml/Media/Index.php | 2 +- .../Ui/Component/Control/CreateFolder.php | 2 +- .../Ui/Component/Control/DeleteAssets.php | 2 +- .../Ui/Component/Control/DeleteFolder.php | 2 +- .../Ui/Component/Control/InsertAsstes.php | 2 +- .../Ui/Component/Control/UploadAssets.php | 2 +- .../Ui/Component/DirectoryTree.php | 2 +- .../Ui/Component/Listing/Columns/Url.php | 4 +-- .../Listing/Massactions/Massaction.php | 2 +- .../MediaGalleryUi/etc/adminhtml/menu.xml | 4 +-- .../layout/media_gallery_index_index.xml | 2 +- .../ui_component/media_gallery_listing.xml | 2 +- .../standalone_media_gallery_listing.xml | 2 +- 27 files changed, 52 insertions(+), 53 deletions(-) delete mode 100644 app/code/Magento/MediaGallery/etc/acl.xml create mode 100644 app/code/Magento/MediaGalleryApi/etc/acl.xml diff --git a/app/code/Magento/MediaGallery/etc/acl.xml b/app/code/Magento/MediaGallery/etc/acl.xml deleted file mode 100644 index a90d43e2a6cd2..0000000000000 --- a/app/code/Magento/MediaGallery/etc/acl.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?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:Acl/etc/acl.xsd"> - <acl> - <resources> - <resource id="Magento_Backend::admin"> - <resource id="Magento_Backend::content"> - <resource id="Magento_Backend::content_elements"> - <resource id="Magento_Cms::media_gallery" title="Media Gallery" translate="title"> - <resource id="Magento_MediaGallery::upload_assets" title="Upload Assets" translate="title" sortOrder="80"/> - <resource id="Magento_MediaGallery::delete_assets" title="Delete Assets" translate="title" sortOrder="70"/> - <resource id="Magento_MediaGallery::insert_assets" title="Insert Assets into the content" translate="title" sortOrder="60"/> - <resource id="Magento_MediaGallery::create_folder" title="Create Folder" translate="title" sortOrder="50"/> - <resource id="Magento_MediaGallery::delete_folder" title="Delete Folder" translate="title" sortOrder="40"/> - </resource> - </resource> - </resource> - </resource> - </resources> - </acl> -</config> diff --git a/app/code/Magento/MediaGalleryApi/etc/acl.xml b/app/code/Magento/MediaGalleryApi/etc/acl.xml new file mode 100644 index 0000000000000..50f7114d1f3be --- /dev/null +++ b/app/code/Magento/MediaGalleryApi/etc/acl.xml @@ -0,0 +1,26 @@ +<?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:Acl/etc/acl.xsd"> + <acl> + <resources> + <resource id="Magento_Backend::admin"> + <resource id="Magento_Backend::content"> + <resource id="Magento_Backend::content_elements"> + <resource id="Magento_MediaGalleryApi::media_gallery" title="Media Gallery" translate="title"> + <resource id="Magento_MediaGalleryApi::upload_assets" title="Upload Assets" translate="title" sortOrder="80"/> + <resource id="Magento_MediaGalleryApi::delete_assets" title="Delete Assets" translate="title" sortOrder="70"/> + <resource id="Magento_MediaGalleryApi::insert_assets" title="Insert Assets into the content" translate="title" sortOrder="60"/> + <resource id="Magento_MediaGalleryApi::create_folder" title="Create Folder" translate="title" sortOrder="50"/> + <resource id="Magento_MediaGalleryApi::delete_folder" title="Delete Folder" translate="title" sortOrder="40"/> + </resource> + </resource> + </resource> + </resource> + </resources> + </acl> +</config> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php b/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php index a541e9999b784..d2fb90f3bccff 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php +++ b/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php @@ -18,7 +18,7 @@ */ class Index extends Action implements HttpGetActionInterface { - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * Get the media gallery layout diff --git a/app/code/Magento/MediaGalleryCatalogUi/composer.json b/app/code/Magento/MediaGalleryCatalogUi/composer.json index 985d581beff25..418e113072ae3 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/composer.json +++ b/app/code/Magento/MediaGalleryCatalogUi/composer.json @@ -4,7 +4,6 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", - "magento/module-cms": "*", "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-store": "*", diff --git a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml index e12d90b95303b..e289c19fe6219 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml @@ -27,7 +27,7 @@ </storageConfig> <updateUrl path="mui/index/render"/> </settings> - <aclResource>Magento_Cms::media_gallery</aclResource> + <aclResource>Magento_MediaGalleryApi::media_gallery</aclResource> <dataProvider class="Magento\MediaGalleryCatalogUi\Model\Listing\DataProvider" name="media_gallery_category_listing_data_source"> <settings> <requestFieldName>entity_id</requestFieldName> diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php index 9b6c08edbc86d..6aaa7d44cb508 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php @@ -34,7 +34,7 @@ class Search extends Action implements HttpGetActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * @var SearchAssetsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php index f303cd4b7d5e5..f65f9d622aeba 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php @@ -29,7 +29,7 @@ class Create extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGallery::create_folder'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::create_folder'; /** * @var CreateDirectoriesByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php index f11fa36c0e499..bda891f93e907 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php @@ -30,7 +30,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGallery::delete_folder'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::delete_folder'; /** * @var DeleteAssetsByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php index d4885cae055dd..0872685444193 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php @@ -25,7 +25,7 @@ class GetTree extends Action implements HttpGetActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * @var LoggerInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php index 30fc9a772cafd..0e91d85ed6a3a 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php @@ -31,7 +31,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGallery::delete_assets'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::delete_assets'; /** * @var DeleteImage diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php index d959a070148ed..3929ad8268f4e 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php @@ -29,7 +29,7 @@ class Details extends Action implements HttpGetActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * @var GetDetailsByAssetId diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php index f41c489607b15..c4d2d3f07bed2 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php @@ -32,7 +32,7 @@ class SaveDetails extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * @var UpdateAsset diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php index 11df39bd2b1dc..902a253a78461 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php @@ -28,7 +28,7 @@ class Upload extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGallery::upload_assets'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::upload_assets'; /** * @var UploadImage diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php index e97d93d86bb0d..4e0544a2870b9 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php @@ -18,7 +18,7 @@ */ class Index extends Action implements HttpGetActionInterface { - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * @var LayoutFactory diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php index 8c5b3d4d3a9ac..6b297a618e517 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php @@ -21,7 +21,7 @@ */ class Index extends Action implements HttpGetActionInterface { - public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; /** * @var Config diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php index b05bf116e61c6..1d71fa9892275 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php @@ -13,7 +13,7 @@ */ class CreateFolder implements ButtonProviderInterface { - private const ACL_CREATE_FOLDER = 'Magento_MediaGallery::create_folder'; + private const ACL_CREATE_FOLDER = 'Magento_MediaGalleryApi::create_folder'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php index fd5564c44ff89..a2078513c0730 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php @@ -13,7 +13,7 @@ */ class DeleteAssets implements ButtonProviderInterface { - private const ACL_DELETE_ASSETS= 'Magento_MediaGallery::delete_assets'; + private const ACL_DELETE_ASSETS= 'Magento_MediaGalleryApi::delete_assets'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php index 2fa14f19b197d..3acafb2ae9c27 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php @@ -13,7 +13,7 @@ */ class DeleteFolder implements ButtonProviderInterface { - private const ACL_DELETE_FOLDER = 'Magento_MediaGallery::delete_folder'; + private const ACL_DELETE_FOLDER = 'Magento_MediaGalleryApi::delete_folder'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php index 4bd901da823fd..ee04e7e8a5837 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php @@ -13,7 +13,7 @@ */ class InsertAsstes implements ButtonProviderInterface { - private const ACL_INSERT_ASSETS = 'Magento_MediaGallery::insert_assets'; + private const ACL_INSERT_ASSETS = 'Magento_MediaGalleryApi::insert_assets'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php index b74e3ebebe5cc..440695a367305 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php @@ -13,7 +13,7 @@ */ class UploadAssets implements ButtonProviderInterface { - private const ACL_UPLOAD_ASSETS= 'Magento_MediaGallery::upload_assets'; + private const ACL_UPLOAD_ASSETS= 'Magento_MediaGalleryApi::upload_assets'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php index b4c791822c59c..d42af8ef609ab 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php @@ -18,7 +18,7 @@ class DirectoryTree extends Container { private const ACL_IMAGE_ACTIONS = [ - 'delete_folder' => 'Magento_MediaGallery::delete_folder' + 'delete_folder' => 'Magento_MediaGalleryApi::delete_folder' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php index 7a6f59a75f1cf..ffbd726cd5f47 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php @@ -23,8 +23,8 @@ class Url extends Column { private const ACL_IMAGE_ACTIONS = [ - 'insert_assets' => 'Magento_MediaGallery::insert_assets', - 'delete_assets' => 'Magento_MediaGallery::delete_assets' + 'insert_assets' => 'Magento_MediaGalleryApi::insert_assets', + 'delete_assets' => 'Magento_MediaGalleryApi::delete_assets' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php index c16d90116bca0..e17048462c684 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php @@ -17,7 +17,7 @@ class Massaction extends Container { private const ACL_IMAGE_ACTIONS = [ - 'delete_assets' => 'Magento_MediaGallery::delete_assets' + 'delete_assets' => 'Magento_MediaGalleryApi::delete_assets' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml b/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml index 92839aa75ac8b..afa73373bd407 100644 --- a/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml +++ b/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> - <add id="Magento_MediaGalleryUi::media" title="Media" translate="title" module="Magento_MediaGalleryUi" sortOrder="15" parent="Magento_Backend::content" resource="Magento_Cms::media_gallery" dependsOnConfig="system/media_gallery/enabled"/> - <add id="Magento_MediaGalleryUi::media_gallery" title="Media Gallery" translate="title" module="Magento_MediaGalleryUi" sortOrder="0" parent="Magento_MediaGalleryUi::media" action="media_gallery/media/index" resource="Magento_Cms::media_gallery"/> + <add id="Magento_MediaGalleryUi::media" title="Media" translate="title" module="Magento_MediaGalleryUi" sortOrder="15" parent="Magento_Backend::content" resource="Magento_MediaGalleryApi::media_gallery" dependsOnConfig="system/media_gallery/enabled"/> + <add id="Magento_MediaGalleryUi::media_gallery" title="Media Gallery" translate="title" module="Magento_MediaGalleryUi" sortOrder="0" parent="Magento_MediaGalleryUi::media" action="media_gallery/media/index" resource="Magento_MediaGalleryApi::media_gallery"/> </menu> </config> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml index f41c0f91b2249..fa03c477ec9a2 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml @@ -11,7 +11,7 @@ <block name="media.gallery.container" class="Magento\Backend\Block\Template" template="Magento_MediaGalleryUi::container.phtml" - aclResource="Magento_Cms::media_gallery"> + aclResource="Magento_MediaGalleryApi::media_gallery"> <container name="gallery.actions" htmlTag="div" htmlClass="page-main-actions"> <block name="page.actions.toolbar" template="Magento_Backend::pageactions.phtml"/> </container> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml index b7307f9a74fae..51c1dc21b016e 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -40,7 +40,7 @@ </storageConfig> <updateUrl path="mui/index/render"/> </settings> - <aclResource>Magento_Cms::media_gallery</aclResource> + <aclResource>Magento_MediaGalleryApi::media_gallery</aclResource> <dataProvider class="Magento\MediaGalleryUi\Model\Listing\DataProvider" name="media_gallery_listing_data_source"> <settings> <requestFieldName>id</requestFieldName> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml index a53a46c61f75d..c1086ad891495 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml @@ -33,7 +33,7 @@ </storageConfig> <updateUrl path="mui/index/render"/> </settings> - <aclResource>Magento_Cms::media_gallery</aclResource> + <aclResource>Magento_MediaGalleryApi::media_gallery</aclResource> <dataProvider class="Magento\MediaGalleryUi\Model\Listing\DataProvider" name="media_gallery_listing_data_source"> <settings> <requestFieldName>id</requestFieldName> From 216c3376c0d90bb5ea0410ae01e68882c1ef1be9 Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Wed, 2 Sep 2020 15:55:48 +0300 Subject: [PATCH 104/195] MC-37315: Customer configuration: Persistent shopping cart --- .../Persistent/Block/Form/RememberTest.php | 108 ++++++++++++ .../Magento/Persistent/Helper/SessionTest.php | 80 +++++++++ .../Checkout/ConfigProviderPluginTest.php | 160 ++++++++++++++++++ .../Model/CheckoutConfigProviderTest.php | 84 +++++++++ .../Persistent/Model/QuoteManagerTest.php | 116 +++++++++++++ ...nchronizePersistentOnLoginObserverTest.php | 142 +++++++++++----- ...chronizePersistentOnLogoutObserverTest.php | 77 ++++++--- .../Persistent/_files/persistent_rollback.php | 11 ++ ...sistent_with_customer_quote_and_cookie.php | 19 +++ ...ith_customer_quote_and_cookie_rollback.php | 17 ++ 10 files changed, 740 insertions(+), 74 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php create mode 100644 dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php new file mode 100644 index 0000000000000..ca1f309c5cc9b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/Block/Form/RememberTest.php @@ -0,0 +1,108 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Block\Form; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Test for remember me checkbox on create customer account page + * + * @see \Magento\Persistent\Block\Form\Remember + * @magentoAppArea frontend + */ +class RememberTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Remember */ + private $block; + + /** + * @inheritdoc + */ + public function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Remember::class) + ->setTemplate('Magento_Persistent::remember_me.phtml'); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_default 0 + * + * @return void + */ + public function testRememberMeEnabled(): void + { + $this->assertFalse($this->block->isRememberMeChecked()); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + '//input[@name="persistent_remember_me"]/following-sibling::label/span[contains(text(), "%s")]', + __('Remember Me') + ), + $this->block->toHtml() + ), + 'Remember Me checkbox wasn\'t found.' + ); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_default 1 + * + * @return void + */ + public function testRememberMeAndRememberDefaultEnabled(): void + { + $this->assertTrue($this->block->isRememberMeChecked()); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath( + sprintf( + '//input[@name="persistent_remember_me"]/following-sibling::label/span[contains(text(), "%s")]', + __('Remember Me') + ), + $this->block->toHtml() + ), + 'Remember Me checkbox wasn\'t found or not checked by default.' + ); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 0 + * + * @return void + */ + public function testPersistentDisabled(): void + { + $this->assertEmpty($this->block->toHtml()); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_enabled 0 + * + * @return void + */ + public function testRememberMeDisabled(): void + { + $this->assertEmpty($this->block->toHtml()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php new file mode 100644 index 0000000000000..16ce015d89ecd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/Helper/SessionTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Helper; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Persistent\Model\SessionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for persistent session helper + * + * @see \Magento\Persistent\Helper\Session + * @magentoDbIsolation enabled + * @magentoAppArea frontend + */ +class SessionTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $helper; + + /** @var SessionFactory */ + private $sessionFactory; + + /** + * @inheritdoc + */ + public function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->helper = $this->objectManager->get(Session::class); + $this->sessionFactory = $this->objectManager->get(SessionFactory::class); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * + * @return void + */ + public function testPersistentEnabled(): void + { + $this->helper->setSession($this->sessionFactory->create()->loadByCustomerId(1)); + $this->assertTrue($this->helper->isPersistent()); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent.php + * @magentoConfigFixture current_store persistent/options/enabled 0 + * + * @return void + */ + public function testPersistentDisabled(): void + { + $this->helper->setSession($this->sessionFactory->create()->loadByCustomerId(1)); + $this->assertFalse($this->helper->isPersistent()); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * + * @return void + */ + public function testCustomerWithoutPersistent(): void + { + $this->helper->setSession($this->sessionFactory->create()->loadByCustomerId(1)); + $this->assertFalse($this->helper->isPersistent()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php new file mode 100644 index 0000000000000..803e1502e3ad9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/Model/Checkout/ConfigProviderPluginTest.php @@ -0,0 +1,160 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Model\Checkout; + +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Checkout\Model\DefaultConfigProvider; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\ObjectManagerInterface; +use Magento\Persistent\Helper\Session as PersistentSessionHelper; +use Magento\Persistent\Model\Session as PersistentSession; +use Magento\Persistent\Model\SessionFactory as PersistentSessionFactory; +use Magento\Quote\Model\QuoteIdMask; +use Magento\Quote\Model\QuoteIdMaskFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Interception\PluginList; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use PHPUnit\Framework\TestCase; + +/** + * Test for checkout config provider plugin + * + * @see \Magento\Persistent\Model\Checkout\ConfigProviderPlugin + * @magentoAppArea frontend + * @magentoDbIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class ConfigProviderPluginTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var DefaultConfigProvider */ + private $configProvider; + + /** @var CustomerSession */ + private $customerSession; + + /** @var CheckoutSession */ + private $checkoutSession; + + /** @var QuoteIdMask */ + private $quoteIdMask; + + /** @var PersistentSessionHelper */ + private $persistentSessionHelper; + + /** @var PersistentSession */ + private $persistentSession; + + /** @var GetQuoteByReservedOrderId */ + private $getQuoteByReservedOrderId; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->configProvider = $this->objectManager->get(DefaultConfigProvider::class); + $this->customerSession = $this->objectManager->get(CustomerSession::class); + $this->checkoutSession = $this->objectManager->get(CheckoutSession::class); + $this->quoteIdMask = $this->objectManager->get(QuoteIdMaskFactory::class)->create(); + $this->persistentSessionHelper = $this->objectManager->get(PersistentSessionHelper::class); + $this->persistentSession = $this->objectManager->get(PersistentSessionFactory::class)->create(); + $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->customerSession->setCustomerId(null); + $this->checkoutSession->clearQuote(); + $this->checkoutSession->setCustomerData(null); + $this->persistentSessionHelper->setSession(null); + + parent::tearDown(); + } + + /** + * @return void + */ + public function testPluginIsRegistered(): void + { + $pluginInfo = $this->objectManager->get(PluginList::class)->get(DefaultConfigProvider::class); + $this->assertSame(ConfigProviderPlugin::class, $pluginInfo['mask_quote_id_substitutor']['instance']); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * + * @return void + */ + public function testWithNotLoggedCustomer(): void + { + $session = $this->persistentSession->loadByCustomerId(1); + $this->persistentSessionHelper->setSession($session); + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->checkoutSession->setQuoteId($quote->getId()); + $result = $this->configProvider->getConfig(); + $this->assertEquals( + $this->quoteIdMask->load($quote->getId(), 'quote_id')->getMaskedId(), + $result['quoteData']['entity_id'] + ); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * + * @return void + */ + public function testWithLoggedCustomer(): void + { + $this->customerSession->setCustomerId(1); + $session = $this->persistentSession->loadByCustomerId(1); + $this->persistentSessionHelper->setSession($session); + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->checkoutSession->setQuoteId($quote->getId()); + $result = $this->configProvider->getConfig(); + $this->assertEquals($quote->getId(), $result['quoteData']['entity_id']); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * @magentoConfigFixture current_store persistent/options/enabled 0 + * + * @return void + */ + public function testPersistentDisabled(): void + { + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->checkoutSession->setQuoteId($quote->getId()); + $result = $this->configProvider->getConfig(); + $this->assertNull($result['quoteData']['entity_id']); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_customer_without_address.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * + * @return void + */ + public function testWithoutPersistentSession(): void + { + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->checkoutSession->setQuoteId($quote->getId()); + $result = $this->configProvider->getConfig(); + $this->assertNull($result['quoteData']['entity_id']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php new file mode 100644 index 0000000000000..176224bad7a1f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/Model/CheckoutConfigProviderTest.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Model; + +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for remember me checkbox on create customer account page. + * + * @see \Magento\Persistent\Model\CheckoutConfigProvider + */ +class CheckoutConfigProviderTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var CheckoutConfigProvider */ + private $model; + + /** + * @inheritdoc + */ + public function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->model = $this->objectManager->get(CheckoutConfigProvider::class); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_default 1 + * + * @return void + */ + public function testRememberMeEnabled(): void + { + $expectedConfig = [ + 'persistenceConfig' => ['isRememberMeCheckboxVisible' => true, 'isRememberMeCheckboxChecked' => true], + ]; + $config = $this->model->getConfig(); + $this->assertEquals($expectedConfig, $config); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/remember_enabled 0 + * @magentoConfigFixture current_store persistent/options/remember_default 0 + * + * @return void + */ + public function testRememberMeDisabled(): void + { + $expectedConfig = [ + 'persistenceConfig' => ['isRememberMeCheckboxVisible' => false, 'isRememberMeCheckboxChecked' => false], + ]; + $config = $this->model->getConfig(); + $this->assertEquals($expectedConfig, $config); + } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 0 + * @magentoConfigFixture current_store persistent/options/remember_default 0 + * + * @return void + */ + public function testPersistentDisabled(): void + { + $expectedConfig = [ + 'persistenceConfig' => ['isRememberMeCheckboxVisible' => false, 'isRememberMeCheckboxChecked' => false], + ]; + $config = $this->model->getConfig(); + $this->assertEquals($expectedConfig, $config); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php new file mode 100644 index 0000000000000..e11d47af3e814 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/Model/QuoteManagerTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Persistent\Model; + +use Magento\Customer\Api\Data\GroupInterface; +use Magento\Checkout\Model\Session as CheckoutSession; +use Magento\Framework\ObjectManagerInterface; +use Magento\Persistent\Helper\Session as PersistentSessionHelper; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Api\Data\CartInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; +use PHPUnit\Framework\TestCase; + +/** + * Test for persistent quote manager model + * + * @see \Magento\Persistent\Model\QuoteManager + * @magentoDbIsolation enabled + */ +class QuoteManagerTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var QuoteManager */ + private $model; + + /** @var CheckoutSession */ + private $checkoutSession; + + /** @var GetQuoteByReservedOrderId */ + private $getQuoteByReservedOrderId; + + /** @var PersistentSessionHelper */ + private $persistentSessionHelper; + + /** @var CartInterface */ + private $quote; + + /** @var CartRepositoryInterface */ + private $quoteRepository; + + /** + * @inheritdoc + */ + public function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->model = $this->objectManager->get(QuoteManager::class); + $this->checkoutSession = $this->objectManager->get(CheckoutSession::class); + $this->getQuoteByReservedOrderId = $this->objectManager->get(GetQuoteByReservedOrderId::class); + $this->persistentSessionHelper = $this->objectManager->get(PersistentSessionHelper::class); + $this->quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->checkoutSession->clearQuote(); + $this->checkoutSession->setCustomerData(null); + if ($this->quote instanceof CartInterface) { + $this->quoteRepository->delete($this->quote); + } + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/shopping_cart 1 + * + * @return void + */ + public function testPersistentShoppingCartEnabled(): void + { + $customerQuote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->checkoutSession->setQuoteId($customerQuote->getId()); + $this->model->setGuest(true); + $this->quote = $this->checkoutSession->getQuote(); + $this->assertNotEquals($customerQuote->getId(), $this->quote->getId()); + $this->assertFalse($this->model->isPersistent()); + $this->assertNull($this->quote->getCustomerId()); + $this->assertNull($this->quote->getCustomerEmail()); + $this->assertNull($this->quote->getCustomerFirstname()); + $this->assertNull($this->quote->getCustomerLastname()); + $this->assertEquals(GroupInterface::NOT_LOGGED_IN_ID, $this->quote->getCustomerGroupId()); + $this->assertEmpty($this->quote->getIsPersistent()); + $this->assertNull($this->persistentSessionHelper->getSession()->getId()); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/shopping_cart 0 + * + * @return void + */ + public function testPersistentShoppingCartDisabled(): void + { + $quote = $this->getQuoteByReservedOrderId->execute('test_order_with_customer_without_address'); + $this->checkoutSession->setQuoteId($quote->getId()); + $this->model->setGuest(true); + $this->assertNull($this->checkoutSession->getQuote()->getId()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php index 35f2283494b1c..f3847b7cb5735 100644 --- a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php @@ -10,16 +10,21 @@ use DateTime; use DateTimeZone; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Customer\Api\Data\CustomerInterface; -use Magento\Framework\Event; -use Magento\Framework\Event\Observer; +use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Persistent\Helper\Session as PersistentSessionHelper; use Magento\Persistent\Model\Session; use Magento\Persistent\Model\SessionFactory; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; /** + * Test for synchronize persistent session on login observer + * + * @see \Magento\Persistent\Observer\SynchronizePersistentOnLoginObserver + * @magentoAppArea frontend + * @magentoDbIsolation enabled * @magentoDataFixture Magento/Customer/_files/customer.php * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -28,87 +33,130 @@ class SynchronizePersistentOnLoginObserverTest extends TestCase /** * @var SynchronizePersistentOnLoginObserver */ - protected $_model; + private $model; /** * @var ObjectManagerInterface */ - protected $_objectManager; + private $objectManager; /** - * @var \Magento\Persistent\Helper\Session + * @var PersistentSessionHelper */ - protected $_persistentSession; + private $persistentSessionHelper; /** - * @var \Magento\Customer\Model\Session + * @var CustomerRepositoryInterface */ - protected $_customerSession; + private $customerRepository; /** - * @var CustomerInterface + * @var SessionFactory */ - private $customer; + private $persistentSessionFactory; + + + /** + * @var CookieManagerInterface + */ + private $cookieManager; + + /** + * @var CustomerSession + */ + private $customerSession; /** * @inheritDoc */ protected function setUp(): void { - $this->_objectManager = Bootstrap::getObjectManager(); - $this->_persistentSession = $this->_objectManager->get(\Magento\Persistent\Helper\Session::class); - $this->_customerSession = $this->_objectManager->get(\Magento\Customer\Model\Session::class); - $this->_model = $this->_objectManager->create( - SynchronizePersistentOnLoginObserver::class, - [ - 'persistentSession' => $this->_persistentSession, - 'customerSession' => $this->_customerSession - ] - ); - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = $this->_objectManager->create(CustomerRepositoryInterface::class); - $this->customer = $customerRepository->getById(1); + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->persistentSessionHelper = $this->objectManager->get(PersistentSessionHelper::class); + $this->model = $this->objectManager->get(SynchronizePersistentOnLoginObserver::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + $this->persistentSessionFactory = $this->objectManager->get(SessionFactory::class); + $this->cookieManager = $this->objectManager->get(CookieManagerInterface::class); + $this->customerSession = $this->objectManager->get(CustomerSession::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->persistentSessionHelper->setRememberMeChecked(null); + $this->customerSession->logout(); + + parent::tearDown(); } /** * Test that persistent session is created on customer login + * + * @return void */ public function testSynchronizePersistentOnLogin(): void { - $sessionModel = $this->_objectManager->create(Session::class); - $sessionModel->loadByCustomerId($this->customer->getId()); + $customer = $this->customerRepository->get('customer@example.com'); + $sessionModel = $this->persistentSessionFactory->create(); + $sessionModel->loadByCustomerId($customer->getId()); $this->assertNull($sessionModel->getCustomerId()); - $event = new Event(); - $observer = new Observer(['event' => $event]); - $event->setData('customer', $this->customer); - $this->_persistentSession->setRememberMeChecked(true); - $this->_model->execute($observer); - // check that persistent session has been stored for Customer - /** @var Session $sessionModel */ - $sessionModel = $this->_objectManager->create(Session::class); - $sessionModel->loadByCustomerId($this->customer->getId()); - $this->assertEquals($this->customer->getId(), $sessionModel->getCustomerId()); + $this->persistentSessionHelper->setRememberMeChecked(true); + $this->customerSession->loginById($customer->getId()); + $sessionModel = $this->persistentSessionFactory->create(); + $sessionModel->loadByCustomerId($customer->getId()); + $this->assertEquals($customer->getId(), $sessionModel->getCustomerId()); } /** * Test that expired persistent session is renewed on customer login + * + * @return void */ public function testExpiredPersistentSessionShouldBeRenewedOnLogin(): void { + $customer = $this->customerRepository->get('customer@example.com'); $lastUpdatedAt = (new DateTime('-1day'))->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i:s'); - /** @var Session $sessionModel */ - $sessionModel = $this->_objectManager->create(SessionFactory::class)->create(); - $sessionModel->setCustomerId($this->customer->getId()); + $sessionModel = $this->persistentSessionFactory->create(); + $sessionModel->setCustomerId($customer->getId()); $sessionModel->setUpdatedAt($lastUpdatedAt); $sessionModel->save(); - $event = new Event(); - $observer = new Observer(['event' => $event]); - $event->setData('customer', $this->customer); - $this->_persistentSession->setRememberMeChecked(true); - $this->_model->execute($observer); - /** @var Session $sessionModel */ - $sessionModel = $this->_objectManager->create(Session::class); - $sessionModel->loadByCustomerId(1); + $this->persistentSessionHelper->setRememberMeChecked(true); + $this->customerSession->loginById($customer->getId()); + $sessionModel = $this->persistentSessionFactory->create(); + $sessionModel->loadByCustomerId($customer->getId()); $this->assertGreaterThan($lastUpdatedAt, $sessionModel->getUpdatedAt()); } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php + * @magentoConfigFixture current_store persistent/options/enabled 0 + * + * @return void + */ + public function testDisabledPersistentSession(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $this->customerSession->loginById($customer->getId()); + $this->assertNull($this->cookieManager->getCookie(Session::COOKIE_NAME)); + } + + /** + * @magentoDataFixture Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/lifetime 0 + * + * @return void + */ + public function testDisabledPersistentSessionLifetime(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $this->customerSession->loginById($customer->getId()); + $session = $this->persistentSessionFactory->create()->setLoadExpired()->loadByCustomerId($customer->getId()); + $this->assertNull($session->getId()); + $this->assertNull($this->cookieManager->getCookie(Session::COOKIE_NAME)); + } } diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php index 2bf97fdb4953f..293f1d1890d92 100644 --- a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLogoutObserverTest.php @@ -3,54 +3,77 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Persistent\Observer; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\ObjectManagerInterface; +use Magento\Persistent\Model\SessionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + /** + * Test for synchronize persistent on logout observer + * + * @see \Magento\Persistent\Observer\SynchronizePersistentOnLogoutObserver * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoAppArea frontend + * @magentoDbIsolation enabled */ -class SynchronizePersistentOnLogoutObserverTest extends \PHPUnit\Framework\TestCase +class SynchronizePersistentOnLogoutObserverTest extends TestCase { - /** - * @var \Magento\Framework\ObjectManagerInterface - */ - protected $_objectManager; + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var CustomerSession */ + private $customerSession; + + /** @var SessionFactory */ + private $sessionFactory; /** - * @var \Magento\Customer\Model\Session + * @inheritdoc */ - protected $_customerSession; - protected function setUp(): void { - $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->_customerSession = $this->_objectManager->get(\Magento\Customer\Model\Session::class); + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(CustomerSession::class); + $this->sessionFactory = $this->objectManager->get(SessionFactory::class); } /** * @magentoConfigFixture current_store persistent/options/enabled 1 * @magentoConfigFixture current_store persistent/options/logout_clear 1 - * @magentoAppArea frontend - * @magentoAppIsolation enabled + * + * @return void */ - public function testSynchronizePersistentOnLogout() + public function testSynchronizePersistentOnLogout(): void { - $this->_customerSession->loginById(1); - - // check that persistent session has been stored for Customer - /** @var \Magento\Persistent\Model\Session $sessionModel */ - $sessionModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Persistent\Model\Session::class - ); + $this->customerSession->loginById(1); + $sessionModel = $this->sessionFactory->create(); $sessionModel->loadByCookieKey(); $this->assertEquals(1, $sessionModel->getCustomerId()); - - $this->_customerSession->logout(); - - /** @var \Magento\Persistent\Model\Session $sessionModel */ - $sessionModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Persistent\Model\Session::class - ); + $this->customerSession->logout(); + $sessionModel = $this->sessionFactory->create(); $sessionModel->loadByCookieKey(); $this->assertNull($sessionModel->getCustomerId()); } + + /** + * @magentoConfigFixture current_store persistent/options/enabled 1 + * @magentoConfigFixture current_store persistent/options/logout_clear 0 + * + * @return void + */ + public function testSynchronizePersistentOnLogoutDisabled(): void + { + $this->customerSession->loginById(1); + $this->customerSession->logout(); + $sessionModel = $this->sessionFactory->create(); + $sessionModel->loadByCookieKey(); + $this->assertEquals(1, $sessionModel->getCustomerId()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php new file mode 100644 index 0000000000000..581ddb35e3678 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_rollback.php @@ -0,0 +1,11 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_address_rollback.php'); +Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php'); diff --git a/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php new file mode 100644 index 0000000000000..a2c68ad9b7f2a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie.php @@ -0,0 +1,19 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Persistent\Model\SessionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_customer_without_address.php'); + +$objectManager = Bootstrap::getObjectManager(); +/** @var SessionFactory $persistentSessionFactory */ +$persistentSessionFactory = $objectManager->get(SessionFactory::class); +$session = $persistentSessionFactory->create(); +$session->setCustomerId(1)->save(); +$session->setPersistentCookie(10000, ''); diff --git a/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php new file mode 100644 index 0000000000000..252b3f4be7079 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Persistent/_files/persistent_with_customer_quote_and_cookie_rollback.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Persistent\Model\SessionFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +$objectManager = Bootstrap::getObjectManager(); +/** @var SessionFactory $sessionFactory */ +$sessionFactory = $objectManager->get(SessionFactory::class); +$sessionFactory->create()->deleteByCustomerId(1); + +Resolver::getInstance()->requireDataFixture('Magento/Checkout/_files/quote_with_customer_without_address_rollback.php'); From 9fd0330017d9ebf5b90229b10d4769284ad415bf Mon Sep 17 00:00:00 2001 From: IvanPletnyov <ivan.pletnyov@transoftgroup.com> Date: Wed, 2 Sep 2020 22:03:39 +0300 Subject: [PATCH 105/195] MC-37315: Customer configuration: Persistent shopping cart --- .../Observer/SynchronizePersistentOnLoginObserverTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php index f3847b7cb5735..bd4d24211f1e3 100644 --- a/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Persistent/Observer/SynchronizePersistentOnLoginObserverTest.php @@ -55,7 +55,6 @@ class SynchronizePersistentOnLoginObserverTest extends TestCase */ private $persistentSessionFactory; - /** * @var CookieManagerInterface */ From b4d73e31d47067fe11bdfc8fa0dca84250849e83 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Thu, 3 Sep 2020 07:02:55 +0800 Subject: [PATCH 106/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated PR with requested changes --- .../Model/Consume.php | 28 ++++++++++--------- .../Test/Integration/Model/PublisherTest.php | 19 ++++++------- ...sCatalog.php => SynchronizeIdentities.php} | 2 +- .../SynchronizeIdentitiesCatalogTest.php | 17 +++++------ .../etc/di.xml | 4 +-- ...itiesCms.php => SynchronizeIdentities.php} | 2 +- .../SynchronizeIdentitiesCmsTest.php | 17 +++++------ .../MediaContentSynchronizationCms/etc/di.xml | 4 +-- 8 files changed, 48 insertions(+), 45 deletions(-) rename app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/{SynchronizeIdentitiesCatalog.php => SynchronizeIdentities.php} (95%) rename app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/{SynchronizeIdentitiesCms.php => SynchronizeIdentities.php} (97%) diff --git a/app/code/Magento/MediaContentSynchronization/Model/Consume.php b/app/code/Magento/MediaContentSynchronization/Model/Consume.php index 6aa8e6e831a25..b01c02cae4234 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/Consume.php +++ b/app/code/Magento/MediaContentSynchronization/Model/Consume.php @@ -70,20 +70,22 @@ public function __construct( public function execute(OperationInterface $operation) : void { $identities = $this->serializer->unserialize($operation->getSerializedData()); - if (!empty($identities)) { - $contentIdentity = []; - foreach ($identities as $identity) { - $contentIdentity[] = $this->contentIdentityFactory->create( - [ - self::ENTITY_TYPE => $identity[self::ENTITY_TYPE], - self::ENTITY_ID => $identity[self::ENTITY_ID], - self::FIELD => $identity[self::FIELD] - ] - ); - } - $this->synchronizeIdentities->execute($contentIdentity); - } else { + + if (empty($identities)) { $this->synchronize->execute(); + return; + } + + $contentIdentities = []; + foreach ($identities as $identity) { + $contentIdentities[] = $this->contentIdentityFactory->create( + [ + self::ENTITY_TYPE => $identity[self::ENTITY_TYPE], + self::ENTITY_ID => $identity[self::ENTITY_ID], + self::FIELD => $identity[self::FIELD] + ] + ); } + $this->synchronizeIdentities->execute($contentIdentities); } } diff --git a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php index 595c6d895bed0..2314796481b55 100644 --- a/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php +++ b/app/code/Magento/MediaContentSynchronization/Test/Integration/Model/PublisherTest.php @@ -81,20 +81,19 @@ public function testExecute(array $contentIdentities): void $consumer->process($maxNumberOfMessages); // verify synchronized media content + $assetId = 2020; + $entityIds = []; foreach ($contentIdentities as $contentIdentity) { - $assetId = 2020; $contentIdentityObject = $this->contentIdentityFactory->create($contentIdentity); $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentityObject)); - $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); - $this->assertEquals(2, count($synchronizedContentIdentities)); + $entityIds[] = $contentIdentityObject->getEntityId(); + } + + $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); + $this->assertEquals(2, count($synchronizedContentIdentities)); - $syncedIds = []; - foreach ($synchronizedContentIdentities as $syncedContentIdentity) { - $syncedIds[] = $syncedContentIdentity->getEntityId(); - } - foreach ($contentIdentityObject as $identityObject) { - $this->assertContains($identityObject->getEntityId(), $syncedIds); - } + foreach ($synchronizedContentIdentities as $syncedContentIdentity) { + $this->assertContains($syncedContentIdentity->getEntityId(), $entityIds); } } diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php b/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentities.php similarity index 95% rename from app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php rename to app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentities.php index a57b8eb2041cb..77188b65a8b88 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentitiesCatalog.php +++ b/app/code/Magento/MediaContentSynchronizationCatalog/Model/Synchronizer/SynchronizeIdentities.php @@ -11,7 +11,7 @@ use Magento\MediaContentApi\Model\GetEntityContentsInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -class SynchronizeIdentitiesCatalog implements SynchronizeIdentitiesInterface +class SynchronizeIdentities implements SynchronizeIdentitiesInterface { private const FIELD_CATALOG_PRODUCT = 'catalog_product'; private const FIELD_CATALOG_CATEGORY = 'catalog_category'; diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php b/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php index ae4bb77881834..4a1ba1445ffe5 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php +++ b/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php @@ -84,17 +84,18 @@ public function testExecute(array $mediaContentIdentities): void $this->assertNotEmpty($contentIdentities); $this->synchronizeIdentities->execute($contentIdentities); + $assetId = 2020; + $entityIds = []; foreach ($contentIdentities as $contentIdentity) { - $assetId = 2020; $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); - $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); - $this->assertEquals(2, count($synchronizedContentIdentities)); + $entityIds[] = $contentIdentity->getEntityId(); + } + + $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); + $this->assertEquals(2, count($synchronizedContentIdentities)); - $syncedIds = []; - foreach ($synchronizedContentIdentities as $syncedContentIdentity) { - $syncedIds[] = $syncedContentIdentity->getEntityId(); - } - $this->assertContains($contentIdentity->getEntityId(), $syncedIds); + foreach ($synchronizedContentIdentities as $syncedContentIdentity) { + $this->assertContains($syncedContentIdentity->getEntityId(), $entityIds); } } diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml b/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml index 7c391faf3f955..d26ed06a42586 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml @@ -41,8 +41,8 @@ <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool"> <arguments> <argument name="synchronizers" xsi:type="array"> - <item name="media_content_catalog_synchronizeIdentity" - xsi:type="object">Magento\MediaContentSynchronizationCatalog\Model\Synchronizer\SynchronizeIdentitiesCatalog + <item name="media_content_catalog" + xsi:type="object">Magento\MediaContentSynchronizationCatalog\Model\Synchronizer\SynchronizeIdentities </item> </argument> </arguments> diff --git a/app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php b/app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentities.php similarity index 97% rename from app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php rename to app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentities.php index 5685e284e20c3..7dd2596a910de 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentitiesCms.php +++ b/app/code/Magento/MediaContentSynchronizationCms/Model/Synchronizer/SynchronizeIdentities.php @@ -12,7 +12,7 @@ use Magento\MediaContentApi\Model\GetEntityContentsInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -class SynchronizeIdentitiesCms implements SynchronizeIdentitiesInterface +class SynchronizeIdentities implements SynchronizeIdentitiesInterface { private const FIELD_CMS_PAGE = 'cms_page'; private const FIELD_CMS_BLOCK = 'cms_block'; diff --git a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php index 48841ecf24658..bdd8bfd1105d3 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php +++ b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php @@ -84,17 +84,18 @@ public function testExecute(array $mediaContentIdentities): void $this->assertNotEmpty($contentIdentities); $this->synchronizeIdentities->execute($contentIdentities); + $assetId = 2020; + $entityIds = []; foreach ($contentIdentities as $contentIdentity) { - $assetId = 2020; $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); - $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); - $this->assertEquals(2, count($synchronizedContentIdentities)); + $entityIds[] = $contentIdentity->getEntityId(); + } + + $synchronizedContentIdentities = $this->getContentIdentities->execute([$assetId]); + $this->assertEquals(2, count($synchronizedContentIdentities)); - $syncedIds = []; - foreach ($synchronizedContentIdentities as $syncedContentIdentity) { - $syncedIds[] = $syncedContentIdentity->getEntityId(); - } - $this->assertContains($contentIdentity->getEntityId(), $syncedIds); + foreach ($synchronizedContentIdentities as $syncedContentIdentity) { + $this->assertContains($syncedContentIdentity->getEntityId(), $entityIds); } } diff --git a/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml b/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml index 43a863dc95764..94e9c686aafbb 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml @@ -17,8 +17,8 @@ <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool"> <arguments> <argument name="synchronizers" xsi:type="array"> - <item name="media_content_cms_synchronizeIdentity" - xsi:type="object">Magento\MediaContentSynchronizationCms\Model\Synchronizer\SynchronizeIdentitiesCms + <item name="media_content_cms" + xsi:type="object">Magento\MediaContentSynchronizationCms\Model\Synchronizer\SynchronizeIdentities </item> </argument> </arguments> From 87bbae122f3cb2fdcd20e6e1b325c864aaad2602 Mon Sep 17 00:00:00 2001 From: "rostyslav.hymon" <rostyslav.hymon@transoftgroup.com> Date: Thu, 3 Sep 2020 11:17:06 +0300 Subject: [PATCH 107/195] MC-36807: ElasticSearch 7 - Limit of total fields [XXXXX] in index has been exceeded --- .../Model/Adapter/Elasticsearch.php | 8 ++++- .../Unit/Model/Adapter/ElasticsearchTest.php | 31 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php index 25e691972d81d..261f8d84b5baa 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/Elasticsearch.php @@ -497,6 +497,12 @@ protected function prepareIndex($storeId, $indexName, $mappedIndexerId) */ private function getMappingTotalFieldsLimit(array $allAttributeTypes): int { - return count($allAttributeTypes) + self::MAPPING_TOTAL_FIELDS_BUFFER_LIMIT; + $count = count($allAttributeTypes); + foreach ($allAttributeTypes as $attributeType) { + if (isset($attributeType['fields'])) { + $count += count($attributeType['fields']); + } + } + return $count + self::MAPPING_TOTAL_FIELDS_BUFFER_LIMIT; } } diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php index 5abe800884ced..b070e3324ed78 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Model/Adapter/ElasticsearchTest.php @@ -174,7 +174,14 @@ protected function setUp(): void ->method('getAllAttributesTypes') ->willReturn( [ - 'name' => 'string', + 'name' => [ + 'type' => 'string', + 'fields' => [ + 'keyword' => [ + 'type' => "keyword", + ], + ], + ], ] ); $this->clientConfig->expects($this->any()) @@ -564,6 +571,28 @@ public function testUpdateIndexMappingWithAliasDefinition(): void $this->model->updateIndexMapping($storeId, $mappedIndexerId, $attributeCode); } + /** + * Test for get mapping total fields limit + * + * @return void + */ + public function testGetMappingTotalFieldsLimit(): void + { + $settings = [ + 'index' => [ + 'mapping' => [ + 'total_fields' => [ + 'limit' => 1002 + ] + ] + ] + ]; + $this->client->expects($this->at(1)) + ->method('createIndex') + ->with(null, ['settings' => $settings]); + $this->model->cleanIndex(1, 'product'); + } + /** * Get elasticsearch client options * From b4f2b131bbfe269650a2cef80fa4cc3ddd1922ae Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Thu, 3 Sep 2020 13:00:11 +0300 Subject: [PATCH 108/195] MC-33493: Test render fields in composite configure block for simple and configurable product --- .../Composite/Fieldset/OptionsTest.php | 618 ++++++++++++++++++ .../Product/Composite/Fieldset/QtyTest.php | 128 ++++ .../Product/Composite/FieldsetTest.php | 120 ++++ .../AbstractRenderCustomOptionsTest.php | 119 +++- .../View/Options/RenderOptionsTest.php | 20 +- .../Composite/Fieldset/ConfigurableTest.php | 101 +++ .../View/CustomOptions/RenderOptionsTest.php | 19 +- .../Product/View/Type/ConfigurableTest.php | 33 + 8 files changed, 1123 insertions(+), 35 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php create mode 100644 dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php new file mode 100644 index 0000000000000..ddf22614abaa4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php @@ -0,0 +1,618 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Adminhtml\Product\Composite\Fieldset; + +use Magento\Catalog\Api\Data\ProductCustomOptionInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Block\Product\View\Options\AbstractRenderCustomOptionsTest; +use Magento\Catalog\Helper\Product as HelperProduct; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Catalog\Model\Product\Option; +use Magento\Catalog\Model\Product\Option\Value; +use Magento\Framework\DataObject; +use Magento\TestFramework\Helper\Xpath; + +/** + * Test cases related to check that simple product custom option renders as expected. + * + * @magentoAppArea adminhtml + */ +class OptionsTest extends AbstractRenderCustomOptionsTest +{ + /** @var HelperProduct */ + private $helperProduct; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->helperProduct = $this->objectManager->get(HelperProduct::class); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @throws \Magento\Framework\Exception\NoSuchEntityException + * @return void + */ + public function testRenderCustomOptionsWithoutOptions(): void + { + $product = $this->productRepository->get('simple'); + $optionHtml = $this->getOptionHtml($product); + $this->assertEquals( + 0, + Xpath::getElementsCountForXpath( + "//fieldset[@id='product_composite_configure_fields_options']", + $optionHtml + ), + 'The option block is expected to be empty!' + ); + } + + /** + * Check that options from text group(field, area) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider renderCustomOptionsFromTextGroupProvider + * @param array $optionData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromTextGroup(array $optionData, array $checkArray): void + { + $this->assertTextOptionRenderingOnProduct('simple', $optionData, $checkArray); + }//test bez opcij + + /** + * Provides test data to verify the display of text type options. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function renderCustomOptionsFromTextGroupProvider(): array + { + return [ + 'type_text_required_field' => [ + [ + Option::KEY_TITLE => 'Test option type text 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 0, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'contains' => [ + 'block_with_required_class' => '<div class="field admin__field">', + 'title' => 'Test option type text 1', + ], + 'equals_xpath' => [ + 'zero_price' => [ + 'xpath' => "//label[contains(@class, 'admin__field-label')]/span", + 'message' => 'Expected empty price is incorrect or missing!', + 'expected' => 0, + ], + ], + ], + ], + 'type_text_is_required_option' => [ + [ + Option::KEY_TITLE => 'Test option type text 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 1, + Option::KEY_PRICE => 0, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'contains' => [ + 'block_with_required_class' => '<div class="field admin__field required _required">', + ], + ], + ], + 'type_text_fixed_positive_price' => [ + [ + Option::KEY_TITLE => 'Test option type text 3', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'contains' => [ + 'price' => 'data-price-amount="50"', + ], + 'equals_xpath' => [ + 'sign_price' => [ + 'xpath' => "//label[contains(@class, 'admin__field-label')]/span[contains(text(), '+')]", + 'message' => 'Expected positive price is incorrect or missing!', + ], + ], + ], + ], + 'type_text_fixed_negative_price' => [ + [ + Option::KEY_TITLE => 'Test option type text 4', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => -50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'contains' => [ + 'price' => 'data-price-amount="50"', + ], + 'equals_xpath' => [ + 'sign_price' => [ + 'xpath' => "//label[contains(@class, 'admin__field-label')]/span[contains(text(), '-')]", + 'message' => 'Expected negative price is incorrect or missing!', + ], + ], + ], + ], + 'type_text_percent_price' => [ + [ + Option::KEY_TITLE => 'Test option type text 5', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 50, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_PERCENT, + Option::KEY_MAX_CHARACTERS => 0, + ], + [ + 'contains' => [ + 'price' => 'data-price-amount="5"', + ], + ], + ], + 'type_text_max_characters' => [ + [ + Option::KEY_TITLE => 'Test option type text 6', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 99, + ], + [ + 'max_characters' => (string)__('Maximum number of characters:') . ' <strong>99</strong>', + ], + ], + 'type_field' => [ + [ + Option::KEY_TITLE => 'Test option type field 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_FIELD, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 0, + 'configure_option_value' => 'Type field option value', + ], + [ + 'equals_xpath' => [ + 'control_price_attribute' => [ + 'xpath' => "//input[@id='options_%s_text' and @price='%s']", + 'message' => 'Expected input price is incorrect or missing!', + ], + 'default_option_value' => [ + 'xpath' => "//input[@id='options_%s_text' and @value='Type field option value']", + 'message' => 'Expected input default value is incorrect or missing!', + ], + ], + ], + ], + 'type_area' => [ + [ + Option::KEY_TITLE => 'Test option type area 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_AREA, + Option::KEY_IS_REQUIRE => 0, + Option::KEY_PRICE => 10, + Option::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Option::KEY_MAX_CHARACTERS => 0, + 'configure_option_value' => 'Type area option value', + ], + [ + 'equals_xpath' => [ + 'control_price_attribute' => [ + 'xpath' => "//textarea[@id='options_%s_text' and @price='%s']", + 'message' => 'Expected textarea price is incorrect or missing!', + ], + 'default_option_value' => [ + 'xpath' => "//textarea[@id='options_%s_text' " + . "and contains(text(), 'Type area option value')]", + 'message' => 'Expected textarea default value is incorrect or missing!', + ], + ], + ], + ], + ]; + } + + /** + * Check that options from select group(drop-down, radio buttons, checkbox, multiple select) render as expected. + * + * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @dataProvider renderCustomOptionsFromSelectGroupProvider + * @param array $optionData + * @param array $optionValueData + * @param array $checkArray + * @return void + */ + public function testRenderCustomOptionsFromSelectGroup( + array $optionData, + array $optionValueData, + array $checkArray + ): void { + $this->assertSelectOptionRenderingOnProduct('simple', $optionData, $optionValueData, $checkArray); + } + + /** + * Provides test data to verify the display of select type options. + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @return array + */ + public function renderCustomOptionsFromSelectGroupProvider(): array + { + return [ + 'type_select_required_field' => [ + [ + Option::KEY_TITLE => 'Test option type select 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Select value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'contains' => [ + 'block_with_required_class' => '<div class="admin__field field">', + 'title' => '<span>Test option type select 1</span>', + ], + 'equals_xpath' => [ + 'required_element' => [ + 'xpath' => "//select[@id='select_%s']", + 'message' => 'Expected select type is incorrect or missing!', + ], + ], + ], + ], + 'type_select_is_required_option' => [ + [ + Option::KEY_TITLE => 'Test option type select 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Select value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'contains' => [ + 'block_with_required_class' => '<div class="admin__field field _required">', + ], + ], + ], + 'type_drop_down_with_selected' => [ + [ + Option::KEY_TITLE => 'Test option type drop-down 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + Option::KEY_IS_REQUIRE => 0, + 'configure_option_value' => 'Drop-down value 1', + ], + [ + Value::KEY_TITLE => 'Drop-down value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'equals_xpath' => [ + 'element_type' => [ + 'xpath' => "//select[contains(@class, 'admin__control-select')]", + 'message' => 'Expected drop down type is incorrect or missing!', + ], + 'default_value' => [ + 'xpath' => "//option[contains(text(), '" . __('-- Please Select --') . "')]", + 'message' => 'Expected default value is incorrect or missing!', + ], + 'selected_value' => [ + 'xpath' => "//option[@selected='selected' and contains(text(), 'Drop-down value 1')]", + 'message' => 'Expected selected value is incorrect or missing!', + ], + ], + ], + ], + 'type_multiple_with_selected' => [ + [ + Option::KEY_TITLE => 'Test option type multiple 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_MULTIPLE, + Option::KEY_IS_REQUIRE => 0, + 'configure_option_value' => 'Multiple value 1', + ], + [ + Value::KEY_TITLE => 'Multiple value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'equals_xpath' => [ + 'element_type' => [ + 'xpath' => "//select[contains(@class, 'admin__control-multiselect') " + . "and @multiple='multiple']", + 'message' => 'Expected multiple type is incorrect or missing!', + ], + 'selected_value' => [ + 'xpath' => "//option[@selected='selected' and contains(text(), 'Multiple value 1')]", + 'message' => 'Expected selected value is incorrect or missing!', + ], + ], + ], + ], + 'type_checkable_required_field' => [ + [ + Option::KEY_TITLE => 'Test option type checkable 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + ], + [ + Value::KEY_TITLE => 'Checkable value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'equals_xpath' => [ + 'required_checkable_option' => [ + 'xpath' => "//div[@id='options-%s-list']", + 'message' => 'Expected checkable option is incorrect or missing!', + ], + 'option_value_title' => [ + 'xpath' => "//label[@for='options_%s_2']/span[contains(text(), 'Checkable value 1')]", + 'message' => 'Expected option value title is incorrect or missing!', + ], + ], + ], + ], + 'type_radio_is_required_option' => [ + [ + Option::KEY_TITLE => 'Test option type radio 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Radio value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'equals_xpath' => [ + 'span_container' => [ + 'xpath' => "//span[@id='options-%s-container']", + 'message' => 'Expected span container is incorrect or missing!', + ], + 'default_option_value' => [ + 'xpath' => "//label[@for='options_%s']/span[contains(text(), '" . __('None') . "')]", + 'message' => 'Expected default option value is incorrect or missing!', + 'expected' => 0, + ], + ], + ], + ], + 'type_radio_with_selected' => [ + [ + Option::KEY_TITLE => 'Test option type radio 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_RADIO, + Option::KEY_IS_REQUIRE => 0, + 'configure_option_value' => 'Radio value 1', + ], + [ + Value::KEY_TITLE => 'Radio value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + ], + [ + 'equals_xpath' => [ + 'default_option_value' => [ + 'xpath' => "//label[@for='options_%s']/span[contains(text(), '" . __('None') . "')]", + 'message' => 'Expected default option value is incorrect or missing!', + ], + 'element_type' => [ + 'xpath' => "//input[@id='options_%s_2' and contains(@class, 'admin__control-radio')]", + 'message' => 'Expected radio type is incorrect or missing!', + ], + 'selected_value' => [ + 'xpath' => "//input[@id='options_%s_2' and @checked='checked']", + 'message' => 'Expected selected option value is incorrect or missing!', + ], + ], + ], + ], + 'type_checkbox_is_required_option' => [ + [ + Option::KEY_TITLE => 'Test option type checkbox 1', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 1, + ], + [ + Value::KEY_TITLE => 'Checkbox value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => '', + ], + [ + 'equals_xpath' => [ + 'span_container' => [ + 'xpath' => "//span[@id='options-%s-container']", + 'message' => 'Expected span container is incorrect or missing!', + ], + ], + ], + ], + 'type_checkbox_with_selected' => [ + [ + Option::KEY_TITLE => 'Test option type checkbox 2', + Option::KEY_TYPE => ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX, + Option::KEY_IS_REQUIRE => 0, + 'configure_option_value' => 'Checkbox value 1', + ], + [ + Value::KEY_TITLE => 'Checkbox value 1', + Value::KEY_PRICE => 10, + Value::KEY_PRICE_TYPE => ProductPriceOptionsInterface::VALUE_FIXED, + Value::KEY_SKU => '', + ], + [ + 'equals_xpath' => [ + 'element_type' => [ + 'xpath' => "//input[@id='options_%s_2' and contains(@class, 'admin__control-checkbox')]", + 'message' => 'Expected checkbox type is incorrect or missing!', + ], + 'selected_value' => [ + 'xpath' => "//input[@id='options_%s_2' and @checked='checked']", + 'message' => 'Expected selected option value is incorrect or missing!', + ], + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + protected function addOptionToProduct( + ProductInterface $product, + array $optionData, + array $optionValueData = [] + ): ProductInterface { + $product = parent::addOptionToProduct($product, $optionData, $optionValueData); + + if (isset($optionData['configure_option_value'])) { + $optionValue = $optionData['configure_option_value']; + $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); + if (!empty($optionValueData)) { + $optionValueObject = $this->findOptionValueByTitle($option, $optionValue); + $optionValue = $option->getType() === Option::OPTION_TYPE_CHECKBOX + ? [$optionValueObject->getOptionTypeId()] + : $optionValueObject->getOptionTypeId(); + } + /** @var DataObject $request */ + $buyRequest = $this->objectManager->create(DataObject::class); + $buyRequest->setData([ + 'qty' => 1, + 'options' => [$option->getId() => $optionValue], + ]); + $this->helperProduct->prepareProductOptions($product, $buyRequest); + } + + return $product; + } + + /** + * @inheritdoc + */ + protected function baseOptionAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + if (isset($checkArray['contains'])) { + foreach ($checkArray['contains'] as $needle) { + $this->assertStringContainsString($needle, $optionHtml); + } + } + } + + /** + * @inheritdoc + */ + protected function additionalTypeTextAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + parent::additionalTypeTextAsserts($option, $optionHtml, $checkArray); + + if (isset($checkArray['equals_xpath'])) { + foreach ($checkArray['equals_xpath'] as $key => $value) { + $value['args'] = $key == 'control_price_attribute' ? [(float)$option->getPrice()] : []; + $this->assertEqualsXpath($option, $optionHtml, $value); + } + } + } + + /** + * @inheritdoc + */ + protected function additionalTypeSelectAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + parent::additionalTypeSelectAsserts($option, $optionHtml, $checkArray); + + if (isset($checkArray['equals_xpath'])) { + foreach ($checkArray['equals_xpath'] as $value) { + $this->assertEqualsXpath($option, $optionHtml, $value); + } + } + } + + /** + * @inheritdoc + */ + protected function getHandlesList(ProductInterface $product): array + { + return [ + 'default', + 'CATALOG_PRODUCT_COMPOSITE_CONFIGURE', + 'catalog_product_view_type_' . $product->getTypeId(), + ]; + } + + /** + * @inheritdoc + */ + protected function getMaxCharactersCssClass(): string + { + return 'class="note"'; + } + + /** + * @inheritdoc + */ + protected function getOptionsBlockName(): string + { + return 'product.composite.fieldset.options'; + } + + /** + * Checks that the xpath string is equal to the expected value + * + * @param ProductCustomOptionInterface $option + * @param string $html + * @param array $xpathData + * @return void + */ + private function assertEqualsXpath(ProductCustomOptionInterface $option, string $html, array $xpathData): void + { + $args = array_merge([$option->getOptionId()], $xpathData['args'] ?? []); + $expected = $xpathData['expected'] ?? 1; + $this->assertEquals( + $expected, + Xpath::getElementsCountForXpath(sprintf($xpathData['xpath'], ...$args), $html), + $xpathData['message'] + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php new file mode 100644 index 0000000000000..b326215688f13 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php @@ -0,0 +1,128 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Adminhtml\Product\Composite\Fieldset; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\Product as HelperProduct; +use Magento\Framework\DataObject; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test Qty block in composite product configuration layout + * + * @see \Magento\Catalog\Block\Adminhtml\Product\Composite\Fieldset\Qty + * @magentoAppArea adminhtml + */ +class QtyTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Qty */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Registry */ + private $registry; + + /** @var HelperProduct */ + private $helperProduct; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Qty::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->helperProduct = $this->objectManager->get(HelperProduct::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * @return void + */ + public function testGetProduct(): void + { + $product = $this->productRepository->get('simple-1'); + $this->registerProduct($product); + $this->assertSame($product, $this->block->getProduct()); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * @dataProvider getQtyValueProvider + * @param bool $isQty + * @param int $qty + * @return void + */ + public function testGetQtyValue(bool $isQty, int $qty): void + { + $product = $this->productRepository->get('simple-1'); + if ($isQty) { + /** @var DataObject $request */ + $buyRequest = $this->objectManager->create(DataObject::class); + $buyRequest->setData(['qty' => $qty]); + $this->helperProduct->prepareProductOptions($product, $buyRequest); + } + $this->registerProduct($product); + $this->assertEquals($qty, $this->block->getQtyValue(), 'Expected block qty value is incorrect!'); + } + + /** + * Provides test data to verify block qty value. + * + * @return array + */ + public function getQtyValueProvider(): array + { + return [ + 'with_qty' => [ + 'is_qty' => true, + 'qty' => 5, + ], + 'without_qty' => [ + 'is_qty' => false, + 'qty' => 1, + ], + ]; + } + + /** + * Register the product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + $this->registry->register('current_product', $product); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php new file mode 100644 index 0000000000000..3356d87a92794 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Catalog\Block\Adminhtml\Product\Composite; + +use Magento\Backend\Model\View\Result\Page; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Test Fieldset block in composite product configuration layout + * + * @see \Magento\Catalog\Block\Adminhtml\Product\Composite\Fieldset + * @magentoAppArea adminhtml + */ +class FieldsetTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Page */ + private $page; + + /** @var Registry */ + private $registry; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var string */ + private $fieldsetXpath = "//fieldset[@id='product_composite_configure_fields_%s']"; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->page = $this->objectManager->get(PageFactory::class)->create(); + $this->registry = $this->objectManager->get(Registry::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_options.php + * @return void + */ + public function testRenderHtml(): void + { + $product = $this->productRepository->get('simple'); + $this->registerProduct($product); + $this->preparePage($product->getTypeId()); + $html = $this->page->getLayout()->getBlock('product.composite.fieldset')->toHtml(); + + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->fieldsetXpath, 'options'), $html), + 'Expected options block is missing!' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->fieldsetXpath, 'qty'), $html), + 'Expected qty block is missing!' + ); + } + + /** + * Prepare page layout + * + * @param string $productType + * @return void + */ + private function preparePage(string $productType): void + { + $this->page->addHandle([ + 'default', + 'CATALOG_PRODUCT_COMPOSITE_CONFIGURE', + 'catalog_product_view_type_' . $productType, + ]); + $this->page->getLayout()->generateXml(); + } + + /** + * Register the product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('current_product'); + $this->registry->unregister('product'); + $this->registry->register('current_product', $product); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php index eb34696c70dbf..6e4df5dbacecb 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php @@ -9,14 +9,14 @@ use Magento\Catalog\Api\Data\ProductCustomOptionInterface; use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; +use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterface; use Magento\Catalog\Api\Data\ProductCustomOptionValuesInterfaceFactory; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Block\Product\View\Options; use Magento\Catalog\Model\Product\Option; -use Magento\Catalog\Model\Product\Option\Value; -use Magento\Framework\View\Element\Template; use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use PHPUnit\Framework\TestCase; @@ -29,12 +29,12 @@ abstract class AbstractRenderCustomOptionsTest extends TestCase /** * @var ObjectManager */ - private $objectManager; + protected $objectManager; /** * @var ProductRepositoryInterface */ - private $productRepository; + protected $productRepository; /** * @var ProductCustomOptionInterfaceFactory @@ -62,7 +62,7 @@ protected function setUp(): void $this->productCustomOptionValuesFactory = $this->objectManager->get( ProductCustomOptionValuesInterfaceFactory::class ); - $this->page = $this->objectManager->create(Page::class); + $this->page = $this->objectManager->get(PageFactory::class)->create(); parent::setUp(); } @@ -94,11 +94,26 @@ protected function assertTextOptionRenderingOnProduct( $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); $optionHtml = $this->getOptionHtml($product); $this->baseOptionAsserts($option, $optionHtml, $checkArray); + $this->additionalTypeTextAsserts($option, $optionHtml, $checkArray); + } - if ($optionData[Option::KEY_MAX_CHARACTERS] > 0) { + /** + * Additional asserts for rendering text type options. + * + * @param ProductCustomOptionInterface $option + * @param string $optionHtml + * @param array $checkArray + * @return void + */ + protected function additionalTypeTextAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + if ($option->getMaxCharacters() > 0) { $this->assertStringContainsString($checkArray['max_characters'], $optionHtml); } else { - $this->assertStringNotContainsString('class="character-counter', $optionHtml); + $this->assertStringNotContainsString($this->getMaxCharactersCssClass(), $optionHtml); } } @@ -153,22 +168,36 @@ protected function assertSelectOptionRenderingOnProduct( $product = $this->productRepository->get($productSku); $product = $this->addOptionToProduct($product, $optionData, $optionValueData); $option = $this->findOptionByTitle($product, $optionData[Option::KEY_TITLE]); - $optionValues = $option->getValues(); - $optionValue = reset($optionValues); $optionHtml = $this->getOptionHtml($product); $this->baseOptionAsserts($option, $optionHtml, $checkArray); + $this->additionalTypeSelectAsserts($option, $optionHtml, $checkArray); + } + /** + * Additional asserts for rendering select type options. + * + * @param ProductCustomOptionInterface $option + * @param string $optionHtml + * @param array $checkArray + * @return void + */ + protected function additionalTypeSelectAsserts( + ProductCustomOptionInterface $option, + string $optionHtml, + array $checkArray + ): void { + $optionValues = $option->getValues(); + $optionValue = reset($optionValues); if (isset($checkArray['not_contain_arr'])) { foreach ($checkArray['not_contain_arr'] as $notContainPattern) { $this->assertDoesNotMatchRegularExpression($notContainPattern, $optionHtml); } } - if (isset($checkArray['option_value_item'])) { $checkArray['option_value_item'] = sprintf( $checkArray['option_value_item'], $optionValue->getOptionTypeId(), - $optionValueData[Value::KEY_TITLE] + $optionValue->getTitle() ); $this->assertMatchesRegularExpression($checkArray['option_value_item'], $optionHtml); } @@ -284,7 +313,7 @@ protected function assertDateOptionRenderingOnProduct( * @param array $checkArray * @return void */ - private function baseOptionAsserts( + protected function baseOptionAsserts( ProductCustomOptionInterface $option, string $optionHtml, array $checkArray @@ -317,7 +346,7 @@ private function baseOptionAsserts( * @param array $optionValueData * @return ProductInterface */ - private function addOptionToProduct( + protected function addOptionToProduct( ProductInterface $product, array $optionData, array $optionValueData = [] @@ -341,30 +370,17 @@ private function addOptionToProduct( * @param ProductInterface $product * @return string */ - private function getOptionHtml(ProductInterface $product): string + protected function getOptionHtml(ProductInterface $product): string { - $optionsBlock = $this->getOptionsBlock(); + $this->page->addHandle($this->getHandlesList($product)); + $this->page->getLayout()->generateXml(); + /** @var Options $optionsBlock */ + $optionsBlock = $this->page->getLayout()->getBlock($this->getOptionsBlockName()); $optionsBlock->setProduct($product); return $optionsBlock->toHtml(); } - /** - * Get options block. - * - * @return Options - */ - private function getOptionsBlock(): Options - { - $this->page->addHandle($this->getHandlesList()); - $this->page->getLayout()->generateXml(); - /** @var Template $productInfoFormOptionsBlock */ - $productInfoFormOptionsBlock = $this->page->getLayout()->getBlock('product.info.form.options'); - $optionsWrapperBlock = $productInfoFormOptionsBlock->getChildBlock('product_options_wrapper'); - - return $optionsWrapperBlock->getChildBlock('product_options'); - } - /** * Find and return custom option. * @@ -372,7 +388,7 @@ private function getOptionsBlock(): Options * @param string $optionTitle * @return null|Option */ - private function findOptionByTitle(ProductInterface $product, string $optionTitle): ?Option + protected function findOptionByTitle(ProductInterface $product, string $optionTitle): ?Option { $option = null; foreach ($product->getOptions() as $customOption) { @@ -385,10 +401,47 @@ private function findOptionByTitle(ProductInterface $product, string $optionTitl return $option; } + /** + * Find and return custom option value. + * + * @param ProductCustomOptionInterface $option + * @param string $optionValueTitle + * @return null|ProductCustomOptionValuesInterface + */ + protected function findOptionValueByTitle( + ProductCustomOptionInterface $option, + string $optionValueTitle + ): ?ProductCustomOptionValuesInterface { + $optionValue = null; + foreach ($option->getValues() as $customOptionValue) { + if ($customOptionValue->getTitle() === $optionValueTitle) { + $optionValue = $customOptionValue; + break; + } + } + + return $optionValue; + } + /** * Return all need handles for load. * + * @param ProductInterface $product * @return array */ - abstract protected function getHandlesList(): array; + abstract protected function getHandlesList(ProductInterface $product): array; + + /** + * + * + * @return string + */ + abstract protected function getMaxCharactersCssClass(): string; + + /** + * + * + * @return string + */ + abstract protected function getOptionsBlockName(): string; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php index da31cfc74476a..fa05e5ece10e3 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php @@ -7,6 +7,8 @@ namespace Magento\Catalog\Block\Product\View\Options; +use Magento\Catalog\Api\Data\ProductInterface; + /** * Test cases related to check that simple product custom option renders as expected. * @@ -82,11 +84,27 @@ public function testRenderCustomOptionsFromDateGroup(array $optionData, array $c /** * @inheritdoc */ - protected function getHandlesList(): array + protected function getHandlesList(ProductInterface $product): array { return [ 'default', 'catalog_product_view', ]; } + + /** + * @inheritdoc + */ + protected function getMaxCharactersCssClass(): string + { + return 'class="character-counter'; + } + + /** + * @inheritdoc + */ + protected function getOptionsBlockName(): string + { + return 'product.info.options'; + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php new file mode 100644 index 0000000000000..3ada18a1849c9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Block\Adminhtml\Product\Composite\Fieldset; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test Configurable block in composite product configuration layout + * + * @see \Magento\ConfigurableProduct\Block\Adminhtml\Product\Composite\Fieldset\Configurable + * @magentoAppArea adminhtml + */ +class ConfigurableTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var SerializerInterface */ + private $serializer; + + /** @var Configurable */ + private $block; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var Registry */ + private $registry; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class); + $this->registry = $this->objectManager->get(Registry::class); + } + + /** + * @inheritdoc + */ + protected function tearDown(): void + { + $this->registry->unregister('product'); + } + + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php + * @return void + */ + public function testGetProduct(): void + { + $product = $this->productRepository->get('simple-1'); + $this->registerProduct($product); + $blockProduct = $this->block->getProduct(); + $this->assertSame($product, $blockProduct); + $this->assertNotNull($blockProduct->getTypeInstance()->getStoreFilter($blockProduct)); + } + + /** + * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_products.php + * @return void + */ + public function testGetJsonConfig(): void + { + $product = $this->productRepository->get('configurable'); + $this->registerProduct($product); + $config = $this->serializer->unserialize($this->block->getJsonConfig()); + $this->assertTrue($config['disablePriceReload']); + $this->assertTrue($config['stablePrices']); + } + + /** + * Register the product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php index 55f8b91f07093..8bac488718217 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php @@ -7,6 +7,7 @@ namespace Magento\ConfigurableProduct\Block\Product\View\CustomOptions; +use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Block\Product\View\Options\AbstractRenderCustomOptionsTest; /** @@ -85,7 +86,7 @@ public function testRenderCustomOptionsFromDateGroup(array $optionData, array $c /** * @inheritdoc */ - protected function getHandlesList(): array + protected function getHandlesList(ProductInterface $product): array { return [ 'default', @@ -93,4 +94,20 @@ protected function getHandlesList(): array 'catalog_product_view_type_configurable', ]; } + + /** + * @inheritdoc + */ + protected function getMaxCharactersCssClass(): string + { + return 'class="character-counter'; + } + + /** + * @inheritdoc + */ + protected function getOptionsBlockName(): string + { + return 'product.info.options'; + } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php index 39ed7965ea9e9..62b691d61efb3 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php @@ -9,9 +9,12 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Helper\Product as HelperProduct; use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute as ConfigurableAttribute; use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\DataObject; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\LayoutInterface; @@ -26,6 +29,7 @@ * @magentoAppIsolation enabled * @magentoDbIsolation enabled * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ConfigurableTest extends TestCase { @@ -64,6 +68,11 @@ class ConfigurableTest extends TestCase */ private $product; + /** + * @var HelperProduct + */ + private $helperProduct; + /** * @inheritdoc */ @@ -79,6 +88,7 @@ protected function setUp(): void $this->product = $this->productRepository->get('configurable'); $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class); $this->block->setProduct($this->product); + $this->helperProduct = $this->objectManager->get(HelperProduct::class); } /** @@ -128,6 +138,29 @@ public function testGetJsonConfig(): void $this->assertCount(0, $config['images']); } + /** + * @return void + */ + public function testGetJsonConfigWithPreconfiguredValues(): void + { + /** @var ConfigurableAttribute $attribute */ + $attribute = $this->product->getExtensionAttributes()->getConfigurableProductOptions()[0]; + $expectedAttributeValue = [ + $attribute->getAttributeId() => $attribute->getOptions()[0]['value_index'], + ]; + /** @var DataObject $request */ + $buyRequest = $this->objectManager->create(DataObject::class); + $buyRequest->setData([ + 'qty' => 1, + 'super_attribute' => $expectedAttributeValue, + ]); + $this->helperProduct->prepareProductOptions($this->product, $buyRequest); + + $config = $this->serializer->unserialize($this->block->getJsonConfig()); + $this->assertArrayHasKey('defaultValues', $config); + $this->assertEquals($expectedAttributeValue, $config['defaultValues']); + } + /** * @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_child_products_with_images.php * @return void From 0591d51dbbff7bfb4714a29d8b2e682d46f76708 Mon Sep 17 00:00:00 2001 From: saphaljha <saphal.jha@krishtechnolabs.com> Date: Thu, 3 Sep 2020 14:32:00 +0300 Subject: [PATCH 109/195] covered mftf for bundle items --- ...aceOrderWithMultipleOptionsSuccessTest.xml | 93 +++++++++++++++++++ .../Mftf/Section/AdminInvoiceItemsSection.xml | 1 + 2 files changed, 94 insertions(+) create mode 100644 app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml new file mode 100644 index 0000000000000..4e01f950cd087 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest"> + <annotations> + <features value="Bundle"/> + <stories value="Bundle product details page"/> + <title value="Customer should be able to see all the bundle items in invoice view"/> + <description value="Customer should be able to see all the bundle items in invoice view"/> + <severity value="CRITICAL"/> + <group value="Bundle"/> + </annotations> + <before> + <createData entity="_defaultCategory" stepKey="createPreReqCategory"/> + <createData entity="SimpleProduct2" stepKey="firstSimpleProduct"/> + <createData entity="SimpleProduct2" stepKey="secondSimpleProduct"/> + <createData entity="CustomerEntityOne" stepKey="createCustomer"/> + <actionGroup stepKey="loginToAdminPanel" ref="AdminLoginActionGroup"/> + </before> + <after> + <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> + <deleteData createDataKey="firstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="secondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!-- Create new bundle product --> + <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="createBundleProduct"> + <argument name="productType" value="bundle"/> + </actionGroup> + + <!-- Fill all main fields --> + <actionGroup ref="FillMainBundleProductFormActionGroup" stepKey="fillMainProductFields"/> + + <!-- Add first bundle option to the product --> + <actionGroup ref="AddBundleOptionWithTwoProductsActionGroup" stepKey="addFirstBundleOption"> + <argument name="x" value="0"/> + <argument name="n" value="1"/> + <argument name="prodOneSku" value="$firstSimpleProduct.sku$"/> + <argument name="prodTwoSku" value="$secondSimpleProduct.sku$$"/> + <argument name="optionTitle" value="{{CheckboxOption.title}}"/> + <argument name="inputType" value="{{CheckboxOption.type}}"/> + </actionGroup> + + <!-- Save product form --> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveWithThreeOptions"/> + + <!--Login customer on storefront--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + + <!--Open Product Page--> + <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage"> + <argument name="productUrl" value="{{BundleProduct.name}}"/> + </actionGroup> + + <!-- Add bundle to cart --> + <actionGroup ref="StorefrontSelectCustomizeAndAddToTheCartButtonActionGroup" stepKey="clickAddToCart"> + <argument name="productUrl" value="{{BundleProduct.name}}"/> + </actionGroup> + <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts(CheckboxOption.title, '1')}}" stepKey="selectOption2Product1"/> + <checkOption selector="{{StorefrontBundledSection.checkboxOptionThreeProducts(CheckboxOption.title, '2')}}" stepKey="selectOption2Product2"/> + <actionGroup ref="StorefrontEnterProductQuantityAndAddToTheCartActionGroup" stepKey="enterProductQuantityAndAddToTheCart"> + <argument name="quantity" value="1"/> + </actionGroup> + + <!--Navigate to checkout--> + <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="openCheckoutPage"/> + <!-- Click next button to open payment section --> + <actionGroup ref="StorefrontCheckoutClickNextButtonActionGroup" stepKey="clickNext"/> + <!-- Click place order --> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="placeOrder"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + + <!-- Order review page has address that was created during checkout --> + <actionGroup ref="OpenOrderByIdActionGroup" stepKey="filterOrdersGridById"> + <argument name="orderId" value="{$grabOrderNumber}"/> + </actionGroup> + + <!-- Open create invoice page --> + <actionGroup ref="StartCreateInvoiceFromOrderPageActionGroup" stepKey="startInvoice"/> + + <!-- Assert item options display --> + <see selector="{{AdminInvoiceItemsSection.bundleItem}}" userInput="50 x $firstSimpleProduct.sku$" stepKey="seeFirstProductInList"/> + <see selector="{{AdminInvoiceItemsSection.bundleItem}}" userInput="50 x $secondSimpleProduct.sku$" stepKey="seeSecondProductInList"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml index 92c01cf380746..4d75589c40e9c 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminInvoiceItemsSection.xml @@ -28,5 +28,6 @@ <element name="discountAmountColumn" type="text" selector=".order-invoice-tables .col-discount .price"/> <element name="totalColumn" type="text" selector=".order-invoice-tables .col-total .price"/> <element name="updateQty" type="button" selector=".order-invoice-tables tfoot button[data-ui-id='order-items-update-button']"/> + <element name="bundleItem" type="text" selector="#invoice_item_container .option-value"/> </section> </sections> From 140c351ead8abf866df95312985b871bfd60ec55 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Thu, 3 Sep 2020 14:32:20 +0300 Subject: [PATCH 110/195] fix static --- .../templates/sales/invoice/create/items/renderer.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml index 9564fbb785119..15ef3c311e396 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml @@ -130,8 +130,8 @@ $catalogHelper = $block->getData('catalogHelper'); <?php endif; ?> </td> <td class="col-qty-invoice"> - <?php if ($block->canShowPriceInfo($_item) || $shipTogether) : ?> - <?php if ($block->canEditQty() && $canEditItemQty) : ?> + <?php if ($block->canShowPriceInfo($_item) || $shipTogether): ?> + <?php if ($block->canEditQty() && $canEditItemQty): ?> <input type="text" class="input-text admin__control-text qty-input" name="invoice[items][<?= $block->escapeHtmlAttr($_item->getOrderItemId()) ?>]" From a55b88012ad40be7bcc64bda42b52e67e0c0b073 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Sep 2020 14:23:56 +0100 Subject: [PATCH 111/195] Introduced MediaGalleryRenditions and MediaGalleryRenditionsApi modules --- .../MediaGalleryRenditions/LICENSE.txt | 48 ++++ .../MediaGalleryRenditions/LICENSE_AFL.txt | 48 ++++ .../MediaGalleryRenditions/Model/Config.php | 117 ++++++++++ .../Model/GenerateRenditions.php | 216 ++++++++++++++++++ .../Model/GetRenditionPath.php | 26 +++ .../Queue/FetchRenditionPathsBatches.php | 111 +++++++++ .../Model/Queue/GetFilesIterator.php | 33 +++ .../Model/Queue/ScheduleRenditionsUpdate.php | 44 ++++ .../Model/Queue/UpdateRenditions.php | 126 ++++++++++ .../Plugin/RemoveRenditions.php | 84 +++++++ .../Plugin/SetRenditionPath.php | 111 +++++++++ .../Plugin/UpdateRenditionsOnConfigChange.php | 62 +++++ .../Magento/MediaGalleryRenditions/README.md | 13 ++ ...ractAssetsFromContentWithRenditionTest.php | 116 ++++++++++ .../Model/GenerateRenditionsTest.php | 124 ++++++++++ .../Model/GetRenditionPathTest.php | 77 +++++++ .../Test/_files/magento_large_image.jpg | Bin 0 -> 55303 bytes .../Test/_files/magento_medium_image.jpg | Bin 0 -> 34986 bytes .../MediaGalleryRenditions/composer.json | 24 ++ .../etc/adminhtml/system.xml | 24 ++ .../etc/communication.xml | 14 ++ .../MediaGalleryRenditions/etc/config.xml | 17 ++ .../Magento/MediaGalleryRenditions/etc/di.xml | 31 +++ .../etc/media_content.xml | 18 ++ .../MediaGalleryRenditions/etc/module.xml | 10 + .../etc/queue_consumer.xml | 11 + .../etc/queue_publisher.xml | 12 + .../etc/queue_topology.xml | 14 ++ .../MediaGalleryRenditions/registration.php | 14 ++ .../Api/GenerateRenditionsInterface.php | 21 ++ .../Api/GetRenditionPathInterface.php | 22 ++ .../MediaGalleryRenditionsApi/LICENSE.txt | 48 ++++ .../MediaGalleryRenditionsApi/LICENSE_AFL.txt | 48 ++++ .../MediaGalleryRenditionsApi/README.md | 13 ++ .../MediaGalleryRenditionsApi/composer.json | 21 ++ .../MediaGalleryRenditionsApi/etc/module.xml | 10 + .../registration.php | 14 ++ composer.json | 2 + composer.lock | 2 +- 39 files changed, 1745 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/MediaGalleryRenditions/LICENSE.txt create mode 100644 app/code/Magento/MediaGalleryRenditions/LICENSE_AFL.txt create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/Config.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/GetRenditionPath.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/Queue/GetFilesIterator.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/Queue/ScheduleRenditionsUpdate.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php create mode 100644 app/code/Magento/MediaGalleryRenditions/README.md create mode 100644 app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/ExtractAssetsFromContentWithRenditionTest.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GenerateRenditionsTest.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GetRenditionPathTest.php create mode 100644 app/code/Magento/MediaGalleryRenditions/Test/_files/magento_large_image.jpg create mode 100644 app/code/Magento/MediaGalleryRenditions/Test/_files/magento_medium_image.jpg create mode 100644 app/code/Magento/MediaGalleryRenditions/composer.json create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/communication.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/config.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/di.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/media_content.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/module.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/queue_consumer.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/queue_publisher.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/etc/queue_topology.xml create mode 100644 app/code/Magento/MediaGalleryRenditions/registration.php create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/LICENSE.txt create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/LICENSE_AFL.txt create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/README.md create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/composer.json create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/etc/module.xml create mode 100644 app/code/Magento/MediaGalleryRenditionsApi/registration.php diff --git a/app/code/Magento/MediaGalleryRenditions/LICENSE.txt b/app/code/Magento/MediaGalleryRenditions/LICENSE.txt new file mode 100644 index 0000000000000..36b2459f6aa63 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/MediaGalleryRenditions/LICENSE_AFL.txt b/app/code/Magento/MediaGalleryRenditions/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Config.php b/app/code/Magento/MediaGalleryRenditions/Model/Config.php new file mode 100644 index 0000000000000..a40fbb41bd831 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/Config.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Model; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Exception\NoSuchEntityException; + +/** + * Class responsible for providing access to Media Gallery Renditions system configuration. + */ +class Config +{ + private const TABLE_CORE_CONFIG_DATA = 'core_config_data'; + private const XML_PATH_ENABLED = 'system/media_gallery/enabled'; + private const XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH = 'system/media_gallery_renditions/width'; + private const XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH = 'system/media_gallery_renditions/height'; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + ScopeConfigInterface $scopeConfig, + ResourceConnection $resourceConnection + ) { + $this->scopeConfig = $scopeConfig; + $this->resourceConnection = $resourceConnection; + } + + /** + * Check if the media gallery is enabled + * + * @return bool + */ + public function isEnabled(): bool + { + return $this->scopeConfig->isSetFlag(self::XML_PATH_ENABLED); + } + + /** + * Get max width + * + * @return int + */ + public function getWidth(): int + { + try { + return $this->getDatabaseValue(self::XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH); + } catch (NoSuchEntityException $exception) { + return (int) $this->scopeConfig->getValue(self::XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH); + } + } + + /** + * Get max height + * + * @return int + */ + public function getHeight(): int + { + try { + return $this->getDatabaseValue(self::XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH); + } catch (NoSuchEntityException $exception) { + return (int) $this->scopeConfig->getValue(self::XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH); + } + } + + /** + * Get value from database bypassing config cache + * + * @param string $path + * @return int + * @throws NoSuchEntityException + */ + private function getDatabaseValue(string $path): int + { + $connection = $this->resourceConnection->getConnection(); + $select = $connection->select() + ->from( + [ + 'config' => $this->resourceConnection->getTableName(self::TABLE_CORE_CONFIG_DATA) + ], + [ + 'value' + ] + ) + ->where('config.path = ?', $path); + $value = $connection->query($select)->fetchColumn(); + + if ($value === false) { + throw new NoSuchEntityException( + __( + 'The config value for %path is not saved to database.', + ['path' => $path] + ) + ); + } + + return (int) $value; + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php b/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php new file mode 100644 index 0000000000000..d1bfcb82a6a84 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php @@ -0,0 +1,216 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Model; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\Driver\File; +use Magento\Framework\Image\AdapterFactory; +use Magento\MediaGalleryApi\Api\IsPathExcludedInterface; +use Magento\MediaGalleryRenditionsApi\Api\GenerateRenditionsInterface; +use Magento\MediaGalleryRenditionsApi\Api\GetRenditionPathInterface; +use Psr\Log\LoggerInterface; + +class GenerateRenditions implements GenerateRenditionsInterface +{ + private const IMAGE_FILE_NAME_PATTERN = '#\.(jpg|jpeg|gif|png)$# i'; + + /** + * @var AdapterFactory + */ + private $imageFactory; + + /** + * @var Config + */ + private $config; + + /** + * @var GetRenditionPathInterface + */ + private $getRenditionPath; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var File + */ + private $driver; + + /** + * @var IsPathExcludedInterface + */ + private $isPathExcluded; + + /** + * @var LoggerInterface + */ + private $log; + + /** + * @param AdapterFactory $imageFactory + * @param Config $config + * @param GetRenditionPathInterface $getRenditionPath + * @param Filesystem $filesystem + * @param File $driver + */ + public function __construct( + AdapterFactory $imageFactory, + Config $config, + GetRenditionPathInterface $getRenditionPath, + Filesystem $filesystem, + File $driver, + IsPathExcludedInterface $isPathExcluded, + LoggerInterface $log + ) { + $this->imageFactory = $imageFactory; + $this->config = $config; + $this->getRenditionPath = $getRenditionPath; + $this->filesystem = $filesystem; + $this->driver = $driver; + $this->isPathExcluded = $isPathExcluded; + $this->log = $log; + } + + /** + * @inheritdoc + */ + public function execute(array $paths): void + { + $failedPaths = []; + + foreach ($paths as $path) { + try { + $this->generateRendition($path); + } catch (\Exception $exception) { + $this->log->error($exception); + $failedPaths[] = $path; + } + } + + if (!empty($failedPaths)) { + throw new LocalizedException( + __( + 'Cannot create rendition for media asset paths: %paths', + [ + 'paths' => implode(', ', $failedPaths) + ] + ) + ); + } + } + + /** + * Generate rendition for media asset path + * + * @param string $path + * @throws FileSystemException + * @throws LocalizedException + * @throws \Exception + */ + private function generateRendition(string $path): void + { + $renditionPath = $this->getRenditionPath->execute($path); + $this->createDirectory($renditionPath); + + $absolutePath = $this->getMediaDirectory()->getAbsolutePath($path); + + if ($this->shouldFileBeResized($absolutePath)) { + $this->createResizedRendition( + $absolutePath, + $this->getMediaDirectory()->getAbsolutePath($renditionPath) + ); + } else { + $this->getMediaDirectory()->copyFile($path, $renditionPath); + } + } + + /** + * @param string $path + * @throws FileSystemException + * @throws LocalizedException + */ + private function validateAsset(string $path): void + { + if (!$this->getMediaDirectory()->isFile($path)) { + throw new LocalizedException(__('Media asset file %path does not exist!', ['path' => $path])); + } + + if ($this->isPathExcluded->execute($path)) { + throw new LocalizedException( + __('Could not create rendition for image, path is restricted: %path', ['path' => $path]) + ); + } + + if (!preg_match(self::IMAGE_FILE_NAME_PATTERN, $path)) { + throw new LocalizedException( + __('Could not create rendition for image, unsupported file type: %path.', ['path' => $path]) + ); + } + } + + /** + * Create directory for rendition file + * + * @param string $path + * @throws LocalizedException + */ + private function createDirectory(string $path): void + { + try { + $this->getMediaDirectory()->create($this->driver->getParentDirectory($path)); + } catch (\Exception $exception) { + throw new LocalizedException(__('Cannot create directory for rendition %path', ['path' => $path])); + } + } + + /** + * Create rendition file + * + * @param string $absolutePath + * @param string $absoluteRenditionPath + * @throws \Exception + */ + private function createResizedRendition(string $absolutePath, string $absoluteRenditionPath): void + { + $image = $this->imageFactory->create(); + $image->open($absolutePath); + $image->keepAspectRatio(true); + $image->resize($this->config->getWidth(), $this->config->getHeight()); + $image->save($absoluteRenditionPath); + } + + /** + * Check if image needs to resize or not + * + * @param string $absolutePath + * @return bool + */ + private function shouldFileBeResized(string $absolutePath): bool + { + [$width, $height] = getimagesize($absolutePath); + return $width > $this->config->getWidth() || $height > $this->config->getHeight(); + } + + /** + * Retrieve a media directory instance with write permissions + * + * @return WriteInterface + * @throws FileSystemException + */ + private function getMediaDirectory(): WriteInterface + { + return $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA); + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Model/GetRenditionPath.php b/app/code/Magento/MediaGalleryRenditions/Model/GetRenditionPath.php new file mode 100644 index 0000000000000..1c93141429ab0 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/GetRenditionPath.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Model; + +use Magento\MediaGalleryRenditionsApi\Api\GetRenditionPathInterface; + +class GetRenditionPath implements GetRenditionPathInterface +{ + private const RENDITIONS_DIRECTORY_NAME = '.renditions'; + + /** + * Returns Rendition image path + * + * @param string $path + * @return string + */ + public function execute(string $path): string + { + return self::RENDITIONS_DIRECTORY_NAME . '/' . ltrim($path, '/'); + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php b/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php new file mode 100644 index 0000000000000..9cec7edbbd39a --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\MediaGalleryRenditions\Model\Queue; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Psr\Log\LoggerInterface; + +/** + * Fetch files from media storage in batches + */ +class FetchRenditionPathsBatches +{ + private const RENDITIONS_DIRECTORY_NAME = '.renditions'; + + /** + * @var GetFilesIterator + */ + private $getFilesIterator; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var string + */ + private $fileExtensions; + + /** + * @var LoggerInterface + */ + private $log; + + /** + * @var int + */ + private $batchSize; + + /** + * @param LoggerInterface $log + * @param Filesystem $filesystem + * @param GetFilesIterator $assetsIterator + * @param int $batchSize + * @param array $fileExtensions + */ + public function __construct( + LoggerInterface $log, + Filesystem $filesystem, + GetFilesIterator $getFilesIterator, + int $batchSize, + array $fileExtensions + ) { + $this->log = $log; + $this->getFilesIterator = $getFilesIterator; + $this->filesystem = $filesystem; + $this->batchSize = $batchSize; + $this->fileExtensions = $fileExtensions; + } + + /** + * Return files from files system by provided size of batch + */ + public function execute(): \Traversable + { + $index = 0; + $batch = []; + $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + $iterator = $this->getFilesIterator->execute( + $mediaDirectory->getAbsolutePath(self::RENDITIONS_DIRECTORY_NAME) + ); + + /** @var \SplFileInfo $file */ + foreach ($iterator as $file) { + $relativePath = $mediaDirectory->getRelativePath($file->getPathName()); + if (!$this->isApplicable($relativePath)) { + continue; + } + + $batch[] = $relativePath; + if (++$index == $this->batchSize) { + yield $batch; + $index = 0; + $batch = []; + } + } + if (count($batch) > 0) { + yield $batch; + } + } + + /** + * Is the path a valid image path + * + * @param string $path + * @return bool + */ + private function isApplicable(string $path): bool + { + try { + return $path && preg_match('#\.(' . implode("|", $this->fileExtensions) . ')$# i', $path); + } catch (\Exception $exception) { + $this->log->critical($exception); + return false; + } + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Queue/GetFilesIterator.php b/app/code/Magento/MediaGalleryRenditions/Model/Queue/GetFilesIterator.php new file mode 100644 index 0000000000000..97efcdc81ba50 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/Queue/GetFilesIterator.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Model\Queue; + +/** + * Retrieve files iterator for path + */ +class GetFilesIterator +{ + /** + * Get files iterator for provided path + * + * @param string $path + * @return \RecursiveIteratorIterator + */ + public function execute(string $path): \RecursiveIteratorIterator + { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator( + $path, + \FilesystemIterator::SKIP_DOTS | + \FilesystemIterator::UNIX_PATHS | + \RecursiveDirectoryIterator::FOLLOW_SYMLINKS + ), + \RecursiveIteratorIterator::CHILD_FIRST + ); + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Queue/ScheduleRenditionsUpdate.php b/app/code/Magento/MediaGalleryRenditions/Model/Queue/ScheduleRenditionsUpdate.php new file mode 100644 index 0000000000000..051c883025587 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/Queue/ScheduleRenditionsUpdate.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Model\Queue; + +use Magento\Framework\MessageQueue\PublisherInterface; + +/** + * Publish media gallery renditions update message to the queue. + */ +class ScheduleRenditionsUpdate +{ + private const TOPIC_MEDIA_GALLERY_UPDATE_RENDITIONS = 'media.gallery.renditions.update'; + + /** + * @var PublisherInterface + */ + private $publisher; + + /** + * @param PublisherInterface $publisher + */ + public function __construct(PublisherInterface $publisher) + { + $this->publisher = $publisher; + } + + /** + * Publish media gallery renditions update message to the queue. + * + * @param array $paths + */ + public function execute(array $paths = []): void + { + $this->publisher->publish( + self::TOPIC_MEDIA_GALLERY_UPDATE_RENDITIONS, + $paths + ); + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php b/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php new file mode 100644 index 0000000000000..1fcc28d235f09 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php @@ -0,0 +1,126 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Model\Queue; + +use Magento\Framework\Exception\LocalizedException; +use Magento\MediaGalleryRenditionsApi\Api\GenerateRenditionsInterface; +use Psr\Log\LoggerInterface; + +/** + * Renditions update queue consumer. + */ +class UpdateRenditions +{ + private const RENDITIONS_DIRECTORY_NAME = '.renditions'; + + /** + * @var GenerateRenditionsInterface + */ + private $generateRenditions; + + /** + * @var FetchRenditionPathsBatches + */ + private $fetchRenditionPathsBatches; + + /** + * @var LoggerInterface + */ + private $log; + + /** + * @param GenerateRenditionsInterface $generateRenditions + * @param FetchRenditionPathsBatches $fetchRenditionPathsBatches + * @param LoggerInterface $log + */ + public function __construct( + GenerateRenditionsInterface $generateRenditions, + FetchRenditionPathsBatches $fetchRenditionPathsBatches, + LoggerInterface $log + ) { + $this->generateRenditions = $generateRenditions; + $this->fetchRenditionPathsBatches = $fetchRenditionPathsBatches; + $this->log = $log; + } + + /** + * Update renditions for given paths, if empty array is provided - all renditions are updated + * + * @param array $paths + * @throws LocalizedException + */ + public function execute(array $paths): void + { + if (!empty($paths)) { + $this->updateRenditions($paths); + return; + } + + foreach ($this->fetchRenditionPathsBatches->execute() as $renditionPaths) { + $this->updateRenditions($renditionPaths); + } + } + + /** + * Update renditions and log exceptions + * + * @param string[] $paths + */ + private function updateRenditions(array $renditionPaths): void + { + try { + $this->generateRenditions->execute($this->getAssetPaths($renditionPaths)); + } catch (LocalizedException $exception) { + $this->log->error($exception); + } + } + + /** + * Get asset paths based on rendition paths + * + * @param string[] $renditionPaths + * @return string[] + */ + private function getAssetPaths(array $renditionPaths): array + { + $paths = []; + + foreach ($renditionPaths as $renditionPath) { + try { + $paths[] = $this->getAssetPath($renditionPath); + } catch (\Exception $exception) { + $this->log->error($exception); + } + } + + return $paths; + } + + /** + * Get asset path based on rendition path + * + * @param string $renditionPath + * @return string + * @throws LocalizedException + */ + private function getAssetPath(string $renditionPath): string + { + if (strpos($renditionPath, self::RENDITIONS_DIRECTORY_NAME) !== 0) { + throw new LocalizedException( + __( + 'Incorrect rendition path provided for update: %path', + [ + 'path' => $renditionPath + ] + ) + ); + } + + return substr($renditionPath, strlen(self::RENDITIONS_DIRECTORY_NAME)); + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php new file mode 100644 index 0000000000000..dd75800e4384d --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Plugin; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\MediaGalleryApi\Api\DeleteAssetsByPathsInterface; +use Magento\MediaGalleryRenditionsApi\Api\GetRenditionPathInterface; +use Psr\Log\LoggerInterface; + +/** + * Remove renditions when assets are removed + */ +class RemoveRenditions +{ + /** + * @var GetRenditionPathInterface + */ + private $getRenditionPath; + + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var LoggerInterface + */ + private $log; + + /** + * @param GetRenditionPathInterface $getRenditionPath + * @param Filesystem $filesystem + * @param LoggerInterface $log + */ + public function __construct( + GetRenditionPathInterface $getRenditionPath, + Filesystem $filesystem, + LoggerInterface $log + ) { + $this->getRenditionPath = $getRenditionPath; + $this->filesystem = $filesystem; + $this->log = $log; + } + + /** + * Remove renditions when assets are removed + * + * @param DeleteAssetsByPathsInterface $deleteAssetsByPaths + * @param \Closure $proceed + * @param array $paths + */ + public function aroundExecute( + DeleteAssetsByPathsInterface $deleteAssetsByPaths, + \Closure $proceed, + array $paths + ): void { + $proceed($paths); + $this->removeRenditions($paths); + } + + /** + * Remove rendition files + * + * @param array $paths + */ + private function removeRenditions(array $paths): void + { + foreach ($paths as $path) { + try { + $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA)->delete( + $this->getRenditionPath->execute($path) + ); + } catch (\Exception $exception) { + $this->log->error($exception); + } + } + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php b/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php new file mode 100644 index 0000000000000..ec2012c528ef1 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/SetRenditionPath.php @@ -0,0 +1,111 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Plugin; + +use Magento\Cms\Helper\Wysiwyg\Images; +use Magento\Cms\Model\Wysiwyg\Images\GetInsertImageContent; +use Magento\Framework\Exception\LocalizedException; +use Magento\MediaGalleryRenditions\Model\Config; +use Magento\MediaGalleryRenditionsApi\Api\GenerateRenditionsInterface; +use Magento\MediaGalleryRenditionsApi\Api\GetRenditionPathInterface; +use Psr\Log\LoggerInterface; + +/** + * Intercept and set renditions path on PrepareImage + */ +class SetRenditionPath +{ + /** + * @var GetRenditionPathInterface + */ + private $getRenditionPath; + + /** + * @var GenerateRenditionsInterface + */ + private $generateRenditions; + + /** + * @var Images + */ + private $imagesHelper; + + /** + * @var Config + */ + private $config; + + /** + * @var LoggerInterface + */ + private $log; + + /** + * @param GetRenditionPathInterface $getRenditionPath + * @param GenerateRenditionsInterface $generateRenditions + * @param Images $imagesHelper + * @param Config $config + * @param LoggerInterface $log + */ + public function __construct( + GetRenditionPathInterface $getRenditionPath, + GenerateRenditionsInterface $generateRenditions, + Images $imagesHelper, + Config $config, + LoggerInterface $log + ) { + $this->getRenditionPath = $getRenditionPath; + $this->generateRenditions = $generateRenditions; + $this->imagesHelper = $imagesHelper; + $this->config = $config; + $this->log = $log; + } + + /** + * Replace the original asset path with rendition path + * + * @param GetInsertImageContent $subject + * @param string $encodedFilename + * @param bool $forceStaticPath + * @param bool $renderAsTag + * @param int|null $storeId + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeExecute( + GetInsertImageContent $subject, + string $encodedFilename, + bool $forceStaticPath, + bool $renderAsTag, + ?int $storeId = null + ): array { + $arguments = [ + $encodedFilename, + $forceStaticPath, + $renderAsTag, + $storeId + ]; + + if (!$this->config->isEnabled()) { + return $arguments; + } + + $path = $this->imagesHelper->idDecode($encodedFilename); + + try { + $this->generateRenditions->execute([$path]); + } catch (LocalizedException $exception) { + $this->log->error($exception); + return $arguments; + } + + $arguments[0] = $this->imagesHelper->idEncode($this->getRenditionPath->execute($path)); + + return $arguments; + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php b/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php new file mode 100644 index 0000000000000..9feb63377add9 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Plugin; + +use Magento\Framework\App\Config\Value; +use Magento\MediaGalleryRenditions\Model\Queue\ScheduleRenditionsUpdate; + +/** + * Update renditions if corresponding configuration changes + */ +class UpdateRenditionsOnConfigChange +{ + private const XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH = 'system/media_gallery_renditions/width'; + private const XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH = 'system/media_gallery_renditions/height'; + + /** + * @var ScheduleRenditionsUpdate + */ + private $scheduleRenditionsUpdate; + + /** + * @param ScheduleRenditionsUpdate $scheduleRenditionsUpdate + */ + public function __construct(ScheduleRenditionsUpdate $scheduleRenditionsUpdate) + { + $this->scheduleRenditionsUpdate = $scheduleRenditionsUpdate; + } + + /** + * Update renditions when configuration is changed + * + * @param Value $config + * @param Value $result + * @return Value + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterSave(Value $config, Value $result): Value + { + if ($this->isRenditionsValue($result) && $result->isValueChanged()) { + $this->scheduleRenditionsUpdate->execute(); + } + + return $result; + } + + /** + * Does configuration value relates to renditions + * + * @param Value $value + * @return bool + */ + private function isRenditionsValue(Value $value) + { + return $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH + || $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH; + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/README.md b/app/code/Magento/MediaGalleryRenditions/README.md new file mode 100644 index 0000000000000..df856e8003a84 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/README.md @@ -0,0 +1,13 @@ +# Magento_MediaGalleryRenditions module + +The Magento_MediaGalleryRenditions module implements height and width fields for for media gallery items. + +## Extensibility + +Extension developers can interact with the Magento_MediaGalleryRenditions module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). + +[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_MediaGalleryRenditions module. + +## Additional information + +For information about significant changes in patch releases, see [2.3.x Release information](https://devdocs.magento.com/guides/v2.3/release-notes/bk-release-notes.html). diff --git a/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/ExtractAssetsFromContentWithRenditionTest.php b/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/ExtractAssetsFromContentWithRenditionTest.php new file mode 100644 index 0000000000000..05bb01b9ff433 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/ExtractAssetsFromContentWithRenditionTest.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + * + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Test\Integration\Model; + +use Magento\MediaContentApi\Api\ExtractAssetsFromContentInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Test for Extracting assets from rendition paths/urls in content + */ +class ExtractAssetsFromContentWithRenditionTest extends TestCase +{ + /** + * @var ExtractAssetsFromContentInterface + */ + private $extractAssetsFromContent; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->extractAssetsFromContent = Bootstrap::getObjectManager() + ->get(ExtractAssetsFromContentInterface::class); + } + + /** + * Assert rendition urls/path in the content are associated with an asset + * + * @magentoDataFixture Magento/MediaGallery/_files/media_asset.php + * + * @dataProvider contentProvider + * @param string $content + * @param array $assetIds + */ + public function testExecute(string $content, array $assetIds): void + { + $assets = $this->extractAssetsFromContent->execute($content); + + $extractedAssetIds = []; + foreach ($assets as $asset) { + $extractedAssetIds[] = $asset->getId(); + } + + sort($assetIds); + sort($extractedAssetIds); + + $this->assertEquals($assetIds, $extractedAssetIds); + } + + /** + * Data provider for testExecute + * + * @return array + */ + public function contentProvider() + { + return [ + 'Empty Content' => [ + '', + [] + ], + 'No paths in content' => [ + 'content without paths', + [] + ], + 'Relevant rendition path in content' => [ + 'content {{media url=".renditions/testDirectory/path.jpg"}} content', + [ + 2020 + ] + ], + 'Relevant wysiwyg rendition path in content' => [ + 'content <img src="https://domain.com/media/.renditions/testDirectory/path.jpg"}} content', + [ + 2020 + ] + ], + 'Relevant rendition path content with pub' => [ + '/pub/media/.renditions/testDirectory/path.jpg', + [ + 2020 + ] + ], + 'Relevant rendition path content' => [ + '/media/.renditions/testDirectory/path.jpg', + [ + 2020 + ] + ], + 'Relevant existing media paths w/o rendition in content' => [ + 'content {{media url="testDirectory/path.jpg"}} content', + [ + 2020 + ] + ], + 'Relevant existing paths w/o rendition in content with pub' => [ + '/pub/media/testDirectory/path.jpg', + [ + 2020 + ] + ], + 'Non-existing rendition paths in content' => [ + 'content {{media url=".renditions/non-existing-path.png"}} content', + [] + ] + ]; + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GenerateRenditionsTest.php b/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GenerateRenditionsTest.php new file mode 100644 index 0000000000000..9655f3949d404 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GenerateRenditionsTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Test\Integration\Model; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\DriverInterface; +use Magento\MediaGalleryRenditionsApi\Api\GenerateRenditionsInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\MediaGalleryRenditions\Model\Config; +use PHPUnit\Framework\TestCase; + +class GenerateRenditionsTest extends TestCase +{ + /** + * @var GenerateRenditionsInterface + */ + private $generateRenditions; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + /** + * @var Config + */ + private $renditionSizeConfig; + + /** + * @var DriverInterface + */ + private $driver; + + protected function setup(): void + { + $this->generateRenditions = Bootstrap::getObjectManager()->get(GenerateRenditionsInterface::class); + $this->mediaDirectory = Bootstrap::getObjectManager()->get(Filesystem::class) + ->getDirectoryWrite(DirectoryList::MEDIA); + $this->driver = Bootstrap::getObjectManager()->get(DriverInterface::class); + $this->renditionSizeConfig = Bootstrap::getObjectManager()->get(Config::class); + } + + public static function tearDownAfterClass(): void + { + /** @var WriteInterface $mediaDirectory */ + $mediaDirectory = Bootstrap::getObjectManager()->get( + Filesystem::class + )->getDirectoryWrite( + DirectoryList::MEDIA + ); + if ($mediaDirectory->isExist($mediaDirectory->getAbsolutePath() . '/.renditions')) { + $mediaDirectory->delete($mediaDirectory->getAbsolutePath() . '/.renditions'); + } + } + + /** + * @dataProvider renditionsImageProvider + * + * Test for generation of rendition images. + * + * @param string $path + * @param string $renditionPath + * @throws LocalizedException + */ + public function testExecute(string $path, string $renditionPath): void + { + $this->copyImage($path); + $this->generateRenditions->execute([$path]); + $expectedRenditionPath = $this->mediaDirectory->getAbsolutePath($renditionPath); + list($imageWidth, $imageHeight) = getimagesize($expectedRenditionPath); + $this->assertFileExists($expectedRenditionPath); + $this->assertLessThanOrEqual( + $this->renditionSizeConfig->getWidth(), + $imageWidth, + 'Generated renditions image width should be less than or equal to configured value' + ); + $this->assertLessThanOrEqual( + $this->renditionSizeConfig->getHeight(), + $imageHeight, + 'Generated renditions image height should be less than or equal to configured value' + ); + } + + /** + * @param array $paths + * @throws FileSystemException + */ + private function copyImage(string $path): void + { + $imagePath = realpath(__DIR__ . '/../../_files' . $path); + $modifiableFilePath = $this->mediaDirectory->getAbsolutePath($path); + $this->driver->copy( + $imagePath, + $modifiableFilePath + ); + } + + /** + * @return array + */ + public function renditionsImageProvider(): array + { + return [ + 'rendition_image_not_generated' => [ + 'paths' => '/magento_medium_image.jpg', + 'renditionPath' => ".renditions/magento_medium_image.jpg" + ], + 'rendition_image_generated' => [ + 'paths' => '/magento_large_image.jpg', + 'renditionPath' => ".renditions/magento_large_image.jpg" + ] + ]; + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GetRenditionPathTest.php b/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GetRenditionPathTest.php new file mode 100644 index 0000000000000..0f8b61147986c --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/Test/Integration/Model/GetRenditionPathTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditions\Test\Integration\Model; + +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Filesystem; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use Magento\Framework\Filesystem\DriverInterface; +use Magento\MediaGalleryRenditionsApi\Api\GetRenditionPathInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +class GetRenditionPathTest extends TestCase +{ + + /** + * @var GetRenditionPathInterface + */ + private $getRenditionPath; + + /** + * @var WriteInterface + */ + private $mediaDirectory; + + /** + * @var DriverInterface + */ + private $driver; + + protected function setup(): void + { + $this->getRenditionPath = Bootstrap::getObjectManager()->get(GetRenditionPathInterface::class); + $this->mediaDirectory = Bootstrap::getObjectManager()->get(Filesystem::class) + ->getDirectoryWrite(DirectoryList::MEDIA); + $this->driver = Bootstrap::getObjectManager()->get(DriverInterface::class); + } + + /** + * @dataProvider getImageProvider + * + * Test for getting a rendition path. + */ + public function testExecute(string $path, string $expectedRenditionPath): void + { + $imagePath = realpath(__DIR__ . '/../../_files' . $path); + $modifiableFilePath = $this->mediaDirectory->getAbsolutePath($path); + $this->driver->copy( + $imagePath, + $modifiableFilePath + ); + $this->assertEquals($expectedRenditionPath, $this->getRenditionPath->execute($path)); + } + + /** + * @return array + */ + public function getImageProvider(): array + { + return [ + 'return_original_path' => [ + 'path' => '/magento_medium_image.jpg', + 'expectedRenditionPath' => '.renditions/magento_medium_image.jpg' + ], + 'return_rendition_path' => [ + 'path' => '/magento_large_image.jpg', + 'expectedRenditionPath' => '.renditions/magento_large_image.jpg' + ] + ]; + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/Test/_files/magento_large_image.jpg b/app/code/Magento/MediaGalleryRenditions/Test/_files/magento_large_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c377daf8fb0b390d89aa4f6f111715fc9118ca1b GIT binary patch literal 55303 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 Hh5vs5{BvNq literal 0 HcmV?d00001 diff --git a/app/code/Magento/MediaGalleryRenditions/Test/_files/magento_medium_image.jpg b/app/code/Magento/MediaGalleryRenditions/Test/_files/magento_medium_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6dc8cd69e41c1ed0fcdbd38370c3b5b90d6c3ded GIT binary patch literal 34986 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 literal 0 HcmV?d00001 diff --git a/app/code/Magento/MediaGalleryRenditions/composer.json b/app/code/Magento/MediaGalleryRenditions/composer.json new file mode 100644 index 0000000000000..948f6ce3b14f3 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/composer.json @@ -0,0 +1,24 @@ +{ + "name": "magento/module-media-gallery-renditions", + "description": "Magento module that implements height and width fields for for media gallery items.", + "require": { + "php": "~7.3.0||~7.4.0", + "magento/framework": "*", + "magento/module-media-gallery-renditions-api": "*", + "magento/module-media-content-api": "*", + "magento/module-cms": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\MediaGalleryRenditions\\": "" + } + } +} diff --git a/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml b/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..64f338d53a283 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/adminhtml/system.xml @@ -0,0 +1,24 @@ +<?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:module:Magento_Config:etc/system_file.xsd"> + <system> + <section id="system"> + <group id="media_gallery_renditions" translate="label" type="text" sortOrder="1010" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Media Gallery Renditions</label> + <field id="width" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Max Width</label> + <validate>validate-zero-or-greater validate-digits</validate> + </field> + <field id="height" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="0" showInStore="0"> + <label>Max Height</label> + <validate>validate-zero-or-greater validate-digits</validate> + </field> + </group> + </section> + </system> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/communication.xml b/app/code/Magento/MediaGalleryRenditions/etc/communication.xml new file mode 100644 index 0000000000000..2c343c4f8086a --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/communication.xml @@ -0,0 +1,14 @@ +<?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:Communication/etc/communication.xsd"> + <topic name="media.gallery.renditions.update" is_synchronous="false" request="string[]"> + <handler name="media.gallery.renditions.update.handler" + type="Magento\MediaGalleryRenditions\Model\Queue\UpdateRenditions" method="execute"/> + </topic> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/config.xml b/app/code/Magento/MediaGalleryRenditions/etc/config.xml new file mode 100644 index 0000000000000..58c5aa1f11fd2 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/config.xml @@ -0,0 +1,17 @@ +<?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:module:Magento_Store:etc/config.xsd"> + <default> + <system> + <media_gallery_renditions> + <width>1000</width> + <height>1000</height> + </media_gallery_renditions> + </system> + </default> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/di.xml b/app/code/Magento/MediaGalleryRenditions/etc/di.xml new file mode 100644 index 0000000000000..af53810b7f69e --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/di.xml @@ -0,0 +1,31 @@ +<?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:ObjectManager/etc/config.xsd"> + <preference for="Magento\MediaGalleryRenditionsApi\Api\GenerateRenditionsInterface" type="Magento\MediaGalleryRenditions\Model\GenerateRenditions"/> + <preference for="Magento\MediaGalleryRenditionsApi\Api\GetRenditionPathInterface" type="Magento\MediaGalleryRenditions\Model\GetRenditionPath"/> + <type name="Magento\Cms\Model\Wysiwyg\Images\GetInsertImageContent"> + <plugin name="set_rendition_path" type="Magento\MediaGalleryRenditions\Plugin\SetRenditionPath"/> + </type> + <type name="Magento\MediaGalleryRenditions\Model\Queue\FetchRenditionPathsBatches"> + <arguments> + <argument name="batchSize" xsi:type="number">100</argument> + <argument name="fileExtensions" xsi:type="array"> + <item name="jpg" xsi:type="string">jpg</item> + <item name="jpeg" xsi:type="string">jpeg</item> + <item name="gif" xsi:type="string">gif</item> + <item name="png" xsi:type="string">png</item> + </argument> + </arguments> + </type> + <type name="Magento\Framework\App\Config\Value"> + <plugin name="admin_system_config_media_gallery_renditions" type="Magento\MediaGalleryRenditions\Plugin\UpdateRenditionsOnConfigChange"/> + </type> + <type name="Magento\MediaGalleryApi\Api\DeleteAssetsByPathsInterface"> + <plugin name="delete_renditions_on_assets_delete" type="Magento\MediaGalleryRenditions\Plugin\RemoveRenditions"/> + </type> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml b/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml new file mode 100644 index 0000000000000..e3bb939158fec --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml @@ -0,0 +1,18 @@ +<?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:module:Magento_MediaContentApi:etc/media_content.xsd"> + <search> + <patterns> + <pattern name="media_gallery_renditions">/{{media url=(?:"|&quot;)(?:.renditions)?(.*?)(?:"|&quot;)}}/</pattern> + <pattern name="media_gallery">/{{media url="?((?!.*.renditions).*?)"?}}/</pattern> + <pattern name="wysiwyg">/src=".*\/media\/(?:.renditions\/)*(.*?)"/</pattern> + <pattern name="catalog_image">/^\/?media\/(?:.renditions\/)?(.*)/</pattern> + <pattern name="catalog_image_with_pub">/^\/pub\/?media\/(?:.renditions\/)?(.*)/</pattern> + </patterns> + </search> +</config> \ No newline at end of file diff --git a/app/code/Magento/MediaGalleryRenditions/etc/module.xml b/app/code/Magento/MediaGalleryRenditions/etc/module.xml new file mode 100644 index 0000000000000..93bc9f1c214e6 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/module.xml @@ -0,0 +1,10 @@ +<?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:Module/etc/module.xsd"> + <module name="Magento_MediaGalleryRenditions"/> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/queue_consumer.xml b/app/code/Magento/MediaGalleryRenditions/etc/queue_consumer.xml new file mode 100644 index 0000000000000..0c584ac12f898 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/queue_consumer.xml @@ -0,0 +1,11 @@ +<?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-message-queue:etc/consumer.xsd"> + <consumer name="media.gallery.renditions.update" queue="media.gallery.renditions.update" + connection="db" handler="Magento\MediaGalleryRenditions\Model\Queue\UpdateRenditions::execute"/> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/queue_publisher.xml b/app/code/Magento/MediaGalleryRenditions/etc/queue_publisher.xml new file mode 100644 index 0000000000000..9618329895230 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/queue_publisher.xml @@ -0,0 +1,12 @@ +<!-- +/** + * 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-message-queue:etc/publisher.xsd"> + <publisher topic="media.gallery.renditions.update"> + <connection name="db" exchange="magento-db" disabled="false" /> + </publisher> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/etc/queue_topology.xml b/app/code/Magento/MediaGalleryRenditions/etc/queue_topology.xml new file mode 100644 index 0000000000000..260e9f5f7f371 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/etc/queue_topology.xml @@ -0,0 +1,14 @@ +<?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-message-queue:etc/topology.xsd"> + <exchange name="magento-db" type="topic" connection="db"> + <binding id="MediaGalleryRenditions" topic="media.gallery.renditions.update" + destinationType="queue" destination="media.gallery.renditions.update"/> + </exchange> +</config> diff --git a/app/code/Magento/MediaGalleryRenditions/registration.php b/app/code/Magento/MediaGalleryRenditions/registration.php new file mode 100644 index 0000000000000..275c06f752a63 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditions/registration.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register( + ComponentRegistrar::MODULE, + 'Magento_MediaGalleryRenditions', + __DIR__ +); diff --git a/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php b/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php new file mode 100644 index 0000000000000..6684fcc47b6c1 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php @@ -0,0 +1,21 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditionsApi\Api; + +use Magento\Framework\Exception\LocalizedException; + +interface GenerateRenditionsInterface +{ + /** + * Generate image renditions + * + * @param string[] $paths + * @throws LocalizedException + */ + public function execute(array $paths): void; +} diff --git a/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php b/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php new file mode 100644 index 0000000000000..3f398dd37529b --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryRenditionsApi\Api; + +use Magento\Framework\Exception\LocalizedException; + +interface GetRenditionPathInterface +{ + /** + * Get Renditions image path + * + * @param string $path + * @return string + * @throws LocalizedException + */ + public function execute(string $path): string; +} diff --git a/app/code/Magento/MediaGalleryRenditionsApi/LICENSE.txt b/app/code/Magento/MediaGalleryRenditionsApi/LICENSE.txt new file mode 100644 index 0000000000000..36b2459f6aa63 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/MediaGalleryRenditionsApi/LICENSE_AFL.txt b/app/code/Magento/MediaGalleryRenditionsApi/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/MediaGalleryRenditionsApi/README.md b/app/code/Magento/MediaGalleryRenditionsApi/README.md new file mode 100644 index 0000000000000..42478c0c9b520 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/README.md @@ -0,0 +1,13 @@ +# Magento_MediaGalleryRenditionsApi module + +The Magento_MediaGalleryRenditionsApi module is responsible for the API implementation of Media Gallery Renditions. + +## Extensibility + +Extension developers can interact with the Magento_MediaGalleryRenditions module. For more information about the Magento extension mechanism, see [Magento plug-ins](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/plugins.html). + +[The Magento dependency injection mechanism](https://devdocs.magento.com/guides/v2.3/extension-dev-guide/depend-inj.html) enables you to override the functionality of the Magento_MediaGalleryRenditionsApi module. + +## Additional information + +For information about significant changes in patch releases, see [2.3.x Release information](https://devdocs.magento.com/guides/v2.3/release-notes/bk-release-notes.html). diff --git a/app/code/Magento/MediaGalleryRenditionsApi/composer.json b/app/code/Magento/MediaGalleryRenditionsApi/composer.json new file mode 100644 index 0000000000000..6e3c559f001c1 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-media-gallery-renditions-api", + "description": "Magento module that is responsible for the API implementation of Media Gallery Renditions.", + "require": { + "php": "~7.3.0||~7.4.0", + "magento/framework": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\MediaGalleryRenditionsApi\\": "" + } + } +} diff --git a/app/code/Magento/MediaGalleryRenditionsApi/etc/module.xml b/app/code/Magento/MediaGalleryRenditionsApi/etc/module.xml new file mode 100644 index 0000000000000..f3a3f87b61105 --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/etc/module.xml @@ -0,0 +1,10 @@ +<?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:Module/etc/module.xsd"> + <module name="Magento_MediaGalleryRenditionsApi"/> +</config> diff --git a/app/code/Magento/MediaGalleryRenditionsApi/registration.php b/app/code/Magento/MediaGalleryRenditionsApi/registration.php new file mode 100644 index 0000000000000..bf057f2d2adbf --- /dev/null +++ b/app/code/Magento/MediaGalleryRenditionsApi/registration.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register( + ComponentRegistrar::MODULE, + 'Magento_MediaGalleryRenditionsApi', + __DIR__ +); diff --git a/composer.json b/composer.json index 1af86e438882c..57fbfaaa35c2b 100644 --- a/composer.json +++ b/composer.json @@ -222,6 +222,8 @@ "magento/module-media-gallery-cms-ui": "*", "magento/module-media-gallery-catalog-integration": "*", "magento/module-media-gallery-catalog": "*", + "magento/module-media-gallery-renditions": "*", + "magento/module-media-gallery-renditions-api": "*", "magento/module-media-storage": "*", "magento/module-message-queue": "*", "magento/module-msrp": "*", diff --git a/composer.lock b/composer.lock index 551167152be4d..8a5d82536cee4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aadcf8a265dd7ecbb86dd3dd4e49bc28", + "content-hash": "a03edc1c8ee05f82886eebd6ed288df8", "packages": [ { "name": "colinmollenhour/cache-backend-file", From 2dea62c1c36f85ed8cd1a098d01366295cd15797 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Thu, 3 Sep 2020 17:01:54 +0300 Subject: [PATCH 112/195] MC-33493: Test render fields in composite configure block for simple and configurable product --- .../Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php | 2 +- .../Product/View/Options/AbstractRenderCustomOptionsTest.php | 2 +- .../Catalog/Block/Product/View/Options/RenderOptionsTest.php | 1 - .../Block/Product/View/CustomOptions/RenderOptionsTest.php | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php index ddf22614abaa4..36a7d37ecbd2b 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php @@ -68,7 +68,7 @@ public function testRenderCustomOptionsWithoutOptions(): void public function testRenderCustomOptionsFromTextGroup(array $optionData, array $checkArray): void { $this->assertTextOptionRenderingOnProduct('simple', $optionData, $checkArray); - }//test bez opcij + } /** * Provides test data to verify the display of text type options. diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php index 6e4df5dbacecb..553130deddbee 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php @@ -57,7 +57,7 @@ abstract class AbstractRenderCustomOptionsTest extends TestCase protected function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); - $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $this->productCustomOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); $this->productCustomOptionValuesFactory = $this->objectManager->get( ProductCustomOptionValuesInterfaceFactory::class diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php index fa05e5ece10e3..89257dbd010a8 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php @@ -12,7 +12,6 @@ /** * Test cases related to check that simple product custom option renders as expected. * - * @magentoDbIsolation disabled * @magentoAppArea frontend */ class RenderOptionsTest extends AbstractRenderCustomOptionsTest diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php index 8bac488718217..b38f6a4b6f0a7 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php @@ -13,7 +13,6 @@ /** * Test cases related to check that configurable product custom option renders as expected. * - * @magentoDbIsolation disabled * @magentoAppArea frontend */ class RenderOptionsTest extends AbstractRenderCustomOptionsTest From c889371002a38db883eb416dcd785e02b055c379 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Thu, 3 Sep 2020 22:19:46 +0800 Subject: [PATCH 113/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - updated PR with requested changes and fixed failed integration test --- ...talogTest.php => SynchronizeIdentitiesTest.php} | 13 ++++--------- ...esCmsTest.php => SynchronizeIdentitiesTest.php} | 14 ++++---------- 2 files changed, 8 insertions(+), 19 deletions(-) rename app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/{SynchronizeIdentitiesCatalogTest.php => SynchronizeIdentitiesTest.php} (93%) rename app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/{SynchronizeIdentitiesCmsTest.php => SynchronizeIdentitiesTest.php} (91%) diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php b/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php similarity index 93% rename from app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php rename to app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php index 4a1ba1445ffe5..5be72e2b4bf60 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCatalogTest.php +++ b/app/code/Magento/MediaContentSynchronizationCatalog/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php @@ -13,14 +13,13 @@ use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; /** * Test for catalog SynchronizeIdentities. */ -class SynchronizeIdentitiesCatalogTest extends TestCase +class SynchronizeIdentitiesTest extends TestCase { private const ENTITY_TYPE = 'entityType'; private const ENTITY_ID = 'entityId'; @@ -46,17 +45,11 @@ class SynchronizeIdentitiesCatalogTest extends TestCase */ private $synchronizeIdentities; - /** - * @var SynchronizeInterface - */ - private $synchronize; - protected function setUp(): void { $this->contentIdentityFactory = Bootstrap::getObjectManager()->get(ContentIdentityInterfaceFactory::class); $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); $this->synchronizeIdentities = Bootstrap::getObjectManager()->get(SynchronizeIdentitiesInterface::class); - $this->synchronize = Bootstrap::getObjectManager()->get(SynchronizeInterface::class); $this->getContentIdentities = Bootstrap::getObjectManager()->get(GetContentByAssetIdsInterface::class); } @@ -70,6 +63,8 @@ protected function setUp(): void */ public function testExecute(array $mediaContentIdentities): void { + $assetId = 2020; + $contentIdentities = []; foreach ($mediaContentIdentities as $mediaContentIdentity) { $contentIdentities[] = $this->contentIdentityFactory->create( @@ -82,9 +77,9 @@ public function testExecute(array $mediaContentIdentities): void } $this->assertNotEmpty($contentIdentities); + $this->assertEmpty($this->getContentIdentities->execute([$assetId])); $this->synchronizeIdentities->execute($contentIdentities); - $assetId = 2020; $entityIds = []; foreach ($contentIdentities as $contentIdentity) { $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); diff --git a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php similarity index 91% rename from app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php rename to app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php index bdd8bfd1105d3..bde43f5477b8b 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesCmsTest.php +++ b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php @@ -13,14 +13,13 @@ use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -use Magento\MediaContentSynchronizationApi\Api\SynchronizeInterface; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; /** * Test for CMS SynchronizeIdentities. */ -class SynchronizeIdentitiesCmsTest extends TestCase +class SynchronizeIdentitiesTest extends TestCase { private const ENTITY_TYPE = 'entityType'; private const ENTITY_ID = 'entityId'; @@ -46,17 +45,11 @@ class SynchronizeIdentitiesCmsTest extends TestCase */ private $synchronizeIdentities; - /** - * @var SynchronizeInterface - */ - private $synchronize; - protected function setUp(): void { $this->contentIdentityFactory = Bootstrap::getObjectManager()->get(ContentIdentityInterfaceFactory::class); $this->getAssetIds = Bootstrap::getObjectManager()->get(GetAssetIdsByContentIdentityInterface::class); $this->synchronizeIdentities = Bootstrap::getObjectManager()->get(SynchronizeIdentitiesInterface::class); - $this->synchronize = Bootstrap::getObjectManager()->get(SynchronizeInterface::class); $this->getContentIdentities = Bootstrap::getObjectManager()->get(GetContentByAssetIdsInterface::class); } @@ -70,6 +63,8 @@ protected function setUp(): void */ public function testExecute(array $mediaContentIdentities): void { + $assetId = 2020; + $contentIdentities = []; foreach ($mediaContentIdentities as $mediaContentIdentity) { $contentIdentities[] = $this->contentIdentityFactory->create( @@ -82,12 +77,11 @@ public function testExecute(array $mediaContentIdentities): void } $this->assertNotEmpty($contentIdentities); + $this->assertEmpty($this->getContentIdentities->execute([$assetId])); $this->synchronizeIdentities->execute($contentIdentities); - $assetId = 2020; $entityIds = []; foreach ($contentIdentities as $contentIdentity) { - $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); $entityIds[] = $contentIdentity->getEntityId(); } From 739e6dc64cff5b7c90f219c103a7469f3dbcf071 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Sep 2020 15:28:27 +0100 Subject: [PATCH 114/195] magento/magento2#29715: Moved ACL to MediaGalleryUiApi --- app/code/Magento/MediaGalleryApi/etc/acl.xml | 26 ------------------ .../Controller/Adminhtml/Category/Index.php | 2 +- .../media_gallery_category_listing.xml | 2 +- .../Controller/Adminhtml/Asset/Search.php | 2 +- .../Adminhtml/Directories/Create.php | 2 +- .../Adminhtml/Directories/Delete.php | 2 +- .../Adminhtml/Directories/GetTree.php | 2 +- .../Controller/Adminhtml/Image/Delete.php | 2 +- .../Controller/Adminhtml/Image/Details.php | 2 +- .../Adminhtml/Image/SaveDetails.php | 2 +- .../Controller/Adminhtml/Image/Upload.php | 2 +- .../Controller/Adminhtml/Index/Index.php | 2 +- .../Controller/Adminhtml/Media/Index.php | 2 +- .../Ui/Component/Control/CreateFolder.php | 2 +- .../Ui/Component/Control/DeleteAssets.php | 2 +- .../Ui/Component/Control/DeleteFolder.php | 2 +- .../Ui/Component/Control/InsertAsstes.php | 2 +- .../Ui/Component/Control/UploadAssets.php | 2 +- .../Ui/Component/DirectoryTree.php | 2 +- .../Ui/Component/Listing/Columns/Url.php | 4 +-- .../Listing/Massactions/Massaction.php | 2 +- .../MediaGalleryUi/etc/adminhtml/menu.xml | 4 +-- .../layout/media_gallery_index_index.xml | 2 +- .../ui_component/media_gallery_listing.xml | 2 +- .../standalone_media_gallery_listing.xml | 2 +- .../Magento/MediaGalleryUiApi/composer.json | 3 ++- .../Magento/MediaGalleryUiApi/etc/acl.xml | 27 +++++++++++++++++++ 27 files changed, 55 insertions(+), 53 deletions(-) delete mode 100644 app/code/Magento/MediaGalleryApi/etc/acl.xml create mode 100644 app/code/Magento/MediaGalleryUiApi/etc/acl.xml diff --git a/app/code/Magento/MediaGalleryApi/etc/acl.xml b/app/code/Magento/MediaGalleryApi/etc/acl.xml deleted file mode 100644 index 50f7114d1f3be..0000000000000 --- a/app/code/Magento/MediaGalleryApi/etc/acl.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?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:Acl/etc/acl.xsd"> - <acl> - <resources> - <resource id="Magento_Backend::admin"> - <resource id="Magento_Backend::content"> - <resource id="Magento_Backend::content_elements"> - <resource id="Magento_MediaGalleryApi::media_gallery" title="Media Gallery" translate="title"> - <resource id="Magento_MediaGalleryApi::upload_assets" title="Upload Assets" translate="title" sortOrder="80"/> - <resource id="Magento_MediaGalleryApi::delete_assets" title="Delete Assets" translate="title" sortOrder="70"/> - <resource id="Magento_MediaGalleryApi::insert_assets" title="Insert Assets into the content" translate="title" sortOrder="60"/> - <resource id="Magento_MediaGalleryApi::create_folder" title="Create Folder" translate="title" sortOrder="50"/> - <resource id="Magento_MediaGalleryApi::delete_folder" title="Delete Folder" translate="title" sortOrder="40"/> - </resource> - </resource> - </resource> - </resource> - </resources> - </acl> -</config> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php b/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php index d2fb90f3bccff..a541e9999b784 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php +++ b/app/code/Magento/MediaGalleryCatalogUi/Controller/Adminhtml/Category/Index.php @@ -18,7 +18,7 @@ */ class Index extends Action implements HttpGetActionInterface { - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** * Get the media gallery layout diff --git a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml index e289c19fe6219..e12d90b95303b 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/view/adminhtml/ui_component/media_gallery_category_listing.xml @@ -27,7 +27,7 @@ </storageConfig> <updateUrl path="mui/index/render"/> </settings> - <aclResource>Magento_MediaGalleryApi::media_gallery</aclResource> + <aclResource>Magento_Cms::media_gallery</aclResource> <dataProvider class="Magento\MediaGalleryCatalogUi\Model\Listing\DataProvider" name="media_gallery_category_listing_data_source"> <settings> <requestFieldName>entity_id</requestFieldName> diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php index 6aaa7d44cb508..9b6c08edbc86d 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Asset/Search.php @@ -34,7 +34,7 @@ class Search extends Action implements HttpGetActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** * @var SearchAssetsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php index f65f9d622aeba..76c00927b33e0 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Create.php @@ -29,7 +29,7 @@ class Create extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::create_folder'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryUiApi::create_folder'; /** * @var CreateDirectoriesByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php index bda891f93e907..3dc43e5276860 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/Delete.php @@ -30,7 +30,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::delete_folder'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryUiApi::delete_folder'; /** * @var DeleteAssetsByPathsInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php index 0872685444193..d4885cae055dd 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Directories/GetTree.php @@ -25,7 +25,7 @@ class GetTree extends Action implements HttpGetActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** * @var LoggerInterface diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php index 0e91d85ed6a3a..2f7766c590033 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Delete.php @@ -31,7 +31,7 @@ class Delete extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::delete_assets'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryUiApi::delete_assets'; /** * @var DeleteImage diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php index 3929ad8268f4e..d959a070148ed 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Details.php @@ -29,7 +29,7 @@ class Details extends Action implements HttpGetActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** * @var GetDetailsByAssetId diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php index c4d2d3f07bed2..87a2e7345c407 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/SaveDetails.php @@ -32,7 +32,7 @@ class SaveDetails extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryUiApi::edit_assets'; /** * @var UpdateAsset diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php index 902a253a78461..4492595bbe6ee 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Image/Upload.php @@ -28,7 +28,7 @@ class Upload extends Action implements HttpPostActionInterface /** * @see _isAllowed() */ - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::upload_assets'; + public const ADMIN_RESOURCE = 'Magento_MediaGalleryUiApi::upload_assets'; /** * @var UploadImage diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php index 4e0544a2870b9..e97d93d86bb0d 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Index/Index.php @@ -18,7 +18,7 @@ */ class Index extends Action implements HttpGetActionInterface { - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** * @var LayoutFactory diff --git a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php index 6b297a618e517..8c5b3d4d3a9ac 100644 --- a/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php +++ b/app/code/Magento/MediaGalleryUi/Controller/Adminhtml/Media/Index.php @@ -21,7 +21,7 @@ */ class Index extends Action implements HttpGetActionInterface { - public const ADMIN_RESOURCE = 'Magento_MediaGalleryApi::media_gallery'; + public const ADMIN_RESOURCE = 'Magento_Cms::media_gallery'; /** * @var Config diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php index 1d71fa9892275..039a1006c79e5 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/CreateFolder.php @@ -13,7 +13,7 @@ */ class CreateFolder implements ButtonProviderInterface { - private const ACL_CREATE_FOLDER = 'Magento_MediaGalleryApi::create_folder'; + private const ACL_CREATE_FOLDER = 'Magento_MediaGalleryUiApi::create_folder'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php index a2078513c0730..10604d65f768f 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteAssets.php @@ -13,7 +13,7 @@ */ class DeleteAssets implements ButtonProviderInterface { - private const ACL_DELETE_ASSETS= 'Magento_MediaGalleryApi::delete_assets'; + private const ACL_DELETE_ASSETS= 'Magento_MediaGalleryUiApi::delete_assets'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php index 3acafb2ae9c27..cb803c1c663e0 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/DeleteFolder.php @@ -13,7 +13,7 @@ */ class DeleteFolder implements ButtonProviderInterface { - private const ACL_DELETE_FOLDER = 'Magento_MediaGalleryApi::delete_folder'; + private const ACL_DELETE_FOLDER = 'Magento_MediaGalleryUiApi::delete_folder'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php index ee04e7e8a5837..6854b79ba2c36 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/InsertAsstes.php @@ -13,7 +13,7 @@ */ class InsertAsstes implements ButtonProviderInterface { - private const ACL_INSERT_ASSETS = 'Magento_MediaGalleryApi::insert_assets'; + private const ACL_INSERT_ASSETS = 'Magento_MediaGalleryUiApi::insert_assets'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php index 440695a367305..32bbdba88a599 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Control/UploadAssets.php @@ -13,7 +13,7 @@ */ class UploadAssets implements ButtonProviderInterface { - private const ACL_UPLOAD_ASSETS= 'Magento_MediaGalleryApi::upload_assets'; + private const ACL_UPLOAD_ASSETS= 'Magento_MediaGalleryUiApi::upload_assets'; /** * @var AuthorizationInterface diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php index d42af8ef609ab..0ad5ad43f6157 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/DirectoryTree.php @@ -18,7 +18,7 @@ class DirectoryTree extends Container { private const ACL_IMAGE_ACTIONS = [ - 'delete_folder' => 'Magento_MediaGalleryApi::delete_folder' + 'delete_folder' => 'Magento_MediaGalleryUiApi::delete_folder' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php index ffbd726cd5f47..05c82cf1b972c 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php @@ -23,8 +23,8 @@ class Url extends Column { private const ACL_IMAGE_ACTIONS = [ - 'insert_assets' => 'Magento_MediaGalleryApi::insert_assets', - 'delete_assets' => 'Magento_MediaGalleryApi::delete_assets' + 'insert_assets' => 'Magento_MediaGalleryUiApi::insert_assets', + 'delete_assets' => 'Magento_MediaGalleryUiApi::delete_assets' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php index e17048462c684..7d7b67125df96 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Massactions/Massaction.php @@ -17,7 +17,7 @@ class Massaction extends Container { private const ACL_IMAGE_ACTIONS = [ - 'delete_assets' => 'Magento_MediaGalleryApi::delete_assets' + 'delete_assets' => 'Magento_MediaGalleryUiApi::delete_assets' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml b/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml index afa73373bd407..92839aa75ac8b 100644 --- a/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml +++ b/app/code/Magento/MediaGalleryUi/etc/adminhtml/menu.xml @@ -7,7 +7,7 @@ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> - <add id="Magento_MediaGalleryUi::media" title="Media" translate="title" module="Magento_MediaGalleryUi" sortOrder="15" parent="Magento_Backend::content" resource="Magento_MediaGalleryApi::media_gallery" dependsOnConfig="system/media_gallery/enabled"/> - <add id="Magento_MediaGalleryUi::media_gallery" title="Media Gallery" translate="title" module="Magento_MediaGalleryUi" sortOrder="0" parent="Magento_MediaGalleryUi::media" action="media_gallery/media/index" resource="Magento_MediaGalleryApi::media_gallery"/> + <add id="Magento_MediaGalleryUi::media" title="Media" translate="title" module="Magento_MediaGalleryUi" sortOrder="15" parent="Magento_Backend::content" resource="Magento_Cms::media_gallery" dependsOnConfig="system/media_gallery/enabled"/> + <add id="Magento_MediaGalleryUi::media_gallery" title="Media Gallery" translate="title" module="Magento_MediaGalleryUi" sortOrder="0" parent="Magento_MediaGalleryUi::media" action="media_gallery/media/index" resource="Magento_Cms::media_gallery"/> </menu> </config> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml index fa03c477ec9a2..f41c0f91b2249 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml @@ -11,7 +11,7 @@ <block name="media.gallery.container" class="Magento\Backend\Block\Template" template="Magento_MediaGalleryUi::container.phtml" - aclResource="Magento_MediaGalleryApi::media_gallery"> + aclResource="Magento_Cms::media_gallery"> <container name="gallery.actions" htmlTag="div" htmlClass="page-main-actions"> <block name="page.actions.toolbar" template="Magento_Backend::pageactions.phtml"/> </container> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml index 51c1dc21b016e..b7307f9a74fae 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/media_gallery_listing.xml @@ -40,7 +40,7 @@ </storageConfig> <updateUrl path="mui/index/render"/> </settings> - <aclResource>Magento_MediaGalleryApi::media_gallery</aclResource> + <aclResource>Magento_Cms::media_gallery</aclResource> <dataProvider class="Magento\MediaGalleryUi\Model\Listing\DataProvider" name="media_gallery_listing_data_source"> <settings> <requestFieldName>id</requestFieldName> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml index c1086ad891495..a53a46c61f75d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/standalone_media_gallery_listing.xml @@ -33,7 +33,7 @@ </storageConfig> <updateUrl path="mui/index/render"/> </settings> - <aclResource>Magento_MediaGalleryApi::media_gallery</aclResource> + <aclResource>Magento_Cms::media_gallery</aclResource> <dataProvider class="Magento\MediaGalleryUi\Model\Listing\DataProvider" name="media_gallery_listing_data_source"> <settings> <requestFieldName>id</requestFieldName> diff --git a/app/code/Magento/MediaGalleryUiApi/composer.json b/app/code/Magento/MediaGalleryUiApi/composer.json index f8d5ef11058c1..b3b3cc5092cab 100644 --- a/app/code/Magento/MediaGalleryUiApi/composer.json +++ b/app/code/Magento/MediaGalleryUiApi/composer.json @@ -3,7 +3,8 @@ "description": "Magento module responsible for the media gallery UI implementation API", "require": { "php": "~7.3.0||~7.4.0", - "magento/framework": "*" + "magento/framework": "*", + "magento/module-cms": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/MediaGalleryUiApi/etc/acl.xml b/app/code/Magento/MediaGalleryUiApi/etc/acl.xml new file mode 100644 index 0000000000000..c496c57d51322 --- /dev/null +++ b/app/code/Magento/MediaGalleryUiApi/etc/acl.xml @@ -0,0 +1,27 @@ +<?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:Acl/etc/acl.xsd"> + <acl> + <resources> + <resource id="Magento_Backend::admin"> + <resource id="Magento_Backend::content"> + <resource id="Magento_Backend::content_elements"> + <resource id="Magento_Cms::media_gallery" title="Media Gallery" translate="title"> + <resource id="Magento_MediaGalleryUiApi::insert_assets" title="Insert assets into the content" translate="title" sortOrder="40"/> + <resource id="Magento_MediaGalleryUiApi::upload_assets" title="Upload assets" translate="title" sortOrder="50"/> + <resource id="Magento_MediaGalleryUiApi::edit_assets" title="Edit asset details" translate="title" sortOrder="60"/> + <resource id="Magento_MediaGalleryUiApi::delete_assets" title="Delete assets" translate="title" sortOrder="70"/> + <resource id="Magento_MediaGalleryUiApi::create_folder" title="Create folder" translate="title" sortOrder="80"/> + <resource id="Magento_MediaGalleryUiApi::delete_folder" title="Delete folder" translate="title" sortOrder="90"/> + </resource> + </resource> + </resource> + </resource> + </resources> + </acl> +</config> From 96ea4d5f260113b010f385b22548410ee57e95f6 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 4 Sep 2020 00:18:12 +0800 Subject: [PATCH 115/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - unskip test --- ...ediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml index 8191d5570f1e8..36df5bf736d1b 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml @@ -9,9 +9,6 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest"> <annotations> - <skip> - <issueId value="https://github.com/magento/adobe-stock-integration/issues/1794"/> - </skip> <features value="AdminMediaGalleryCategoryGrid"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1503"/> <title value="User can open each entity the asset is associated with in a separate tab to manage association"/> @@ -84,9 +81,8 @@ <deleteData createDataKey="category" stepKey="deleteCategory"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openMediaGallery"/> - <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetailsToVerfifyEmptyUsedIn"/> + <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetailsToVerifyEmptyUsedIn"/> <actionGroup ref="AssertAdminEnhancedMediaGalleryUsedInSectionNotDisplayedActionGroup" stepKey="assertThereIsNoUsedInSection"/> <actionGroup ref="AdminEnhancedMediaGalleryCloseViewDetailsActionGroup" stepKey="closeDetails"/> - </test> </tests> From 68bc010fc93a332082cfee97b700fa689161dd84 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Sep 2020 17:54:34 +0100 Subject: [PATCH 116/195] magento/magento2#29715: Enforced ACL for context menu and view details --- .../Block/Adminhtml/ImageDetails.php | 98 +++++++++++++++++++ .../Ui/Component/Listing/Columns/Url.php | 6 +- .../layout/media_gallery_index_index.xml | 2 +- .../layout/media_gallery_media_index.xml | 2 +- .../adminhtml/templates/image_details.phtml | 31 +----- .../templates/image_details_standalone.phtml | 25 +---- .../adminhtml/web/js/grid/columns/image.js | 4 +- .../web/js/grid/columns/image/actions.js | 6 +- 8 files changed, 116 insertions(+), 58 deletions(-) create mode 100644 app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php diff --git a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php new file mode 100644 index 0000000000000..94a2b3cf1047c --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryUi\Block\Adminhtml; + +use Magento\Backend\Block\Template; +use Magento\Directory\Helper\Data as DirectoryHelper; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Json\Helper\Data as JsonHelper; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Image details block + * + * @api + */ +class ImageDetails extends Template +{ + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var Json + */ + private $json; + + /** + * @param AuthorizationInterface $authorization + * @param Template\Context $context + * @param array $data + * @param JsonHelper|null $jsonHelper + * @param DirectoryHelper|null $directoryHelper + */ + public function __construct( + AuthorizationInterface $authorization, + Json $json, + Template\Context $context, + array $data = [], + ?JsonHelper $jsonHelper = null, + ?DirectoryHelper $directoryHelper = null + ) { + $this->authorization = $authorization; + $this->json = $json; + parent::__construct($context, $data, $jsonHelper, $directoryHelper); + } + + /** + * Retrieve actions json + * + * @return string + */ + public function getActionsJson(): string + { + $actions = [ + [ + 'title' => __('Cancel'), + 'handler' => 'closeModal', + 'name' => 'cancel', + 'classes' => 'action-default scalable cancel action-quaternary' + ] + ]; + + if ($this->authorization->isAllowed('MediaGalleryUiApi::edit_assets')) { + $actions[] = [ + 'title' => __('Edit Details'), + 'handler' => 'editImageAction', + 'name' => 'edit', + 'classes' => 'action-default scalable edit action-quaternary' + ]; + } + + if ($this->authorization->isAllowed('MediaGalleryUiApi::delete_assets')) { + $actions[] = [ + 'title' => __('Delete Image'), + 'handler' => 'deleteImageAction', + 'name' => 'delete', + 'classes' => 'action-default scalable delete action-quaternary' + ]; + } + + if ($this->authorization->isAllowed('MediaGalleryUiApi::insert_assets')) { + $actions[] = [ + 'title' => __('Add Image'), + 'handler' => 'addImage', + 'name' => 'add-image', + 'classes' => 'scalable action-primary add-image-action' + ]; + } + + return $this->json->serialize($actions); + } +} diff --git a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php index 05c82cf1b972c..0d48a0d0ff0e1 100644 --- a/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php +++ b/app/code/Magento/MediaGalleryUi/Ui/Component/Listing/Columns/Url.php @@ -23,8 +23,10 @@ class Url extends Column { private const ACL_IMAGE_ACTIONS = [ - 'insert_assets' => 'Magento_MediaGalleryUiApi::insert_assets', - 'delete_assets' => 'Magento_MediaGalleryUiApi::delete_assets' + 'image-details' => 'Magento_Cms::media_gallery', + 'insert' => 'Magento_MediaGalleryUiApi::insert_assets', + 'delete' => 'Magento_MediaGalleryUiApi::delete_assets', + 'edit' => 'Magento_MediaGalleryUiApi::edit_assets' ]; /** diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml index f41c0f91b2249..a5eb247bd344f 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_index_index.xml @@ -16,7 +16,7 @@ <block name="page.actions.toolbar" template="Magento_Backend::pageactions.phtml"/> </container> <uiComponent name="media_gallery_listing"/> - <block name="image.details" class="Magento\Backend\Block\Template" template="Magento_MediaGalleryUi::image_details.phtml"> + <block name="image.details" class="Magento\MediaGalleryUi\Block\Adminhtml\ImageDetails" template="Magento_MediaGalleryUi::image_details.phtml"> <arguments> <argument name="imageDetailsUrl" xsi:type="url" path="media_gallery/image/details"/> </arguments> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml index 7750f22b39ce7..7697519c40f4d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml @@ -10,7 +10,7 @@ <body> <referenceContainer htmlTag="div" htmlClass="media-gallery-container" name="content"> <uiComponent name="standalone_media_gallery_listing"/> - <block name="image.details" class="Magento\Backend\Block\Template" template="Magento_MediaGalleryUi::image_details_standalone.phtml"> + <block name="image.details" class="Magento\MediaGalleryUi\Block\Adminhtml\ImageDetails" template="Magento_MediaGalleryUi::image_details_standalone.phtml"> <arguments> <argument name="imageDetailsUrl" xsi:type="url" path="media_gallery/image/details"/> </arguments> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml index 67733b2c6855d..a547f33adbbdb 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml @@ -4,11 +4,11 @@ * See COPYING.txt for license details. */ -use Magento\Backend\Block\Template; +use Magento\MediaGalleryUi\Block\Adminhtml\ImageDetails; use Magento\Framework\Escaper; // phpcs:disable Magento2.Files.LineLength, Generic.Files.LineLength -/** @var Template $block */ +/** @var ImageDetails $block */ /** @var Escaper $escaper */ ?> @@ -73,32 +73,7 @@ use Magento\Framework\Escaper; "modalWindowSelector": ".media-gallery-image-details", "imageModelName" : "media_gallery_listing.media_gallery_listing.media_gallery_columns.thumbnail_url", "mediaGalleryImageDetailsName": "mediaGalleryImageDetails", - "actionsList": [ - { - "title": "<?= $escaper->escapeJs(__('Edit Details')); ?>", - "handler": "editImageAction", - "name": "edit", - "classes": "action-default scalable edit action-quaternary" - }, - { - "title": "<?= $escaper->escapeJs(__('Cancel')); ?>", - "handler": "closeModal", - "name": "cancel", - "classes": "action-default scalable cancel action-quaternary" - }, - { - "title": "<?= $escaper->escapeJs(__('Delete Image')); ?>", - "handler": "deleteImageAction", - "name": "delete", - "classes": "action-default scalable delete action-quaternary" - }, - { - "title": "<?= $escaper->escapeJs(__('Add Image')); ?>", - "handler": "addImage", - "name": "add-image", - "classes": "scalable action-primary add-image-action" - } - ] + "actionsList": <?= $block->getActionsJson() ?> } } } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml index f0b653cb8cd14..b4c80bf6d4196 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml @@ -4,10 +4,8 @@ * See COPYING.txt for license details. */ -use Magento\Backend\Block\Template; - // phpcs:disable Magento2.Files.LineLength, Generic.Files.LineLength -/** @var Template $block */ +/** @var \Magento\MediaGalleryUi\Block\Adminhtml\ImageDetails $block */ /** @var \Magento\Framework\Escaper $escaper */ ?> @@ -71,26 +69,7 @@ use Magento\Backend\Block\Template; "modalWindowSelector": ".media-gallery-image-details", "mediaGalleryImageDetailsName": "mediaGalleryImageDetails", "imageModelName" : "standalone_media_gallery_listing.standalone_media_gallery_listing.media_gallery_columns.thumbnail_url", - "actionsList": [ - { - "title": "<?= $escaper->escapeJs(__('Edit Details')); ?>", - "handler": "editImageAction", - "name": "edit", - "classes": "action-default scalable edit action-quaternary" - }, - { - "title": "<?= $escaper->escapeJs(__('Cancel')); ?>", - "handler": "closeModal", - "name": "cancel", - "classes": "action-default scalable cancel action-quaternary" - }, - { - "title": "<?= $escaper->escapeJs(__('Delete Image')); ?>", - "handler": "deleteImageAction", - "name": "delete", - "classes": "action-default scalable delete action-quaternary" - } - ] + "actionsList": <?= $block->getActionsJson() ?> } } } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js index ca731e693bd2d..c2a16170d58b8 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js @@ -228,11 +228,11 @@ define([ return; } - if (this.allowedActions.includes('insert_assets')) { + if (this.allowedActions.includes('insert')) { $(this.addSelectedBtnSelector).removeClass('no-display'); } - if (this.allowedActions.includes('delete_assets')) { + if (this.allowedActions.includes('delete')) { $(this.deleteSelectedBtnSelector).removeClass('no-display'); } }, diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js index ec959a06e1cce..10b94632d6745 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js @@ -55,7 +55,11 @@ define([ this._super(); this.initEvents(); - if (!this.allowedActions.includes('delete_assets')) { + this.actionsList = this.actionsList.filter(function(item) { + return this.allowedActions.includes(item.name); + }.bind(this)); + + if (!this.allowedActions.includes('delete')) { $.async('.media-gallery-delete-assets', function () { $('.media-gallery-delete-assets').unbind('click').addClass('action-disabled'); }); From 0ad0630ee1c426be5468fa223ecd012f06d339b1 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Sep 2020 18:03:04 +0100 Subject: [PATCH 117/195] magento/magento2#29715: Corrected ACL resource names --- .../Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php index 94a2b3cf1047c..9f67ee5578e29 100644 --- a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php +++ b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php @@ -66,7 +66,7 @@ public function getActionsJson(): string ] ]; - if ($this->authorization->isAllowed('MediaGalleryUiApi::edit_assets')) { + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::edit_assets')) { $actions[] = [ 'title' => __('Edit Details'), 'handler' => 'editImageAction', @@ -75,7 +75,7 @@ public function getActionsJson(): string ]; } - if ($this->authorization->isAllowed('MediaGalleryUiApi::delete_assets')) { + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::delete_assets')) { $actions[] = [ 'title' => __('Delete Image'), 'handler' => 'deleteImageAction', @@ -84,7 +84,7 @@ public function getActionsJson(): string ]; } - if ($this->authorization->isAllowed('MediaGalleryUiApi::insert_assets')) { + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::insert_assets')) { $actions[] = [ 'title' => __('Add Image'), 'handler' => 'addImage', From 9ccfdc9ca4eb2f20ad9ac1b40f999f3f302c9951 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 3 Sep 2020 18:29:22 +0100 Subject: [PATCH 118/195] magento/magento2#29715: Added data upgrade script --- .../Patch/Data/AddMediaGalleryPermissions.php | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php diff --git a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php new file mode 100644 index 0000000000000..60ffb2147bab6 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php @@ -0,0 +1,106 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryUi\Setup\Patch\Data; + +use Magento\Framework\Setup\Patch\PatchVersionInterface; +use Magento\Framework\Setup\Patch\DataPatchInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; + +/** + * Patch is mechanism, that allows to do atomic upgrade data changes + */ +class AddMediaGalleryPermissions implements + DataPatchInterface, + PatchVersionInterface +{ + /** + * @var ModuleDataSetupInterface $moduleDataSetup + */ + private $moduleDataSetup; + + /** + * @param ModuleDataSetupInterface $moduleDataSetup + */ + public function __construct(ModuleDataSetupInterface $moduleDataSetup) + { + $this->moduleDataSetup = $moduleDataSetup; + } + + /** + * Add child resources permissions for user roles with Magento_Cms::media_gallery permission + */ + public function apply(): void + { + $tableName = $this->moduleDataSetup->getTable('authorization_rule'); + $connection = $this->moduleDataSetup->getConnection(); + + if (!$tableName) { + return; + } + + $select = $connection->select() + ->from($tableName, ['role_id']) + ->where('resource_id = "Magento_Cms::media_gallery"'); + + $connection->insertMultiple($tableName, $this->getInsertData($connection->fetchCol($select))); + } + + /** + * Retrieve data to insert to authorization_rule table based on role ids + * + * @param array $roleIds + * @return array + */ + private function getInsertData(array $roleIds): array + { + $newResources = [ + 'Magento_MediaGalleryUiApi::insert_assets', + 'Magento_MediaGalleryUiApi::upload_assets', + 'Magento_MediaGalleryUiApi::edit_assets', + 'Magento_MediaGalleryUiApi::delete_assets', + 'Magento_MediaGalleryUiApi::create_folder', + 'Magento_MediaGalleryUiApi::delete_folder' + ]; + + $data = []; + + foreach ($roleIds as $roleId) { + foreach ($newResources as $resourceId) { + $data[] = [ + 'role_id' => $roleId, + 'resource_id' => $resourceId, + 'permission' => 'allow' + ]; + } + } + } + + /** + * @inheritdoc + */ + public function getAliases() + { + return []; + } + + /** + * @inheritdoc + */ + public static function getDependencies() + { + return []; + } + + /** + * @inheritdoc + */ + public static function getVersion() + { + return '2.4.2'; + } +} From 6bc5a8a1c029b7d3802e5f3a4411815a8e3d16e5 Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Fri, 4 Sep 2020 02:39:13 +0800 Subject: [PATCH 119/195] magento/adobe-stock-integration#1724: Support batches processing for synchronization queue messages - fixed failed integration test --- .../SynchronizeIdentitiesTest.php | 84 ++++++++++++++----- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php index bde43f5477b8b..825542baaff8c 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php +++ b/app/code/Magento/MediaContentSynchronizationCms/Test/Integration/Model/Synchronizer/SynchronizeIdentitiesTest.php @@ -7,8 +7,13 @@ namespace Magento\MediaContentSynchronizationCms\Test\Integration\Model\Synchronizer; +use Magento\Cms\Api\BlockRepositoryInterface; +use Magento\Cms\Api\Data\BlockInterface; +use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Exception\IntegrationException; -use Magento\MediaContentApi\Api\Data\ContentIdentityInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\MediaContentApi\Api\Data\ContentIdentityInterfaceFactory; use Magento\MediaContentApi\Api\GetAssetIdsByContentIdentityInterface; use Magento\MediaContentApi\Api\GetContentByAssetIdsInterface; @@ -54,16 +59,29 @@ protected function setUp(): void } /** - * @dataProvider filesProvider * @magentoDataFixture Magento/MediaContentCms/_files/page_with_asset.php * @magentoDataFixture Magento/MediaContentCms/_files/block_with_asset.php * @magentoDataFixture Magento/MediaGallery/_files/media_asset.php - * @param ContentIdentityInterface[] $mediaContentIdentities * @throws IntegrationException + * @throws LocalizedException */ - public function testExecute(array $mediaContentIdentities): void + public function testExecute(): void { $assetId = 2020; + $pageId = $this->getPage('fixture_page_with_asset')->getId(); + $blockId = $this->getBlock('fixture_block_with_asset')->getId(); + $mediaContentIdentities = [ + [ + 'entityType' => 'cms_page', + 'field' => 'content', + 'entityId' => $pageId + ], + [ + 'entityType' => 'cms_block', + 'field' => 'content', + 'entityId' => $blockId + ] + ]; $contentIdentities = []; foreach ($mediaContentIdentities as $mediaContentIdentity) { @@ -82,6 +100,7 @@ public function testExecute(array $mediaContentIdentities): void $entityIds = []; foreach ($contentIdentities as $contentIdentity) { + $this->assertEquals([$assetId], $this->getAssetIds->execute($contentIdentity)); $entityIds[] = $contentIdentity->getEntityId(); } @@ -94,27 +113,46 @@ public function testExecute(array $mediaContentIdentities): void } /** - * Data provider + * Get fixture block * - * @return array + * @param string $identifier + * @return BlockInterface + * @throws LocalizedException */ - public function filesProvider(): array + private function getBlock(string $identifier): BlockInterface { - return [ - [ - [ - [ - 'entityType' => 'cms_page', - 'field' => 'content', - 'entityId' => 5 - ], - [ - 'entityType' => 'cms_block', - 'field' => 'content', - 'entityId' => 1 - ] - ] - ] - ]; + $objectManager = Bootstrap::getObjectManager(); + + /** @var BlockRepositoryInterface $blockRepository */ + $blockRepository = $objectManager->get(BlockRepositoryInterface::class); + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter(BlockInterface::IDENTIFIER, $identifier) + ->create(); + + return current($blockRepository->getList($searchCriteria)->getItems()); + } + + /** + * Get fixture page + * + * @param string $identifier + * @return PageInterface + * @throws LocalizedException + */ + private function getPage(string $identifier): PageInterface + { + $objectManager = Bootstrap::getObjectManager(); + + /** @var PageRepositoryInterface $repository */ + $repository = $objectManager->get(PageRepositoryInterface::class); + + /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ + $searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); + $searchCriteria = $searchCriteriaBuilder->addFilter(PageInterface::IDENTIFIER, $identifier) + ->create(); + + return current($repository->getList($searchCriteria)->getItems()); } } From cbca0326509f10f80085055bf67a241436e246b2 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 4 Sep 2020 03:58:35 +0800 Subject: [PATCH 120/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - modified assert image column --- .../AssertAdminCategoryGridPageImageColumnActionGroup.xml | 7 ++++++- .../AdminMediaGalleryCatalogUiCategoryGridSection.xml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageImageColumnActionGroup.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageImageColumnActionGroup.xml index b110ce44a8469..9cd627e900873 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageImageColumnActionGroup.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageImageColumnActionGroup.xml @@ -15,6 +15,11 @@ <description>Assert category grid page image column a specific category</description> </annotations> - <seeElement selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.image(file)}}" stepKey="assertImageColumn"/> + <grabAttributeFrom selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.image}}" userInput="src" + stepKey="getImageSrc"/> + <assertStringContainsString stepKey="assertImageSrc"> + <actualResult type="string">{$getImageSrc}</actualResult> + <expectedResult type="string">{{file}}</expectedResult> + </assertStringContainsString> </actionGroup> </actionGroups> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml index f65ec84bc2ec8..96b4bad5d5add 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml @@ -11,7 +11,7 @@ <section name="AdminMediaGalleryCatalogUiCategoryGridSection"> <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> <element name="activeFilterPlaceholder" type="text" selector="//div[@class='admin__current-filters-list-wrap']//li//span[contains(text(), '{{filterPlaceholder}}')]" parameterized="true"/> - <element name="image" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Image')]/preceding-sibling::th) +1]//img[contains(@src, '{{file}}')]" parameterized="true"/> + <element name="image" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Image')]/preceding-sibling::th) +1]//img"/> <element name="columnValue" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{columnName}}')]/preceding-sibling::th) +1 ]//div" parameterized="true"/> <element name="edit" type="button" selector="//tr[td//text()[contains(., '{{categoryName}}')]]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Action')]/preceding-sibling::th) +1 ]//*[text()='{{actionButton}}']" parameterized="true"/> </section> From 1d82b50123a55291d95b488a60c1f7dfaffc9b95 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Fri, 4 Sep 2020 07:35:48 +0300 Subject: [PATCH 121/195] cover by api --- .../testsuite/Magento/Bundle/Api/ProductServiceTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php b/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php index 7a4f472c69513..538c0b0ee5fac 100644 --- a/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Bundle/Api/ProductServiceTest.php @@ -225,6 +225,7 @@ public function testUpdateBundleAddSelection() public function testUpdateBundleAddAndDeleteOption() { $bundleProduct = $this->createDynamicBundleProduct(); + $linkedProductPrice = 20; $bundleProductOptions = $this->getBundleProductOptions($bundleProduct); @@ -238,7 +239,7 @@ public function testUpdateBundleAddAndDeleteOption() [ 'sku' => 'simple2', 'qty' => 2, - "price" => 20, + "price" => $linkedProductPrice, "price_type" => 1, "is_default" => false, ], @@ -256,6 +257,7 @@ public function testUpdateBundleAddAndDeleteOption() $this->assertFalse(isset($bundleOptions[1])); $this->assertEquals('simple2', $bundleOptions[0]['product_links'][0]['sku']); $this->assertEquals(2, $bundleOptions[0]['product_links'][0]['qty']); + $this->assertEquals($linkedProductPrice, $bundleOptions[0]['product_links'][0]['price']); } /** From ba8797a62f1a10370ab86dbc34975bc007c4d251 Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Fri, 4 Sep 2020 13:58:43 +0300 Subject: [PATCH 122/195] MC-33493: Test render fields in composite configure block for simple and configurable product --- .../Composite/Fieldset/OptionsTest.php | 17 +++++++------ .../Product/Composite/Fieldset/QtyTest.php | 24 +++++++++++++------ .../Product/Composite/FieldsetTest.php | 11 +++++---- .../AbstractRenderCustomOptionsTest.php | 9 +++---- .../View/Options/RenderOptionsTest.php | 4 +--- .../Composite/Fieldset/ConfigurableTest.php | 7 ++++++ .../View/CustomOptions/RenderOptionsTest.php | 3 +-- .../Product/View/Type/ConfigurableTest.php | 7 +++++- 8 files changed, 51 insertions(+), 31 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php index 36a7d37ecbd2b..c50c21a3328ae 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/OptionsTest.php @@ -15,6 +15,7 @@ use Magento\Catalog\Model\Product\Option; use Magento\Catalog\Model\Product\Option\Value; use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; use Magento\TestFramework\Helper\Xpath; /** @@ -27,6 +28,9 @@ class OptionsTest extends AbstractRenderCustomOptionsTest /** @var HelperProduct */ private $helperProduct; + /** @var DataObjectFactory */ + private $dataObjectFactory; + /** * @inheritdoc */ @@ -35,22 +39,21 @@ protected function setUp(): void parent::setUp(); $this->helperProduct = $this->objectManager->get(HelperProduct::class); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); } /** * @magentoDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php - * @throws \Magento\Framework\Exception\NoSuchEntityException * @return void */ public function testRenderCustomOptionsWithoutOptions(): void { $product = $this->productRepository->get('simple'); - $optionHtml = $this->getOptionHtml($product); $this->assertEquals( 0, Xpath::getElementsCountForXpath( "//fieldset[@id='product_composite_configure_fields_options']", - $optionHtml + $this->getOptionHtml($product) ), 'The option block is expected to be empty!' ); @@ -508,7 +511,7 @@ protected function addOptionToProduct( : $optionValueObject->getOptionTypeId(); } /** @var DataObject $request */ - $buyRequest = $this->objectManager->create(DataObject::class); + $buyRequest = $this->dataObjectFactory->create(); $buyRequest->setData([ 'qty' => 1, 'options' => [$option->getId() => $optionValue], @@ -546,7 +549,7 @@ protected function additionalTypeTextAsserts( if (isset($checkArray['equals_xpath'])) { foreach ($checkArray['equals_xpath'] as $key => $value) { - $value['args'] = $key == 'control_price_attribute' ? [(float)$option->getPrice()] : []; + $value['args'] = $key === 'control_price_attribute' ? [(float)$option->getPrice()] : []; $this->assertEqualsXpath($option, $optionHtml, $value); } } @@ -572,12 +575,12 @@ protected function additionalTypeSelectAsserts( /** * @inheritdoc */ - protected function getHandlesList(ProductInterface $product): array + protected function getHandlesList(): array { return [ 'default', 'CATALOG_PRODUCT_COMPOSITE_CONFIGURE', - 'catalog_product_view_type_' . $product->getTypeId(), + 'catalog_product_view_type_simple', ]; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php index b326215688f13..a51b51a73645f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/Fieldset/QtyTest.php @@ -11,6 +11,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Helper\Product as HelperProduct; use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Registry; use Magento\Framework\View\LayoutInterface; @@ -40,17 +41,23 @@ class QtyTest extends TestCase /** @var HelperProduct */ private $helperProduct; + /** @var DataObjectFactory */ + private $dataObjectFactory; + /** * @inheritdoc */ protected function setUp(): void { + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Qty::class); $this->registry = $this->objectManager->get(Registry::class); $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $this->productRepository->cleanCache(); $this->helperProduct = $this->objectManager->get(HelperProduct::class); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); } /** @@ -60,6 +67,8 @@ protected function tearDown(): void { $this->registry->unregister('current_product'); $this->registry->unregister('product'); + + parent::tearDown(); } /** @@ -70,7 +79,11 @@ public function testGetProduct(): void { $product = $this->productRepository->get('simple-1'); $this->registerProduct($product); - $this->assertSame($product, $this->block->getProduct()); + $this->assertEquals( + $product->getId(), + $this->block->getProduct()->getId(), + 'The expected product is missing in the Qty block!' + ); } /** @@ -80,12 +93,12 @@ public function testGetProduct(): void * @param int $qty * @return void */ - public function testGetQtyValue(bool $isQty, int $qty): void + public function testGetQtyValue(bool $isQty = false, int $qty = 1): void { $product = $this->productRepository->get('simple-1'); if ($isQty) { /** @var DataObject $request */ - $buyRequest = $this->objectManager->create(DataObject::class); + $buyRequest = $this->dataObjectFactory->create(); $buyRequest->setData(['qty' => $qty]); $this->helperProduct->prepareProductOptions($product, $buyRequest); } @@ -105,10 +118,7 @@ public function getQtyValueProvider(): array 'is_qty' => true, 'qty' => 5, ], - 'without_qty' => [ - 'is_qty' => false, - 'qty' => 1, - ], + 'without_qty' => [], ]; } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php index 3356d87a92794..ab09314e18cc8 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Adminhtml/Product/Composite/FieldsetTest.php @@ -73,8 +73,10 @@ public function testRenderHtml(): void { $product = $this->productRepository->get('simple'); $this->registerProduct($product); - $this->preparePage($product->getTypeId()); - $html = $this->page->getLayout()->getBlock('product.composite.fieldset')->toHtml(); + $this->preparePage(); + $fieldsetBlock = $this->page->getLayout()->getBlock('product.composite.fieldset'); + $this->assertNotFalse($fieldsetBlock, 'Expected fieldset block is missing!'); + $html = $fieldsetBlock->toHtml(); $this->assertEquals( 1, @@ -91,15 +93,14 @@ public function testRenderHtml(): void /** * Prepare page layout * - * @param string $productType * @return void */ - private function preparePage(string $productType): void + private function preparePage(): void { $this->page->addHandle([ 'default', 'CATALOG_PRODUCT_COMPOSITE_CONFIGURE', - 'catalog_product_view_type_' . $productType, + 'catalog_product_view_type_simple', ]); $this->page->getLayout()->generateXml(); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php index 553130deddbee..44c9ada3cacab 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php @@ -58,6 +58,7 @@ protected function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); $this->productCustomOptionFactory = $this->objectManager->get(ProductCustomOptionInterfaceFactory::class); $this->productCustomOptionValuesFactory = $this->objectManager->get( ProductCustomOptionValuesInterfaceFactory::class @@ -376,6 +377,7 @@ protected function getOptionHtml(ProductInterface $product): string $this->page->getLayout()->generateXml(); /** @var Options $optionsBlock */ $optionsBlock = $this->page->getLayout()->getBlock($this->getOptionsBlockName()); + $this->assertNotFalse($optionsBlock); $optionsBlock->setProduct($product); return $optionsBlock->toHtml(); @@ -426,21 +428,16 @@ protected function findOptionValueByTitle( /** * Return all need handles for load. * - * @param ProductInterface $product * @return array */ - abstract protected function getHandlesList(ProductInterface $product): array; + abstract protected function getHandlesList(): array; /** - * - * * @return string */ abstract protected function getMaxCharactersCssClass(): string; /** - * - * * @return string */ abstract protected function getOptionsBlockName(): string; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php index 89257dbd010a8..83c249ed062e6 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/RenderOptionsTest.php @@ -7,8 +7,6 @@ namespace Magento\Catalog\Block\Product\View\Options; -use Magento\Catalog\Api\Data\ProductInterface; - /** * Test cases related to check that simple product custom option renders as expected. * @@ -83,7 +81,7 @@ public function testRenderCustomOptionsFromDateGroup(array $optionData, array $c /** * @inheritdoc */ - protected function getHandlesList(ProductInterface $product): array + protected function getHandlesList(): array { return [ 'default', diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php index 3ada18a1849c9..88c8fb726c472 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Adminhtml/Product/Composite/Fieldset/ConfigurableTest.php @@ -59,6 +59,8 @@ protected function setUp(): void protected function tearDown(): void { $this->registry->unregister('product'); + + parent::tearDown(); } /** @@ -71,6 +73,11 @@ public function testGetProduct(): void $this->registerProduct($product); $blockProduct = $this->block->getProduct(); $this->assertSame($product, $blockProduct); + $this->assertEquals( + $product->getId(), + $blockProduct->getId(), + 'The expected product is missing in the Configurable block!' + ); $this->assertNotNull($blockProduct->getTypeInstance()->getStoreFilter($blockProduct)); } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php index b38f6a4b6f0a7..303a32d34bf6c 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/CustomOptions/RenderOptionsTest.php @@ -7,7 +7,6 @@ namespace Magento\ConfigurableProduct\Block\Product\View\CustomOptions; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Block\Product\View\Options\AbstractRenderCustomOptionsTest; /** @@ -85,7 +84,7 @@ public function testRenderCustomOptionsFromDateGroup(array $optionData, array $c /** * @inheritdoc */ - protected function getHandlesList(ProductInterface $product): array + protected function getHandlesList(): array { return [ 'default', diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php index 62b691d61efb3..0344d467a3cc2 100644 --- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Block/Product/View/Type/ConfigurableTest.php @@ -15,6 +15,7 @@ use Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\DataObject; +use Magento\Framework\DataObjectFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Framework\View\LayoutInterface; @@ -73,6 +74,9 @@ class ConfigurableTest extends TestCase */ private $helperProduct; + /** @var DataObjectFactory */ + private $dataObjectFactory; + /** * @inheritdoc */ @@ -89,6 +93,7 @@ protected function setUp(): void $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Configurable::class); $this->block->setProduct($this->product); $this->helperProduct = $this->objectManager->get(HelperProduct::class); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); } /** @@ -149,7 +154,7 @@ public function testGetJsonConfigWithPreconfiguredValues(): void $attribute->getAttributeId() => $attribute->getOptions()[0]['value_index'], ]; /** @var DataObject $request */ - $buyRequest = $this->objectManager->create(DataObject::class); + $buyRequest = $this->dataObjectFactory->create(); $buyRequest->setData([ 'qty' => 1, 'super_attribute' => $expectedAttributeValue, From 90c9dac7173cc7755daf94eef37673473c97fed7 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 12:29:25 +0100 Subject: [PATCH 123/195] magento/magento2#29715: Corrected actions order --- .../Block/Adminhtml/ImageDetails.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php index 9f67ee5578e29..e3f9c5bafca22 100644 --- a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php +++ b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php @@ -66,15 +66,6 @@ public function getActionsJson(): string ] ]; - if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::edit_assets')) { - $actions[] = [ - 'title' => __('Edit Details'), - 'handler' => 'editImageAction', - 'name' => 'edit', - 'classes' => 'action-default scalable edit action-quaternary' - ]; - } - if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::delete_assets')) { $actions[] = [ 'title' => __('Delete Image'), @@ -84,6 +75,15 @@ public function getActionsJson(): string ]; } + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::edit_assets')) { + $actions[] = [ + 'title' => __('Edit Details'), + 'handler' => 'editImageAction', + 'name' => 'edit', + 'classes' => 'action-default scalable edit action-quaternary' + ]; + } + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::insert_assets')) { $actions[] = [ 'title' => __('Add Image'), From 2c6953783b2078a7f2a7de39644685e6e2a91e1d Mon Sep 17 00:00:00 2001 From: eduard13 <e.chitoraga@atwix.com> Date: Fri, 4 Sep 2020 14:45:25 +0300 Subject: [PATCH 124/195] Fixing the default value when wishlist item quantity isn't defined --- .../Wishlist/Model/Wishlist/Data/WishlistItemFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php b/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php index aef3cbf571ff6..622f072e8d668 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php +++ b/app/code/Magento/Wishlist/Model/Wishlist/Data/WishlistItemFactory.php @@ -24,7 +24,7 @@ class WishlistItemFactory public function create(array $data): WishlistItem { return new WishlistItem( - $data['quantity'], + $data['quantity'] ?? 0, $data['sku'] ?? null, $data['parent_sku'] ?? null, isset($data['wishlist_item_id']) ? (int) $data['wishlist_item_id'] : null, From 7beb815d9ec7060de67eef855b5709f8597ae5a4 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 12:49:13 +0100 Subject: [PATCH 125/195] magento/magento2#29889: Code review and static tests fixes --- app/code/Magento/MediaGalleryRenditions/Model/Config.php | 1 + .../MediaGalleryRenditions/Model/GenerateRenditions.php | 6 ++++++ .../Model/Queue/FetchRenditionPathsBatches.php | 2 +- .../Model/Queue/UpdateRenditions.php | 2 +- .../MediaGalleryRenditions/Plugin/RemoveRenditions.php | 8 ++++---- .../Plugin/UpdateRenditionsOnConfigChange.php | 2 +- app/code/Magento/MediaGalleryRenditions/composer.json | 2 ++ .../Magento/MediaGalleryRenditions/etc/media_content.xml | 2 +- .../Api/GenerateRenditionsInterface.php | 3 +++ .../Api/GetRenditionPathInterface.php | 3 +++ 10 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Config.php b/app/code/Magento/MediaGalleryRenditions/Model/Config.php index a40fbb41bd831..d1a48904d1f13 100644 --- a/app/code/Magento/MediaGalleryRenditions/Model/Config.php +++ b/app/code/Magento/MediaGalleryRenditions/Model/Config.php @@ -34,6 +34,7 @@ class Config /** * @param ScopeConfigInterface $scopeConfig + * @param ResourceConnection $resourceConnection */ public function __construct( ScopeConfigInterface $scopeConfig, diff --git a/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php b/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php index d1bfcb82a6a84..6bc54fdf9aca4 100644 --- a/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php +++ b/app/code/Magento/MediaGalleryRenditions/Model/GenerateRenditions.php @@ -64,6 +64,8 @@ class GenerateRenditions implements GenerateRenditionsInterface * @param GetRenditionPathInterface $getRenditionPath * @param Filesystem $filesystem * @param File $driver + * @param IsPathExcludedInterface $isPathExcluded + * @param LoggerInterface $log */ public function __construct( AdapterFactory $imageFactory, @@ -121,6 +123,8 @@ public function execute(array $paths): void */ private function generateRendition(string $path): void { + $this->validateAsset($path); + $renditionPath = $this->getRenditionPath->execute($path); $this->createDirectory($renditionPath); @@ -137,6 +141,8 @@ private function generateRendition(string $path): void } /** + * Ensure valid media asset path is provided for renditions generation + * * @param string $path * @throws FileSystemException * @throws LocalizedException diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php b/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php index 9cec7edbbd39a..7263010a8f587 100644 --- a/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php +++ b/app/code/Magento/MediaGalleryRenditions/Model/Queue/FetchRenditionPathsBatches.php @@ -44,7 +44,7 @@ class FetchRenditionPathsBatches /** * @param LoggerInterface $log * @param Filesystem $filesystem - * @param GetFilesIterator $assetsIterator + * @param GetFilesIterator $getFilesIterator * @param int $batchSize * @param array $fileExtensions */ diff --git a/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php b/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php index 1fcc28d235f09..45cea58d05018 100644 --- a/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php +++ b/app/code/Magento/MediaGalleryRenditions/Model/Queue/UpdateRenditions.php @@ -69,7 +69,7 @@ public function execute(array $paths): void /** * Update renditions and log exceptions * - * @param string[] $paths + * @param string[] $renditionPaths */ private function updateRenditions(array $renditionPaths): void { diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php index dd75800e4384d..69f8e5b955cba 100644 --- a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php @@ -52,15 +52,15 @@ public function __construct( * Remove renditions when assets are removed * * @param DeleteAssetsByPathsInterface $deleteAssetsByPaths - * @param \Closure $proceed + * @param null $result * @param array $paths + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundExecute( + public function afterExecute( DeleteAssetsByPathsInterface $deleteAssetsByPaths, - \Closure $proceed, + $result, array $paths ): void { - $proceed($paths); $this->removeRenditions($paths); } diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php b/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php index 9feb63377add9..9cf969c16782f 100644 --- a/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/UpdateRenditionsOnConfigChange.php @@ -54,7 +54,7 @@ public function afterSave(Value $config, Value $result): Value * @param Value $value * @return bool */ - private function isRenditionsValue(Value $value) + private function isRenditionsValue(Value $value): bool { return $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_WIDTH_PATH || $value->getPath() === self::XML_PATH_MEDIA_GALLERY_RENDITIONS_HEIGHT_PATH; diff --git a/app/code/Magento/MediaGalleryRenditions/composer.json b/app/code/Magento/MediaGalleryRenditions/composer.json index 948f6ce3b14f3..669a62865e5be 100644 --- a/app/code/Magento/MediaGalleryRenditions/composer.json +++ b/app/code/Magento/MediaGalleryRenditions/composer.json @@ -5,7 +5,9 @@ "php": "~7.3.0||~7.4.0", "magento/framework": "*", "magento/module-media-gallery-renditions-api": "*", + "magento/module-media-gallery-api": "*", "magento/module-media-content-api": "*", + "magento/framework-message-queue": "*", "magento/module-cms": "*" }, "type": "magento2-module", diff --git a/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml b/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml index e3bb939158fec..a1fbe5cba558e 100644 --- a/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml +++ b/app/code/Magento/MediaGalleryRenditions/etc/media_content.xml @@ -15,4 +15,4 @@ <pattern name="catalog_image_with_pub">/^\/pub\/?media\/(?:.renditions\/)?(.*)/</pattern> </patterns> </search> -</config> \ No newline at end of file +</config> diff --git a/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php b/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php index 6684fcc47b6c1..b3ad5543c17fa 100644 --- a/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php +++ b/app/code/Magento/MediaGalleryRenditionsApi/Api/GenerateRenditionsInterface.php @@ -9,6 +9,9 @@ use Magento\Framework\Exception\LocalizedException; +/** + * Generate optimized version of media assets based on configuration for insertion to content + */ interface GenerateRenditionsInterface { /** diff --git a/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php b/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php index 3f398dd37529b..b00c3615d9a29 100644 --- a/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php +++ b/app/code/Magento/MediaGalleryRenditionsApi/Api/GetRenditionPathInterface.php @@ -9,6 +9,9 @@ use Magento\Framework\Exception\LocalizedException; +/** + * Based on media assset path provides path to an optimized image version for insertion to the content + */ interface GetRenditionPathInterface { /** From bb408984f2d58d301eef6d8faf3bff2c268f385f Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <engcom-vendorworker-foxtrot@adobe.com> Date: Fri, 4 Sep 2020 15:24:24 +0300 Subject: [PATCH 126/195] magento/magento2#26526: OrderRepository does not check if there are no extensionAttributes. --- .../Test/Unit/Model/OrderRepositoryTest.php | 69 +++++++++++++++++-- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php index 84c66d12c10d8..407d158e6f8d4 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php @@ -18,12 +18,13 @@ use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Api\Data\OrderSearchResultInterfaceFactory as SearchResultFactory; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Shipping; use Magento\Sales\Model\Order\ShippingAssignment; use Magento\Sales\Model\Order\ShippingAssignmentBuilder; use Magento\Sales\Model\OrderRepository; use Magento\Sales\Model\ResourceModel\Metadata; -use Magento\Sales\Model\ResourceModel\Order; +use Magento\Sales\Model\ResourceModel\Order as OrderResource; use Magento\Sales\Model\ResourceModel\Order\Collection; use Magento\Tax\Api\Data\OrderTaxDetailsInterface; use Magento\Tax\Api\OrderTaxManagementInterface; @@ -70,6 +71,11 @@ class OrderRepositoryTest extends TestCase */ private $paymentAdditionalInfoFactory; + /** + * @var OrderExtensionFactory|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderExtensionFactoryMock; + /** * Setup the test * @@ -88,7 +94,7 @@ protected function setUp(): void $this->collectionProcessor = $this->createMock( CollectionProcessorInterface::class ); - $orderExtensionFactoryMock = $this->getMockBuilder(OrderExtensionFactory::class) + $this->orderExtensionFactoryMock = $this->getMockBuilder(OrderExtensionFactory::class) ->disableOriginalConstructor() ->getMock(); $this->orderTaxManagementMock = $this->getMockBuilder(OrderTaxManagementInterface::class) @@ -103,7 +109,7 @@ protected function setUp(): void 'metadata' => $this->metadata, 'searchResultFactory' => $this->searchResultFactory, 'collectionProcessor' => $this->collectionProcessor, - 'orderExtensionFactory' => $orderExtensionFactoryMock, + 'orderExtensionFactory' => $this->orderExtensionFactoryMock, 'orderTaxManagement' => $this->orderTaxManagementMock, 'paymentAdditionalInfoFactory' => $this->paymentAdditionalInfoFactory ] @@ -178,10 +184,10 @@ public function testGetList() */ public function testSave() { - $mapperMock = $this->getMockBuilder(Order::class) + $mapperMock = $this->getMockBuilder(OrderResource::class) ->disableOriginalConstructor() ->getMock(); - $orderEntity = $this->createMock(\Magento\Sales\Model\Order::class); + $orderEntity = $this->createMock(Order::class); $extensionAttributes = $this->getMockBuilder(OrderExtension::class) ->addMethods(['getShippingAssignments']) ->getMock(); @@ -207,4 +213,57 @@ public function testSave() $orderEntity->expects($this->any())->method('getEntityId')->willReturn(1); $this->orderRepository->save($orderEntity); } + + /** + * Test for method get. + * + * @return void + */ + public function testGet() + { + $orderId = 1; + $appliedTaxes = 'applied_taxes'; + $items = 'items'; + $paymentInfo = []; + + $orderEntity = $this->createMock(Order::class); + $paymentMock = $this->getMockBuilder(OrderPaymentInterface::class) + ->disableOriginalConstructor()->getMockForAbstractClass(); + $paymentMock->expects($this->once())->method('getAdditionalInformation')->willReturn($paymentInfo); + $orderExtension = $this->getMockBuilder(OrderExtension::class) + ->setMethods( + [ + 'getShippingAssignments', + 'setAppliedTaxes', + 'setConvertingFromQuote', + 'setItemAppliedTaxes', + 'setPaymentAdditionalInfo' + ] + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $orderExtension->expects($this->once())->method('getShippingAssignments')->willReturn(true); + $orderExtension->expects($this->once())->method('setAppliedTaxes')->with($appliedTaxes); + $orderExtension->expects($this->once())->method('setConvertingFromQuote')->with(true); + $orderExtension->expects($this->once())->method('setItemAppliedTaxes')->with($items); + $orderExtension->expects($this->once())->method('setPaymentAdditionalInfo')->with($paymentInfo); + $this->orderExtensionFactoryMock->expects($this->once())->method('create')->willReturn($orderExtension); + $orderEntity->expects($this->once())->method('load')->with($orderId)->willReturn($orderEntity); + $orderEntity->expects($this->exactly(2))->method('getEntityId')->willReturn($orderId); + $orderEntity->expects($this->once())->method('getPayment')->willReturn($paymentMock); + $orderEntity->expects($this->exactly(2))->method('setExtensionAttributes')->with($orderExtension); + $orderEntity->expects($this->exactly(3)) + ->method('getExtensionAttributes') + ->willReturnOnConsecutiveCalls(null, $orderExtension, $orderExtension); + $this->metadata->expects($this->once())->method('getNewInstance')->willReturn($orderEntity); + $orderTaxDetailsMock = $this->getMockBuilder(OrderTaxDetailsInterface::class) + ->disableOriginalConstructor() + ->setMethods(['setAppliedTaxes'])->getMockForAbstractClass(); + $orderTaxDetailsMock->expects($this->once())->method('getAppliedTaxes')->willReturn($appliedTaxes); + $orderTaxDetailsMock->expects($this->once())->method('getItems')->willReturn($items); + $this->orderTaxManagementMock->expects($this->atLeastOnce())->method('getOrderTaxDetails') + ->willReturn($orderTaxDetailsMock); + + $this->orderRepository->get($orderId); + } } From 74325ef39fd1c1e4e957747dc2d29ca3e73f4cf8 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Fri, 4 Sep 2020 15:46:53 +0300 Subject: [PATCH 127/195] minor refactor --- app/code/Magento/Widget/Model/Widget.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index ec948063234da..72f7dd77577af 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -310,10 +310,14 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) { $widget = $this->getConfigAsObject($type); + $params = array_filter($params, function ($value) { + return $value !== null; + }); + $directiveParams = ''; foreach ($params as $name => $value) { // Retrieve default option value if pre-configured - $directiveParams .= $value === null ? '' : $this->getDirectiveParam($widget, $name, $value); + $directiveParams .= $this->getDirectiveParam($widget, $name, $value); } $directive = sprintf('{{widget type="%s"%s%s}}', $type, $directiveParams, $this->getWidgetPageVarName($params)); From 0ac2e5d691f5bccbdb2fba896bb1c22ff6a0fa80 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Fri, 4 Sep 2020 15:47:29 +0300 Subject: [PATCH 128/195] mftf coverage --- ...alogProductsListWidgetTitleActionGroup.xml | 22 ++++++++ ...StorefrontAssertWidgetTitleActionGroup.xml | 26 ++++++++++ .../InsertWidgetSection.xml | 1 + ...eFrontWidgetTitleWithReservedCharsTest.xml | 52 +++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.xml create mode 100644 app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml create mode 100644 app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.xml new file mode 100644 index 0000000000000..e146506d51a24 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetTitleActionGroup.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="AdminFillCatalogProductsListWidgetTitleActionGroup"> + <annotations> + <description>Fill catalog products list title field.</description> + </annotations> + + <arguments> + <argument name="title" type="string" defaultValue=""/> + </arguments> + <waitForElementVisible selector="{{InsertWidgetSection.title}}" stepKey="waitForField"/> + <fillField selector="{{InsertWidgetSection.title}}" userInput="{{title}}" stepKey="fillTitleField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml new file mode 100644 index 0000000000000..4505680424471 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/StorefrontAssertWidgetTitleActionGroup.xml @@ -0,0 +1,26 @@ +<?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="StorefrontAssertWidgetTitleActionGroup"> + <annotations> + <description>Assert widget title on storefront.</description> + </annotations> + <arguments> + <argument name="title" type="string"/> + </arguments> + + <grabTextFrom selector="{{StorefrontWidgetsSection.widgetProductsGrid}} {{StorefrontWidgetsSection.widgetTitle}}" + stepKey="grabWidgetTitle"/> + <assertEquals stepKey="assertWidgetTitle"> + <actualResult type="string">$grabWidgetTitle</actualResult> + <expectedResult type="string">{{title}}</expectedResult> + </assertEquals> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml index 9b40971611d6f..3d8d5ecc1cda9 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection/InsertWidgetSection.xml @@ -19,5 +19,6 @@ <element name="checkElementStorefrontByPrice" type="button" selector="//*[@class='product-items widget-product-grid']//*[contains(text(),'${{arg4}}.00')]" parameterized="true"/> <element name="checkElementStorefrontByName" type="button" selector="//*[@class='product-items widget-product-grid']//*[@class='product-item'][{{productPosition}}]//a[contains(text(), '{{productName}}')]" parameterized="true"/> <element name="categoryTreeWrapper" type="text" selector=".rule-chooser .tree.x-tree"/> + <element name="title" type="text" selector="input[name='parameters[title]']"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml new file mode 100644 index 0000000000000..4cfdc8197ad84 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StoreFrontWidgetTitleWithReservedCharsTest"> + <annotations> + <features value="Cms"/> + <stories value="Create a CMS Page via the Admin when widget title contains reserved chairs"/> + <title value="Create CMS Page via the Admin when widget title contains reserved chairs"/> + <description value="See CMS Page title on store front page if titled widget with reserved chairs added"/> + <severity value="MAJOR"/> + <group value="Cms"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <createData entity="simpleProductWithoutCategory" stepKey="createSimpleProductWithoutCategory"/> + <createData entity="_defaultCmsPage" stepKey="createCmsPage"/> + </before> + <after> + <deleteData createDataKey="createSimpleProductWithoutCategory" stepKey="deleteProduct"/> + <deleteData createDataKey="createCmsPage" stepKey="deleteCmsPage" /> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <!--Navigate to Page in Admin--> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCMSPage"> + <argument name="CMSPage" value="$createCmsPage$"/> + </actionGroup> + <!--Insert widget--> + <actionGroup ref="AdminInsertWidgetToCmsPageContentActionGroup" stepKey="insertWidgetToCmsPageContent"> + <argument name="widgetType" value="Catalog Products List"/> + </actionGroup> + <!--Fill widget title and save--> + <actionGroup ref="AdminFillCatalogProductsListWidgetTitleActionGroup" stepKey="fillWidgetTitle"> + <argument name="title" value="Tittle }}"/> + </actionGroup> + <actionGroup ref="AdminClickInsertWidgetActionGroup" stepKey="clickInsertWidgetButton"/> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="saveOpenedPage"/> + <!--Verify data on frontend--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateToPageOnStorefront"> + <argument name="identifier" value="$createCmsPage.identifier$"/> + </actionGroup> + <actionGroup ref="StorefrontAssertWidgetTitleActionGroup" stepKey="verifyPageDataOnFrontend"> + <argument name="title" value="Tittle }}"/> + </actionGroup> + </test> +</tests> From f7cd6d77439722059b0a7f8ef7c295f67d53c0bd Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 13:58:06 +0100 Subject: [PATCH 129/195] magento/magento2#29889: Removed redundant dependency --- app/code/Magento/MediaGalleryRenditions/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryRenditions/composer.json b/app/code/Magento/MediaGalleryRenditions/composer.json index 669a62865e5be..873e0b4a8c60b 100644 --- a/app/code/Magento/MediaGalleryRenditions/composer.json +++ b/app/code/Magento/MediaGalleryRenditions/composer.json @@ -6,10 +6,12 @@ "magento/framework": "*", "magento/module-media-gallery-renditions-api": "*", "magento/module-media-gallery-api": "*", - "magento/module-media-content-api": "*", "magento/framework-message-queue": "*", "magento/module-cms": "*" }, + "suggest": { + "magento/module-media-content-api": "*" + }, "type": "magento2-module", "license": [ "OSL-3.0", From 86e96564509e78fb04670d2f2d7157d08e9d0189 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 4 Sep 2020 21:06:15 +0800 Subject: [PATCH 130/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - fixed spacing on data --- .../Test/Mftf/Data/AdminEnhancedMediaGalleryImageData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdminEnhancedMediaGalleryImageData.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdminEnhancedMediaGalleryImageData.xml index dbc298798ee8e..4adf92b1c4c09 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdminEnhancedMediaGalleryImageData.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Data/AdminEnhancedMediaGalleryImageData.xml @@ -24,7 +24,7 @@ <data key="fileName">png</data> <data key="extension">png</data> </entity> - <entity name="ImageUploadGif" type="uploadImage"> + <entity name="ImageUploadGif" type="uploadImage"> <data key="title" unique="suffix">Image1</data> <data key="file_type">Upload File</data> <data key="value">gif.gif</data> From 748eb4f13da880c022203fca256a1cfebd96b778 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 14:08:49 +0100 Subject: [PATCH 131/195] magento/magento2#29715: Corrected dependencies --- app/code/Magento/MediaGalleryCatalogUi/composer.json | 1 + app/code/Magento/MediaGalleryUi/composer.json | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/composer.json b/app/code/Magento/MediaGalleryCatalogUi/composer.json index 418e113072ae3..985d581beff25 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/composer.json +++ b/app/code/Magento/MediaGalleryCatalogUi/composer.json @@ -4,6 +4,7 @@ "require": { "php": "~7.3.0||~7.4.0", "magento/framework": "*", + "magento/module-cms": "*", "magento/module-backend": "*", "magento/module-catalog": "*", "magento/module-store": "*", diff --git a/app/code/Magento/MediaGalleryUi/composer.json b/app/code/Magento/MediaGalleryUi/composer.json index 6008bb1579c49..f4701306eb369 100644 --- a/app/code/Magento/MediaGalleryUi/composer.json +++ b/app/code/Magento/MediaGalleryUi/composer.json @@ -12,8 +12,7 @@ "magento/module-media-gallery-metadata-api": "*", "magento/module-media-gallery-synchronization-api": "*", "magento/module-media-content-api": "*", - "magento/module-cms": "*", - "magento/module-media-gallery": "*" + "magento/module-cms": "*" }, "type": "magento2-module", "license": [ From b3d52559d3529b4ad1f8d28dfb200d6c579a94e7 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 14:39:53 +0100 Subject: [PATCH 132/195] magento/magento2#29715: Corrected standalone actions and upgrade script --- .../Adminhtml/ImageDetailsStandalone.php | 89 +++++++++++++++++++ .../Patch/Data/AddMediaGalleryPermissions.php | 8 +- .../layout/media_gallery_media_index.xml | 2 +- 3 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php diff --git a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php new file mode 100644 index 0000000000000..44637770271a6 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\MediaGalleryUi\Block\Adminhtml; + +use Magento\Backend\Block\Template; +use Magento\Directory\Helper\Data as DirectoryHelperData; +use Magento\Framework\AuthorizationInterface; +use Magento\Framework\Json\Helper\Data as JsonHelperData; +use Magento\Framework\Serialize\Serializer\Json; + +/** + * Image details block + * + * @api + */ +class ImageDetailsStandalone extends Template +{ + /** + * @var AuthorizationInterface + */ + private $authorization; + + /** + * @var Json + */ + private $json; + + /** + * @param AuthorizationInterface $authorization + * @param Template\Context $context + * @param array $data + * @param JsonHelperData|null $jsonHelper + * @param DirectoryHelperData|null $directoryHelper + */ + public function __construct( + AuthorizationInterface $authorization, + Json $json, + Template\Context $context, + array $data = [], + ?JsonHelperData $jsonHelper = null, + ?DirectoryHelperData $directoryHelper = null + ) { + $this->authorization = $authorization; + $this->json = $json; + parent::__construct($context, $data, $jsonHelper, $directoryHelper); + } + + /** + * Retrieve actions json + * + * @return string + */ + public function getActionsJson(): string + { + $actions = [ + [ + 'title' => __('Cancel'), + 'handler' => 'closeModal', + 'name' => 'cancel', + 'classes' => 'action-default scalable cancel action-quaternary' + ] + ]; + + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::delete_assets')) { + $actions[] = [ + 'title' => __('Delete Image'), + 'handler' => 'deleteImageAction', + 'name' => 'delete', + 'classes' => 'action-default scalable delete action-quaternary' + ]; + } + + if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::edit_assets')) { + $actions[] = [ + 'title' => __('Edit Details'), + 'handler' => 'editImageAction', + 'name' => 'edit', + 'classes' => 'action-default scalable edit action-quaternary' + ]; + } + + return $this->json->serialize($actions); + } +} diff --git a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php index 60ffb2147bab6..f3b554e207037 100644 --- a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php +++ b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php @@ -78,12 +78,14 @@ private function getInsertData(array $roleIds): array ]; } } + + return $data; } /** * @inheritdoc */ - public function getAliases() + public function getAliases(): array { return []; } @@ -91,7 +93,7 @@ public function getAliases() /** * @inheritdoc */ - public static function getDependencies() + public static function getDependencies(): array { return []; } @@ -99,7 +101,7 @@ public static function getDependencies() /** * @inheritdoc */ - public static function getVersion() + public static function getVersion(): string { return '2.4.2'; } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml index 7697519c40f4d..b4f377627c850 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/layout/media_gallery_media_index.xml @@ -10,7 +10,7 @@ <body> <referenceContainer htmlTag="div" htmlClass="media-gallery-container" name="content"> <uiComponent name="standalone_media_gallery_listing"/> - <block name="image.details" class="Magento\MediaGalleryUi\Block\Adminhtml\ImageDetails" template="Magento_MediaGalleryUi::image_details_standalone.phtml"> + <block name="image.details" class="Magento\MediaGalleryUi\Block\Adminhtml\ImageDetailsStandalone" template="Magento_MediaGalleryUi::image_details_standalone.phtml"> <arguments> <argument name="imageDetailsUrl" xsi:type="url" path="media_gallery/image/details"/> </arguments> From 0a6156f038ff20ba31ada254baf0855b4f045f9d Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 14:51:19 +0100 Subject: [PATCH 133/195] magento/magento2#29715: Fixed upgrade --- .../Setup/Patch/Data/AddMediaGalleryPermissions.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php index f3b554e207037..e72017e20a7f6 100644 --- a/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php +++ b/app/code/Magento/MediaGalleryUi/Setup/Patch/Data/AddMediaGalleryPermissions.php @@ -47,7 +47,11 @@ public function apply(): void ->from($tableName, ['role_id']) ->where('resource_id = "Magento_Cms::media_gallery"'); - $connection->insertMultiple($tableName, $this->getInsertData($connection->fetchCol($select))); + $insertData = $this->getInsertData($connection->fetchCol($select)); + + if (!empty($insertData)) { + $connection->insertMultiple($tableName, $insertData); + } } /** From 70b0d06f94d24903e04c7d4690f737ed1b73d0b1 Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@gmail.com> Date: Fri, 4 Sep 2020 16:59:28 +0300 Subject: [PATCH 134/195] adobe-stock-integration#1792: fix category image is not removed from tmp folder after category save --- .../Plugin/SaveBaseCategoryImageInformation.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogIntegration/Plugin/SaveBaseCategoryImageInformation.php b/app/code/Magento/MediaGalleryCatalogIntegration/Plugin/SaveBaseCategoryImageInformation.php index d439b53c120cb..b683ec8fe9d91 100644 --- a/app/code/Magento/MediaGalleryCatalogIntegration/Plugin/SaveBaseCategoryImageInformation.php +++ b/app/code/Magento/MediaGalleryCatalogIntegration/Plugin/SaveBaseCategoryImageInformation.php @@ -81,17 +81,18 @@ public function __construct( * * @param ImageUploader $subject * @param string $imagePath + * @param string $initialImageName * @return string * @throws LocalizedException */ - public function afterMoveFileFromTmp(ImageUploader $subject, string $imagePath): string + public function afterMoveFileFromTmp(ImageUploader $subject, string $imagePath, string $initialImageName): string { if (!$this->config->isEnabled()) { return $imagePath; } $absolutePath = $this->storage->getCmsWysiwygImages()->getStorageRoot() . $imagePath; - $tmpPath = $subject->getBaseTmpPath() . '/' . substr(strrchr($imagePath, '/'), 1); + $tmpPath = $subject->getBaseTmpPath() . '/' . $initialImageName; $tmpAssets = $this->getAssetsByPaths->execute([$tmpPath]); if (!empty($tmpAssets)) { From 80c1f3ca7e0abffbf214c14292cd6fe322f0b484 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 4 Sep 2020 22:25:01 +0800 Subject: [PATCH 135/195] magento/adobe-stock-integration#1795: [MFTF] Unskip AdminMediaGalleryCatalogUiUsedInProductFilterTest - modified test and actiongroup --- ...dminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml | 2 +- .../AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml index e21fa89965391..fbf4437b8266d 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml @@ -15,6 +15,6 @@ <argument name="filterPlaceholder" type="string"/> </arguments> - <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="{{filterPlaceholder}}" stepKey="seeFilter"/> + <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="SKU: {{filterPlaceholder}}" stepKey="seeFilter"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml index d2a04a7d21d11..11b64c596fb84 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml @@ -9,9 +9,6 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMediaGalleryCatalogUiUsedInProductFilterTest"> <annotations> - <skip> - <issueId value="https://github.com/magento/adobe-stock-integration/issues/1795"/> - </skip> <features value="AdminMediaGalleryUsedInProductsFilter"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1503"/> <title value="User can open product entity the asset is associated"/> @@ -65,6 +62,7 @@ <deleteData createDataKey="product" stepKey="deleteProduct"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openMediaGallery"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetailsToAssertEmptyUsedIn"/> <actionGroup ref="AssertAdminEnhancedMediaGalleryUsedInSectionNotDisplayedActionGroup" stepKey="assertThereIsNoUsedInSection"/> <actionGroup ref="AdminEnhancedMediaGalleryCloseViewDetailsActionGroup" stepKey="closeDetails"/> From 055ad7e58e21ff7748493892c43eb980cddc3984 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 4 Sep 2020 23:50:04 +0800 Subject: [PATCH 136/195] magento/adobe-stock-integration#1795: [MFTF] Unskip AdminMediaGalleryCatalogUiUsedInProductFilterTest - modified actiongroup --- .../AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml index fbf4437b8266d..e21fa89965391 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup.xml @@ -15,6 +15,6 @@ <argument name="filterPlaceholder" type="string"/> </arguments> - <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="SKU: {{filterPlaceholder}}" stepKey="seeFilter"/> + <see selector="{{AdminProductGridFilterSection.enabledFilters}}" userInput="{{filterPlaceholder}}" stepKey="seeFilter"/> </actionGroup> </actionGroups> From f3ea93f27c2d7b6258bab7c165ede0ca5a219af0 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Sat, 5 Sep 2020 01:02:06 +0800 Subject: [PATCH 137/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - modified actiongroup, test and added new category data entity --- ...eProductsInMenuEnabledColumnsActionGroup.xml | 5 +++++ .../AdminEnhancedMediaGalleryCategoryData.xml | 17 +++++++++++++++++ ...atalogUiVerifyUsedInLinkCategoryGridTest.xml | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml index e5d6f26e777fc..15b0ff149d807 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml @@ -12,6 +12,11 @@ <description>Assert category grid page products, in menu, and enabled column values for a specific category</description> </annotations> + <grabTextFrom selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.columnValue('Display Mode')}}" stepKey="grabDisplayModeColumnValue"/> + <assertEquals stepKey="assertDisplayModeColumn"> + <expectedResult type="string">PRODUCTS</expectedResult> + <actualResult type="variable">grabDisplayModeColumnValue</actualResult> + </assertEquals> <grabTextFrom selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.columnValue('Products')}}" stepKey="grabProductsColumnValue"/> <assertEquals stepKey="assertProductsColumn"> <expectedResult type="string">0</expectedResult> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml new file mode 100644 index 0000000000000..34cc0f4fa752c --- /dev/null +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="TestSubCategory" type="category"> + <data key="name">TestSubCategory</data> + <data key="name_lwr">testsubcategory</data> + <data key="is_active">true</data> + <data key="include_in_menu">true</data> + </entity> +</entities> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml index 36df5bf736d1b..dba78d0cf5261 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml @@ -19,7 +19,7 @@ <group value="media_gallery_ui"/> </annotations> <before> - <createData entity="SimpleSubCategory" stepKey="category"/> + <createData entity="TestSubCategory" stepKey="category"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> From 635971ffaec3d80af20da051e685f4106d677f64 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 19:13:19 +0100 Subject: [PATCH 138/195] Fixed static test --- .../MediaGalleryRenditions/Plugin/RemoveRenditions.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php index 69f8e5b955cba..f05c34e703abe 100644 --- a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php @@ -7,6 +7,7 @@ namespace Magento\MediaGalleryRenditions\Plugin; +use Magento\Catalog\Helper\Data as CatalogHelper; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\MediaGalleryApi\Api\DeleteAssetsByPathsInterface; @@ -18,6 +19,11 @@ */ class RemoveRenditions { + /** + * @var CatalogHelper + */ + private $catalogHelper; + /** * @var GetRenditionPathInterface */ @@ -39,10 +45,12 @@ class RemoveRenditions * @param LoggerInterface $log */ public function __construct( + CatalogHelper $catalogHelper, GetRenditionPathInterface $getRenditionPath, Filesystem $filesystem, LoggerInterface $log ) { + $this->catalogHelper = $catalogHelper; $this->getRenditionPath = $getRenditionPath; $this->filesystem = $filesystem; $this->log = $log; @@ -52,7 +60,7 @@ public function __construct( * Remove renditions when assets are removed * * @param DeleteAssetsByPathsInterface $deleteAssetsByPaths - * @param null $result + * @param void $result * @param array $paths * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ From 253bafbc254692717a8e090b67da129a72b4f061 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 4 Sep 2020 19:34:57 +0100 Subject: [PATCH 139/195] magento/magento2#29715: Fixed tests --- .../MediaGalleryUi/Block/Adminhtml/ImageDetails.php | 5 +++-- .../Block/Adminhtml/ImageDetailsStandalone.php | 13 +++++++------ .../Test/AdminMediaGalleryCreateFolderAclTest.xml | 4 ++-- .../Test/AdminMediaGalleryDeleteAssetsAclTest.xml | 4 ++-- .../Test/AdminMediaGalleryDeleteFolderAclTest.xml | 6 +++--- .../Test/AdminMediaGalleryUploadAssetsAclTest.xml | 4 ++-- app/code/Magento/MediaGalleryUi/composer.json | 4 +++- .../view/adminhtml/templates/image_details.phtml | 2 +- .../templates/image_details_standalone.phtml | 2 +- .../adminhtml/web/js/grid/columns/image/actions.js | 2 +- 10 files changed, 25 insertions(+), 21 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php index e3f9c5bafca22..d797acedda6ec 100644 --- a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php +++ b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetails.php @@ -31,16 +31,17 @@ class ImageDetails extends Template private $json; /** - * @param AuthorizationInterface $authorization * @param Template\Context $context + * @param AuthorizationInterface $authorization + * @param Json $json * @param array $data * @param JsonHelper|null $jsonHelper * @param DirectoryHelper|null $directoryHelper */ public function __construct( + Template\Context $context, AuthorizationInterface $authorization, Json $json, - Template\Context $context, array $data = [], ?JsonHelper $jsonHelper = null, ?DirectoryHelper $directoryHelper = null diff --git a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php index 44637770271a6..7e73b1682f79a 100644 --- a/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php +++ b/app/code/Magento/MediaGalleryUi/Block/Adminhtml/ImageDetailsStandalone.php @@ -31,16 +31,17 @@ class ImageDetailsStandalone extends Template private $json; /** - * @param AuthorizationInterface $authorization * @param Template\Context $context + * @param AuthorizationInterface $authorization + * @param Json $json * @param array $data * @param JsonHelperData|null $jsonHelper * @param DirectoryHelperData|null $directoryHelper */ public function __construct( + Template\Context $context, AuthorizationInterface $authorization, Json $json, - Template\Context $context, array $data = [], ?JsonHelperData $jsonHelper = null, ?DirectoryHelperData $directoryHelper = null @@ -57,7 +58,7 @@ public function __construct( */ public function getActionsJson(): string { - $actions = [ + $standaloneActions = [ [ 'title' => __('Cancel'), 'handler' => 'closeModal', @@ -67,7 +68,7 @@ public function getActionsJson(): string ]; if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::delete_assets')) { - $actions[] = [ + $standaloneActions[] = [ 'title' => __('Delete Image'), 'handler' => 'deleteImageAction', 'name' => 'delete', @@ -76,7 +77,7 @@ public function getActionsJson(): string } if ($this->authorization->isAllowed('Magento_MediaGalleryUiApi::edit_assets')) { - $actions[] = [ + $standaloneActions[] = [ 'title' => __('Edit Details'), 'handler' => 'editImageAction', 'name' => 'edit', @@ -84,6 +85,6 @@ public function getActionsJson(): string ]; } - return $this->json->serialize($actions); + return $this->json->serialize($standaloneActions); } } diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml index 8e1e4bbfbd48a..9738ddedc3cc3 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryCreateFolderAclTest.xml @@ -44,7 +44,7 @@ <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Create Folder"/> + <argument name="restrictedRole" value="Create folder"/> </actionGroup> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> <argument name="User" value="adminRole"/> @@ -58,7 +58,7 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> - + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="username" value="{{admin2.username}}"/> <argument name="password" value="{{admin2.password}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml index bdfb5c475b3d8..1d51caf0fc400 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteAssetsAclTest.xml @@ -44,7 +44,7 @@ <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="uncheckDeleteFolder"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Assets"/> + <argument name="restrictedRole" value="Delete assets"/> </actionGroup> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> @@ -59,7 +59,7 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> - + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="username" value="{{admin2.username}}"/> <argument name="password" value="{{admin2.password}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml index f261e1bd4601c..121ad25c93f0d 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDeleteFolderAclTest.xml @@ -44,9 +44,9 @@ <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryResource"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Delete Folder"/> + <argument name="restrictedRole" value="Delete folder"/> </actionGroup> - + <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> <argument name="User" value="adminRole"/> <argument name="restrictedRole" value="Pages"/> @@ -59,7 +59,7 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> - + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="username" value="{{admin2.username}}"/> <argument name="password" value="{{admin2.password}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml index 4dae0b1ec5510..c8f8655d11edb 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryUploadAssetsAclTest.xml @@ -44,7 +44,7 @@ <actionGroup ref="AdminUserClickRoleResourceTabActionGroup" stepKey="switchToRoleResourceTab"/> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryUnchekDeleteAssets"> <argument name="User" value="adminRole"/> - <argument name="restrictedRole" value="Upload Assets"/> + <argument name="restrictedRole" value="Upload assets"/> </actionGroup> <actionGroup ref="AdminAddRestrictedRoleActionGroup" stepKey="AddMediaGalleryPagesResource"> @@ -59,7 +59,7 @@ </actionGroup> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> - + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsNewUser"> <argument name="username" value="{{admin2.username}}"/> <argument name="password" value="{{admin2.password}}"/> diff --git a/app/code/Magento/MediaGalleryUi/composer.json b/app/code/Magento/MediaGalleryUi/composer.json index f4701306eb369..204e0b37c3bf8 100644 --- a/app/code/Magento/MediaGalleryUi/composer.json +++ b/app/code/Magento/MediaGalleryUi/composer.json @@ -12,7 +12,9 @@ "magento/module-media-gallery-metadata-api": "*", "magento/module-media-gallery-synchronization-api": "*", "magento/module-media-content-api": "*", - "magento/module-cms": "*" + "magento/module-cms": "*", + "magento/module-directory": "*", + "magento/module-authorization": "*" }, "type": "magento2-module", "license": [ diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml index a547f33adbbdb..5df5c1a6c4cbd 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details.phtml @@ -73,7 +73,7 @@ use Magento\Framework\Escaper; "modalWindowSelector": ".media-gallery-image-details", "imageModelName" : "media_gallery_listing.media_gallery_listing.media_gallery_columns.thumbnail_url", "mediaGalleryImageDetailsName": "mediaGalleryImageDetails", - "actionsList": <?= $block->getActionsJson() ?> + "actionsList": <?= /* @noEscape */ $block->getActionsJson() ?> } } } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml index b4c80bf6d4196..fdae0a549606c 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/templates/image_details_standalone.phtml @@ -69,7 +69,7 @@ "modalWindowSelector": ".media-gallery-image-details", "mediaGalleryImageDetailsName": "mediaGalleryImageDetails", "imageModelName" : "standalone_media_gallery_listing.standalone_media_gallery_listing.media_gallery_columns.thumbnail_url", - "actionsList": <?= $block->getActionsJson() ?> + "actionsList": <?= /* @noEscape */ $block->getActionsJson() ?> } } } diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js index 10b94632d6745..76e051072285a 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image/actions.js @@ -55,7 +55,7 @@ define([ this._super(); this.initEvents(); - this.actionsList = this.actionsList.filter(function(item) { + this.actionsList = this.actionsList.filter(function (item) { return this.allowedActions.includes(item.name); }.bind(this)); From a298f3b7123ff5fce15822aa7b3d8c9186dec584 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Sat, 5 Sep 2020 02:35:29 +0800 Subject: [PATCH 140/195] magento/adobe-stock-integration#1795: [MFTF] Unskip AdminMediaGalleryCatalogUiUsedInProductFilterTest - modified test file --- .../AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml index 11b64c596fb84..3caa106ffa245 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml @@ -43,6 +43,8 @@ <actionGroup ref="AdminMediaGalleryClickAddSelectedActionGroup" stepKey="clickAddSelectedContentImage"/> <actionGroup ref="AdminMediaGalleryClickOkButtonTinyMce4ActionGroup" stepKey="clickOkButton"/> <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductsGrid"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFilters"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGallery"/> <actionGroup ref="AdminEnhancedMediaGalleryExpandFilterActionGroup" stepKey="expandFilters"/> <actionGroup ref="AdminEnhancedMediaGallerySelectUsedInFilterActionGroup" stepKey="setUsedInFilter"> @@ -57,7 +59,7 @@ </actionGroup> <actionGroup ref="AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup" stepKey="assertFilterApplied"> - <argument name="filterPlaceholder" value="$$product.name$$"/> + <argument name="filterPlaceholder" value="{{ImageMetadata.title}}"/> </actionGroup> <deleteData createDataKey="product" stepKey="deleteProduct"/> @@ -71,7 +73,7 @@ <actionGroup ref="AdminEnhancedMediaGallerySelectImageForMassActionActionGroup" stepKey="selectFirstImageToDelete"> <argument name="imageName" value="{{ImageMetadata.title}}"/> </actionGroup> - <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clikDeleteSelectedButton"/> + <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clickDeleteSelectedButton"/> <actionGroup ref="AdminEnhancedMediaGalleryConfirmDeleteImagesActionGroup" stepKey="deleteImages"/> </test> From 27a9befc488ef2fd097d3779d150a0a9de75363c Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Sat, 5 Sep 2020 03:43:34 +0800 Subject: [PATCH 141/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - modified custom category entity, actiongroup, and test --- ...oryGridPageProductsInMenuEnabledColumnsActionGroup.xml | 5 ----- .../Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml | 4 ++-- ...iaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml | 8 ++++++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml index 15b0ff149d807..e5d6f26e777fc 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/ActionGroup/AssertAdminCategoryGridPageProductsInMenuEnabledColumnsActionGroup.xml @@ -12,11 +12,6 @@ <description>Assert category grid page products, in menu, and enabled column values for a specific category</description> </annotations> - <grabTextFrom selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.columnValue('Display Mode')}}" stepKey="grabDisplayModeColumnValue"/> - <assertEquals stepKey="assertDisplayModeColumn"> - <expectedResult type="string">PRODUCTS</expectedResult> - <actualResult type="variable">grabDisplayModeColumnValue</actualResult> - </assertEquals> <grabTextFrom selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.columnValue('Products')}}" stepKey="grabProductsColumnValue"/> <assertEquals stepKey="assertProductsColumn"> <expectedResult type="string">0</expectedResult> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml index 34cc0f4fa752c..d123cce516533 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml @@ -9,8 +9,8 @@ <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> <entity name="TestSubCategory" type="category"> - <data key="name">TestSubCategory</data> - <data key="name_lwr">testsubcategory</data> + <data key="name" unique="suffix">TestSubCategory</data> + <data key="name_lwr" unique="suffix">testsubcategory</data> <data key="is_active">true</data> <data key="include_in_menu">true</data> </entity> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml index dba78d0cf5261..aaba740930267 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml @@ -21,13 +21,16 @@ <before> <createData entity="TestSubCategory" stepKey="category"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> + <argument name="tags" value=""/> + </actionGroup> </before> <after> <actionGroup ref="AdminEnhancedMediaGalleryEnableMassActionModeActionGroup" stepKey="enableMassActionToDeleteImages"/> <actionGroup ref="AdminEnhancedMediaGallerySelectImageForMassActionActionGroup" stepKey="selectSecondImageToDelete"> <argument name="imageName" value="{{UpdatedImageDetails.title}}"/> </actionGroup> - <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clikDeleteSelectedButton"/> + <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clickDeleteSelectedButton"/> <actionGroup ref="AdminEnhancedMediaGalleryConfirmDeleteImagesActionGroup" stepKey="deleteImages"/> </after> @@ -36,6 +39,7 @@ <argument name="category" value="$$category$$"/> </actionGroup> <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGalleryFromImageUploader"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetCategoryImageGalleryGridToDefaultView"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> @@ -77,7 +81,7 @@ <actionGroup ref="AssertAdminMediaGalleryAssetFilterPlaceHolderActionGroup" stepKey="assertFilterAppliedAfterUrlFilterApplier"> <argument name="filterPlaceholder" value="{{UpdatedImageDetails.title}}"/> </actionGroup> - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> <deleteData createDataKey="category" stepKey="deleteCategory"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openMediaGallery"/> From 1b76317db0d8653d18cb3ea143f5a0711713ba25 Mon Sep 17 00:00:00 2001 From: Tu Nguyen <tuna@ecommage.com> Date: Sat, 5 Sep 2020 14:41:44 +0700 Subject: [PATCH 142/195] Removed redundant less styles navigation in blank theme u up --- .../blank/web/css/source/_navigation.less | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/app/design/frontend/Magento/blank/web/css/source/_navigation.less b/app/design/frontend/Magento/blank/web/css/source/_navigation.less index fad906a089400..f9cca1ca16a18 100644 --- a/app/design/frontend/Magento/blank/web/css/source/_navigation.less +++ b/app/design/frontend/Magento/blank/web/css/source/_navigation.less @@ -28,10 +28,10 @@ .nav-toggle { .lib-icon-font( - @icon-menu, - @_icon-font-size: 28px, - @_icon-font-color: @header-icons-color, - @_icon-font-color-hover: @header-icons-color-hover + @icon-menu, + @_icon-font-size: 28px, + @_icon-font-color: @header-icons-color, + @_icon-font-color-hover: @header-icons-color-hover ); .lib-icon-text-hide(); cursor: pointer; @@ -54,13 +54,13 @@ .parent { .level-top { - position: relative; .lib-icon-font( - @_icon-font-content: @icon-down, - @_icon-font-size: 42px, - @_icon-font-position: after, - @_icon-font-display: block + @_icon-font-content: @icon-down, + @_icon-font-size: 42px, + @_icon-font-position: after, + @_icon-font-display: block ); + position: relative; &:after { position: absolute; @@ -70,8 +70,8 @@ &.ui-state-active { .lib-icon-font-symbol( - @_icon-font-content: @icon-up, - @_icon-font-position: after + @_icon-font-content: @icon-up, + @_icon-font-position: after ); } } @@ -82,12 +82,10 @@ -webkit-overflow-scrolling: touch; .lib-css(transition, left .3s, 1); height: 100%; - left: -80%; left: calc(~'-1 * (100% - @{active-nav-indent})'); overflow: auto; position: fixed; top: 0; - width: 80%; width: calc(~'100% - @{active-nav-indent}'); .switcher { @@ -109,13 +107,13 @@ .switcher-trigger { strong { - position: relative; .lib-icon-font( - @_icon-font-content: @icon-down, - @_icon-font-size: 42px, - @_icon-font-position: after, - @_icon-font-display: block + @_icon-font-content: @icon-down, + @_icon-font-size: 42px, + @_icon-font-position: after, + @_icon-font-display: block ); + position: relative; &:after { position: absolute; @@ -126,16 +124,18 @@ &.active strong { .lib-icon-font-symbol( - @_icon-font-content: @icon-up, - @_icon-font-position: after + @_icon-font-content: @icon-up, + @_icon-font-position: after ); } } + .switcher-dropdown { .lib-list-reset-styles(); display: none; padding: @indent__s 0; } + .switcher-options { &.active { .switcher-dropdown { @@ -143,6 +143,7 @@ } } } + .header.links { .lib-list-reset-styles(); border-bottom: 1px solid @color-gray82; @@ -200,13 +201,11 @@ .nav-open { .page-wrapper { - left: 80%; left: calc(~'100% - @{active-nav-indent}'); } .nav-sections { @_shadow: 0 0 5px 0 rgba(50, 50, 50, .75); - .lib-css(box-shadow, @_shadow, 1); left: 0; z-index: 99; @@ -293,10 +292,6 @@ display: none; } - .nav-sections-item-content { - display: block !important; - } - .nav-sections-item-content > * { display: none; } From 66ee024ddeae22a6deffa84246b0c75d9315d0c6 Mon Sep 17 00:00:00 2001 From: Mohamed-Asar <mohamed.azarudeen@ziffity.com> Date: Sat, 5 Sep 2020 18:30:20 +0530 Subject: [PATCH 143/195] Jasmine test case added --- .../app/code/Magento/Ui/base/js/form/ui-select.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js index f46ff6b30abbe..c0ecec40516fa 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js @@ -246,6 +246,12 @@ define([ expect(type).toEqual('object'); }); + it('Must be false if "disabled" is true', function () { + obj.listVisible(false); + obj.disabled(true); + obj.toggleListVisible(); + expect(obj.listVisible()).toEqual(false); + }); it('Must be false if "listVisible" is true', function () { obj.listVisible(true); obj.toggleListVisible(); From ee1d2d2bc57de68d4d57114e28001f546d5cc78c Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Sun, 6 Sep 2020 11:34:40 +0100 Subject: [PATCH 144/195] magento/magento2#29543: Removed general operations from partial synchronizer --- .../Model/SynchronizeIdentities.php | 54 +++---------------- ...Pool.php => SynchronizeIdentitiesPool.php} | 2 +- .../etc/di.xml | 2 +- .../MediaContentSynchronizationCms/etc/di.xml | 2 +- 4 files changed, 11 insertions(+), 49 deletions(-) rename app/code/Magento/MediaContentSynchronizationApi/Model/{SynchronizerIdentitiesPool.php => SynchronizeIdentitiesPool.php} (97%) diff --git a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php index 0cd9b9bca6784..1bf57c6b2ec42 100644 --- a/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php +++ b/app/code/Magento/MediaContentSynchronization/Model/SynchronizeIdentities.php @@ -11,7 +11,7 @@ use Magento\Framework\FlagManager; use Magento\Framework\Stdlib\DateTime\DateTimeFactory; use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -use Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool; +use Magento\MediaContentSynchronizationApi\Model\SynchronizeIdentitiesPool; use Psr\Log\LoggerInterface; /** @@ -19,52 +19,26 @@ */ class SynchronizeIdentities implements SynchronizeIdentitiesInterface { - private const LAST_EXECUTION_TIME_CODE = 'media_content_last_execution'; - - /** - * @var DateTimeFactory - */ - private $dateFactory; - - /** - * @var FlagManager - */ - private $flagManager; - /** * @var LoggerInterface */ private $log; /** - * @var SynchronizerIdentitiesPool - */ - private $synchronizerIdentitiesPool; - - /** - * @var RemoveObsoleteContentAsset + * @var SynchronizeIdentitiesPool */ - private $removeObsoleteContent; + private $synchronizeIdentitiesPool; /** - * @param RemoveObsoleteContentAsset $removeObsoleteContent - * @param DateTimeFactory $dateFactory - * @param FlagManager $flagManager * @param LoggerInterface $log - * @param SynchronizerIdentitiesPool $synchronizerIdentitiesPool + * @param SynchronizeIdentitiesPool $synchronizeIdentitiesPool */ public function __construct( - RemoveObsoleteContentAsset $removeObsoleteContent, - DateTimeFactory $dateFactory, - FlagManager $flagManager, LoggerInterface $log, - SynchronizerIdentitiesPool $synchronizerIdentitiesPool + SynchronizeIdentitiesPool $synchronizeIdentitiesPool ) { - $this->removeObsoleteContent = $removeObsoleteContent; - $this->dateFactory = $dateFactory; - $this->flagManager = $flagManager; $this->log = $log; - $this->synchronizerIdentitiesPool = $synchronizerIdentitiesPool; + $this->synchronizeIdentitiesPool = $synchronizeIdentitiesPool; } /** @@ -74,9 +48,9 @@ public function execute(array $mediaContentIdentities): void { $failed = []; - foreach ($this->synchronizerIdentitiesPool->get() as $name => $synchronizers) { + foreach ($this->synchronizeIdentitiesPool->get() as $name => $synchronizer) { try { - $synchronizers->execute($mediaContentIdentities); + $synchronizer->execute($mediaContentIdentities); } catch (\Exception $exception) { $this->log->critical($exception); $failed[] = $name; @@ -93,17 +67,5 @@ public function execute(array $mediaContentIdentities): void ) ); } - - $this->setLastExecutionTime(); - $this->removeObsoleteContent->execute(); - } - - /** - * Set last synchronizer execution time - */ - private function setLastExecutionTime(): void - { - $currentTime = $this->dateFactory->create()->gmtDate(); - $this->flagManager->saveFlag(self::LAST_EXECUTION_TIME_CODE, $currentTime); } } diff --git a/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php b/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizeIdentitiesPool.php similarity index 97% rename from app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php rename to app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizeIdentitiesPool.php index 2f4a503625e91..1ea957d5cd6e7 100644 --- a/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizerIdentitiesPool.php +++ b/app/code/Magento/MediaContentSynchronizationApi/Model/SynchronizeIdentitiesPool.php @@ -9,7 +9,7 @@ use Magento\MediaContentSynchronizationApi\Api\SynchronizeIdentitiesInterface; -class SynchronizerIdentitiesPool +class SynchronizeIdentitiesPool { /** * Content with assets synchronizers diff --git a/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml b/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml index d26ed06a42586..070f25f501712 100644 --- a/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronizationCatalog/etc/di.xml @@ -38,7 +38,7 @@ </argument> </arguments> </type> - <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool"> + <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizeIdentitiesPool"> <arguments> <argument name="synchronizers" xsi:type="array"> <item name="media_content_catalog" diff --git a/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml b/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml index 94e9c686aafbb..d6e7604c71d97 100644 --- a/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml +++ b/app/code/Magento/MediaContentSynchronizationCms/etc/di.xml @@ -14,7 +14,7 @@ </argument> </arguments> </type> - <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizerIdentitiesPool"> + <type name="Magento\MediaContentSynchronizationApi\Model\SynchronizeIdentitiesPool"> <arguments> <argument name="synchronizers" xsi:type="array"> <item name="media_content_cms" From 4c6606600f01d963dd2f56cd850558efe5ba25f6 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Sun, 6 Sep 2020 18:19:35 +0300 Subject: [PATCH 145/195] use depricated tag --- .../Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml index 29e100a7e9c37..4da29104797ac 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml @@ -7,10 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCmsBlockActionGroup"> - <annotations> - <description>DEPRECATED. Use AdminOpenCmsPageActionGroup instead.</description> - </annotations> + <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use AdminOpenCmsBlockActionGroup instead."> <arguments> <argument name="block_id" type="string"/> </arguments> From c379cea911ab649a4a7e4fde81dabbc310f7a0c9 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Mon, 7 Sep 2020 11:44:00 +0300 Subject: [PATCH 146/195] refactor tests --- .../ExportCoupons/ExportCouponsController.php | 87 ------------------- .../ExportCoupons/ExportCouponsCsvTest.php | 75 +++++++++++++++- .../ExportCoupons/ExportCouponsXmlTest.php | 75 +++++++++++++++- 3 files changed, 146 insertions(+), 91 deletions(-) delete mode 100644 dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php deleted file mode 100644 index 315860ea6973e..0000000000000 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsController.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -declare(strict_types=1); - -namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons; - -use Magento\Framework\App\ResourceConnection; -use Magento\SalesRule\Model\ResourceModel\Rule\Collection as RuleCollection; -use Magento\SalesRule\Model\Rule; -use Magento\TestFramework\Helper\Bootstrap; -use Magento\TestFramework\TestCase\AbstractBackendController; - -/** - * Abstract controller for test export coupon - */ -abstract class ExportCouponsController extends AbstractBackendController -{ - /** - * @var string - */ - protected $resource = 'Magento_SalesRule::quote'; - - /** - * @var Rule - */ - private $salesRule; - - /** - * @var ResourceConnection - */ - private $resourceConnection; - - /** - * @inheritdoc - */ - protected function setUp(): void - { - parent::setUp(); - $this->resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class); - $this->initSalesRule(); - } - - /** - * Prepare request - * - * @return void - */ - protected function prepareRequest(): void - { - $couponList = $this ->getCouponsIdList(); - if (count($couponList)) { - $this->getRequest()->setParams(['internal_ids' => $couponList[0]])->setMethod('POST'); - } - } - - /** - * Init current sales rule - * - * @return void - */ - private function initSalesRule(): void - { - /** @var RuleCollection $collection */ - $collection = Bootstrap::getObjectManager()->create(RuleCollection::class); - $collection->addFieldToFilter('name', 'Rule with coupon list'); - $this->salesRule = $collection->getFirstItem(); - } - - /** - * Retrieve id list of coupons - * - * @return array - */ - private function getCouponsIdList(): array - { - $select = $this->resourceConnection->getConnection() - ->select() - ->from($this->resourceConnection->getTableName('salesrule_coupon')) - ->columns(['coupon_id']) - ->where('rule_id=?', $this->salesRule->getId()); - - return $this->resourceConnection->getConnection()->fetchCol($select); - } -} diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php index 04d6ad570a883..954c23498ec66 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsCsvTest.php @@ -7,7 +7,11 @@ namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons; -use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons\ExportCouponsController; +use Magento\Framework\App\ResourceConnection; +use Magento\SalesRule\Model\ResourceModel\Rule\Collection as RuleCollection; +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\TestCase\AbstractBackendController; +use Magento\TestFramework\Helper\Bootstrap; /** * Test export coupon csv @@ -16,13 +20,80 @@ * @magentoAppArea adminhtml * @magentoDataFixture Magento/SalesRule/_files/cart_rule_with_coupon_list.php */ -class ExportCouponsCsvTest extends ExportCouponsController +class ExportCouponsCsvTest extends AbstractBackendController { /** * @var string */ protected $uri = 'backend/sales_rule/promo_quote/exportCouponsCsv'; + /** + * @var string + */ + protected $resource = 'Magento_SalesRule::quote'; + + /** + * @var Rule + */ + private $salesRule; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class); + $this->initSalesRule(); + } + + /** + * Prepare request + * + * @return void + */ + private function prepareRequest(): void + { + $couponList = $this->getCouponsIdList(); + if (count($couponList)) { + $this->getRequest()->setParams(['internal_ids' => $couponList[0]])->setMethod('POST'); + } + } + + /** + * Init current sales rule + * + * @return void + */ + private function initSalesRule(): void + { + /** @var RuleCollection $collection */ + $collection = Bootstrap::getObjectManager()->create(RuleCollection::class); + $collection->addFieldToFilter('name', 'Rule with coupon list'); + $this->salesRule = $collection->getFirstItem(); + } + + /** + * Retrieve id list of coupons + * + * @return array + */ + private function getCouponsIdList(): array + { + $select = $this->resourceConnection->getConnection() + ->select() + ->from($this->resourceConnection->getTableName('salesrule_coupon')) + ->columns(['coupon_id']) + ->where('rule_id=?', $this->salesRule->getId()); + + return $this->resourceConnection->getConnection()->fetchCol($select); + } + /** * Test export csv * diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php index 3484a6676d82e..d222b064a0d2c 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Controller/Adminhtml/Promo/Quote/ExportCoupons/ExportCouponsXmlTest.php @@ -7,7 +7,11 @@ namespace Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons; -use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\ExportCoupons\ExportCouponsController; +use Magento\Framework\App\ResourceConnection; +use Magento\SalesRule\Model\ResourceModel\Rule\Collection as RuleCollection; +use Magento\SalesRule\Model\Rule; +use Magento\TestFramework\TestCase\AbstractBackendController; +use Magento\TestFramework\Helper\Bootstrap; /** * Test export coupon xml @@ -16,13 +20,80 @@ * @magentoAppArea adminhtml * @magentoDataFixture Magento/SalesRule/_files/cart_rule_with_coupon_list.php */ -class ExportCouponsXmlTest extends ExportCouponsController +class ExportCouponsXmlTest extends AbstractBackendController { /** * @var string */ protected $uri = 'backend/sales_rule/promo_quote/exportCouponsXml'; + /** + * @var string + */ + protected $resource = 'Magento_SalesRule::quote'; + + /** + * @var Rule + */ + private $salesRule; + + /** + * @var ResourceConnection + */ + private $resourceConnection; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + $this->resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class); + $this->initSalesRule(); + } + + /** + * Prepare request + * + * @return void + */ + private function prepareRequest(): void + { + $couponList = $this->getCouponsIdList(); + if (count($couponList)) { + $this->getRequest()->setParams(['internal_ids' => $couponList[0]])->setMethod('POST'); + } + } + + /** + * Init current sales rule + * + * @return void + */ + private function initSalesRule(): void + { + /** @var RuleCollection $collection */ + $collection = Bootstrap::getObjectManager()->create(RuleCollection::class); + $collection->addFieldToFilter('name', 'Rule with coupon list'); + $this->salesRule = $collection->getFirstItem(); + } + + /** + * Retrieve id list of coupons + * + * @return array + */ + private function getCouponsIdList(): array + { + $select = $this->resourceConnection->getConnection() + ->select() + ->from($this->resourceConnection->getTableName('salesrule_coupon')) + ->columns(['coupon_id']) + ->where('rule_id=?', $this->salesRule->getId()); + + return $this->resourceConnection->getConnection()->fetchCol($select); + } + /** * Test export xml * From 2d1d097b39bdd94ef6e929c34fc721113a3505ea Mon Sep 17 00:00:00 2001 From: "Zeno F. Pensky" <zeno.pensky@sitewards.com> Date: Mon, 7 Sep 2020 12:55:02 +0200 Subject: [PATCH 147/195] Fix #29879 Breadcrump Undefined class constant 'XML_PATH_CATEGORY_URL_SUFFIX' When creating a plugin, that is calling the public functions for the product breadcrump they ending up throwing an error message, because this constants has been marked as private recently. This is fixing it with referencing to the current instance and all is working again. This fix is analog to another bug of the same type: https://github.com/magento/magento2/issues/28981 https://github.com/magento/magento2/pull/28797 This should resolve: https://github.com/magento/magento2/issues/29879 --- app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php index d3c8c406ee34d..2aa30fb18fdf4 100644 --- a/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php +++ b/app/code/Magento/Catalog/ViewModel/Product/Breadcrumbs.php @@ -71,7 +71,7 @@ public function __construct( public function getCategoryUrlSuffix() { return $this->scopeConfig->getValue( - static::XML_PATH_CATEGORY_URL_SUFFIX, + self::XML_PATH_CATEGORY_URL_SUFFIX, ScopeInterface::SCOPE_STORE ); } @@ -84,7 +84,7 @@ public function getCategoryUrlSuffix() public function isCategoryUsedInProductUrl(): bool { return $this->scopeConfig->isSetFlag( - static::XML_PATH_PRODUCT_USE_CATEGORIES, + self::XML_PATH_PRODUCT_USE_CATEGORIES, ScopeInterface::SCOPE_STORE ); } From edd340130b92015e3ecd6e03e93ca75111610afe Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Mon, 7 Sep 2020 13:55:55 +0300 Subject: [PATCH 148/195] improve array filter --- app/code/Magento/Widget/Model/Widget.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index 72f7dd77577af..c8b13ef028592 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -311,7 +311,7 @@ public function getWidgetDeclaration($type, $params = [], $asIs = true) $widget = $this->getConfigAsObject($type); $params = array_filter($params, function ($value) { - return $value !== null; + return $value !== null && $value !== ''; }); $directiveParams = ''; From 811861c0609edcb71e7ae7cbb8e019e8e53aaa65 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Mon, 7 Sep 2020 19:04:47 +0800 Subject: [PATCH 149/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - change to cache clean --- ...nMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml index aaba740930267..eb2f75dad0151 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml @@ -21,8 +21,8 @@ <before> <createData entity="TestSubCategory" stepKey="category"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> - <argument name="tags" value=""/> + <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanFullPageCache"> + <argument name="tags" value="config full_page"/> </actionGroup> </before> <after> From 9fd54363ced22b9d627f0768cf1820d125468e80 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Mon, 7 Sep 2020 14:38:11 +0300 Subject: [PATCH 150/195] refactor; add strict type --- .../Unit/Block/Product/View/Type/ConfigurableTest.php | 6 +++--- app/code/Magento/Tax/Pricing/Render/Adjustment.php | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php index 9abcfc6efaee3..08279c55c5b30 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Block/Product/View/Type/ConfigurableTest.php @@ -256,10 +256,10 @@ public function cacheKeyProvider(): array */ public function testGetCacheKeyInfo( array $expected, - string $priceCurrency = null, - string $customerGroupId = null + ?string $priceCurrency = null, + ?int $customerGroupId = null ): void { - $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) + $storeMock = $this->getMockBuilder(StoreInterface::class) ->setMethods(['getCurrentCurrency']) ->getMockForAbstractClass(); $storeMock->expects($this->any()) diff --git a/app/code/Magento/Tax/Pricing/Render/Adjustment.php b/app/code/Magento/Tax/Pricing/Render/Adjustment.php index ec451eb012878..0e5c619790a97 100644 --- a/app/code/Magento/Tax/Pricing/Render/Adjustment.php +++ b/app/code/Magento/Tax/Pricing/Render/Adjustment.php @@ -181,11 +181,10 @@ public function displayPriceExcludingTax() * * @return string */ - public function getDataPriceType() + public function getDataPriceType(): string { - if ($this->getData('price_type') && $this->getData('price_type') !== 'finalPrice') { - return 'base' . ucfirst($this->getData('price_type')); - } - return 'basePrice'; + return $this->amountRender->getPriceType() === 'finalPrice' + ? 'basePrice' + : 'base' . ucfirst($this->amountRender->getPriceType()); } } From 1e8f523ebff0817bd44c1a65bdb4a3c10582261b Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Mon, 7 Sep 2020 15:08:03 +0300 Subject: [PATCH 151/195] cover MFTF --- ...vigateToCurrencyRatesOptionActionGroup.xml | 15 ++++++++ .../Section/AdminCurrencyRatesSection.xml | 1 + ...encyOptionsSystemConfigExpandedTabTest.xml | 37 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencyRatesOptionActionGroup.xml create mode 100644 app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyOptionsSystemConfigExpandedTabTest.xml diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencyRatesOptionActionGroup.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencyRatesOptionActionGroup.xml new file mode 100644 index 0000000000000..39f37c745998e --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/ActionGroup/AdminNavigateToCurrencyRatesOptionActionGroup.xml @@ -0,0 +1,15 @@ +<?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="AdminNavigateToCurrencyRatesOptionActionGroup"> + <click selector="{{AdminCurrencyRatesSection.options}}" stepKey="clickOptionsButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml index bc80a51c41c47..10f345ec69369 100644 --- a/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Section/AdminCurrencyRatesSection.xml @@ -11,6 +11,7 @@ <section name="AdminCurrencyRatesSection"> <element name="import" type="button" selector="//button[@title='Import']"/> <element name="saveCurrencyRates" type="button" selector="//button[@title='Save Currency Rates']"/> + <element name="options" type="button" selector="//button[@title='Options']"/> <element name="oldRate" type="text" selector="//div[contains(@class, 'admin__field-note') and contains(text(), 'Old rate:')]/strong"/> <element name="rateService" type="select" selector="#rate_services"/> <element name="currencyRate" type="input" selector="input[name='rate[{{fistCurrency}}][{{secondCurrency}}]']" parameterized="true"/> diff --git a/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyOptionsSystemConfigExpandedTabTest.xml b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyOptionsSystemConfigExpandedTabTest.xml new file mode 100644 index 0000000000000..4e0eb72df3aa5 --- /dev/null +++ b/app/code/Magento/CurrencySymbol/Test/Mftf/Test/AdminCurrencyOptionsSystemConfigExpandedTabTest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCurrencyOptionsSystemConfigExpandedTabTest"> + <annotations> + <features value="Expanded tab on Currency Option page"/> + <stories value="Expanded tab"/> + <title value=" Verify the Currency Option tab expands automatically."/> + <description value="Check auto open the collapse on Currency Option page."/> + <severity value="MINOR"/> + <testCaseId value="MC-37425"/> + </annotations> + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + <actionGroup ref="AdminNavigateMenuActionGroup" stepKey="navigateToStoresCurrencyRatesPage"> + <argument name="menuUiId" value="{{AdminMenuStores.dataUiId}}"/> + <argument name="submenuUiId" value="{{AdminMenuStoresCurrencyCurrencyRates.dataUiId}}"/> + </actionGroup> + <actionGroup ref="AdminNavigateToCurrencyRatesOptionActionGroup" stepKey="navigateToOptions" /> + <grabAttributeFrom selector="{{CurrencySetupSection.currencyOptions}}" userInput="class" stepKey="grabClass"/> + <assertStringContainsString stepKey="assertClass"> + <actualResult type="string">{$grabClass}</actualResult> + <expectedResult type="string">open</expectedResult> + </assertStringContainsString> + </test> +</tests> \ No newline at end of file From d0ba33745ab8bedcd4a18f6757c7d5dd9ac4712a Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Mon, 7 Sep 2020 16:17:13 +0300 Subject: [PATCH 152/195] added AdminDeleteTaxRateActionGroup --- .../AdminDeleteTaxRateActionGroup.xml | 28 +++++++++++++++++++ .../AdminCreateTaxRateAllPostCodesTest.xml | 9 ++---- .../Test/AdminCreateTaxRateLargeRateTest.xml | 9 ++---- ...AdminCreateTaxRateSpecificPostcodeTest.xml | 9 ++---- ...dminCreateTaxRateWiderZipCodeRangeTest.xml | 9 ++---- .../AdminCreateTaxRateZipCodeRangeTest.xml | 9 ++---- .../Mftf/Test/DeleteTaxRateEntityTest.xml | 11 +++----- 7 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml new file mode 100644 index 0000000000000..b609ef8827764 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml @@ -0,0 +1,28 @@ +<?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="AdminDeleteTaxRateActionGroup"> + <annotations> + <description>Goes to the Admin Tax Rate grid page. Deletes the provided Tax Rate Code.</description> + </annotations> + <arguments> + <argument name="taxRateCode" type="string"/> + </arguments> + + <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> + <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{taxRateCode}}" stepKey="fillNameFilter"/> + <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForTaxRuleSearch"/> + <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> + <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml index be7185a5166a2..f2f7d78ea2650 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml @@ -23,12 +23,9 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters2"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{SimpleTaxRate.code}}" stepKey="fillNameFilter"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> + </actionGroup> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml index 89cfdd0eb9943..144f6b644d168 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml @@ -23,12 +23,9 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters2"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{SimpleTaxRate.code}}" stepKey="fillNameFilter"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> + </actionGroup> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml index a3386cada436f..49a89b33d55d0 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml @@ -23,12 +23,9 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters2"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{SimpleTaxRate.code}}" stepKey="fillNameFilter"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> + </actionGroup> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml index 6ceeae953139c..620ad1909c6f8 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml @@ -23,12 +23,9 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters2"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{SimpleTaxRate.code}}" stepKey="fillNameFilter"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> + </actionGroup> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml index 4f9e876fed696..fe8f67f880f49 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml @@ -23,12 +23,9 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters2"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{SimpleTaxRate.code}}" stepKey="fillNameFilter"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow1"/> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> + </actionGroup> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml index eb774297b8322..baae945bd8d1d 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml @@ -25,14 +25,11 @@ <!-- Search the tax rate on tax grid page --> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters1"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="$$initialTaxRate.code$$" stepKey="fillCode"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch1"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow1"/> - <!-- Delete values on the tax rate form page --> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <argument name="taxRateCode" value="{{initialTaxRate.code}}" /> + </actionGroup> + <see selector="{{AdminMessagesSection.success}}" userInput="You Deleted the tax rate." stepKey="seeSuccess1"/> <!-- Confirm Deleted TaxIdentifier(from the above step) on the tax rate grid page --> From 879a74ea12c529f9a93e64ec1c95bf37164f5d80 Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@gmail.com> Date: Mon, 7 Sep 2020 16:19:41 +0300 Subject: [PATCH 153/195] adobe-stock-integration#1792: cover by mftf test --- ...SameImageDeleteFromTemporaryFolderTest.xml | 48 +++++++++++++++++++ ...diaGalleryCatalogUiCategoryGridSection.xml | 1 + ...sertMediaGalleryEmptyFolderActionGroup.xml | 17 +++++++ ...lleryExpandCatalogTmpFolderActionGroup.xml | 21 ++++++++ ...lleryFolderSelectByFullPathActionGroup.xml | 16 +++++++ 5 files changed, 103 insertions(+) create mode 100644 app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup.xml create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml diff --git a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml new file mode 100644 index 0000000000000..c138ea81e7f7b --- /dev/null +++ b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminUploadSameImageDeleteFromTemporaryFolderTest"> + <annotations> + <features value="AdminUploadSameImageDeleteFromTemporaryFolderTest"/> + <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1792"/> + <title value="Image is deleted from tmp folder if is uploaded second time"/> + <description value="Image is deleted from tmp folder if is uploaded second time"/> + <severity value="CRITICAL"/> + <group value="media_gallery_ui"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="category"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + </after> + + <!-- Upload test image to category twice --> + <actionGroup ref="AdminOpenCategoryGridPageActionGroup" stepKey="openCategoryPage"/> + <actionGroup ref="AdminEditCategoryInGridPageActionGroup" stepKey="editCategoryItem"> + <argument name="categoryName" value="$category.name$"/> + </actionGroup> + <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImage"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryForm"/> + <actionGroup ref="AddCategoryImageActionGroup" stepKey="addCategoryImageSecondTime"/> + <actionGroup ref="AdminSaveCategoryFormActionGroup" stepKey="saveCategoryFormSecondTime"/> + + <!-- Open tmp/category folder --> + <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGallery"/> + <actionGroup ref="AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup" stepKey="expandTmpFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectByFullPathActionGroup" stepKey="selectCategoryFolder"> + <argument name="name" value="catalog/tmp/category"/> + </actionGroup> + + <!-- Asset folder is empty --> + <actionGroup ref="AdminAssertMediaGalleryEmptyFolderActionGroup" stepKey="assertEmptyFolder"/> + </test> +</tests> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml index f65ec84bc2ec8..9baa4bd3c3268 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml @@ -14,5 +14,6 @@ <element name="image" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Image')]/preceding-sibling::th) +1]//img[contains(@src, '{{file}}')]" parameterized="true"/> <element name="columnValue" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{columnName}}')]/preceding-sibling::th) +1 ]//div" parameterized="true"/> <element name="edit" type="button" selector="//tr[td//text()[contains(., '{{categoryName}}')]]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Action')]/preceding-sibling::th) +1 ]//*[text()='{{actionButton}}']" parameterized="true"/> + <element name="noDataMessage" type="text" selector="div.no-data-message-container"/> </section> </sections> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml new file mode 100644 index 0000000000000..e6f8327b814a5 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml @@ -0,0 +1,17 @@ +<?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="AdminAssertMediaGalleryEmptyFolderActionGroup"> + <annotations> + <description>Requires select folder in directory tree. Assert that selected folder is empty.</description> + </annotations> + + <seeElement selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.noDataMessage}}" stepKey="assertNoDataMessageDisplayed" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup.xml new file mode 100644 index 0000000000000..db9d1853df583 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup.xml @@ -0,0 +1,21 @@ +<?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="AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup"> + <annotations> + <description>Expand media gallery tmp folder tree</description> + </annotations> + <waitForLoadingMaskToDisappear stepKey="waitLoadingMask"/> + <conditionalClick selector="//li[@id='catalog']/ins" dependentSelector="//li[@id='catalog']/ul" visible="false" stepKey="expandCatalog"/> + <wait time="2" stepKey="waitCatalogExpanded"/> + <conditionalClick selector="//li[@id='catalog/tmp']/ins" dependentSelector="//li[@id='catalog/tmp']/ul" visible="false" stepKey="expandTmp"/> + <wait time="2" stepKey="waitTmpExpanded"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml new file mode 100644 index 0000000000000..717278d5c8f26 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AdminMediaGalleryFolderSelectByFullPathActionGroup" + extends="AdminMediaGalleryFolderSelectActionGroup"> + <remove keyForRemoval="selectFolder"/> + <click selector="//li[@id='{{name}}']" stepKey="selectSubFolder" after="waitBeforeClickOnFolder"/> + </actionGroup> +</actionGroups> From 3095bbef26969e44446247f18fa0707f2aab2894 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Mon, 7 Sep 2020 16:38:33 +0300 Subject: [PATCH 154/195] add clear description --- .../Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml index 4da29104797ac..0a2eaacfc613c 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use AdminOpenCmsBlockActionGroup instead."> + <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlockActionGroup.xml instead."> <arguments> <argument name="block_id" type="string"/> </arguments> From 5b93a7692be050669f511d679e12c2ae75c7bc8e Mon Sep 17 00:00:00 2001 From: mastiuhin-olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Mon, 7 Sep 2020 18:57:11 +0300 Subject: [PATCH 155/195] MC-35416: It is not possible to Manage Shopping Cart from Customer edit page after adding product to Wish List --- .../Model/ResourceModel/Item/Collection.php | 2 +- .../Model/ResourceModel/Item/CollectionTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php index 5d9b1911bc292..73b7b6c827ec1 100644 --- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php +++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php @@ -398,7 +398,7 @@ protected function _renderFiltersBefore() $availableProductTypes = $this->salesConfig->getAvailableProductTypes(); $this->getSelect()->join( ['cat_prod' => $this->getTable('catalog_product_entity')], - $this->getConnection()->quoteInto('cat_prod.type_id IN (?)', $availableProductTypes), + $this->getConnection()->quoteInto('cat_prod.type_id IN (?) AND main_table.product_id = cat_prod.entity_id', $availableProductTypes), [] ); } diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php index 9a95ed4fd462d..e347ee88af902 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php @@ -62,6 +62,20 @@ public function testLoadedProductAttributes() $this->assertEquals('Short description', $productOnWishlist->getData('short_description')); } + /** + * Tests collection load. + * Tests collection load method when product salable filter flag is setted to true + * and few products are present. + * + * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + */ + public function testGonnaCutYouDown() + { + $this->itemCollection->setSalableFilter(true); + $this->itemCollection->load(); + } + /** * @param array $attributes */ From c1410e7da82dad289b5efe172c9c0f5f67a13dd7 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Tue, 8 Sep 2020 00:28:52 +0800 Subject: [PATCH 156/195] magento/adobe-stock-integration#1795: [MFTF] Unskip AdminMediaGalleryCatalogUiUsedInProductFilterTest - modified sorting mftf tests --- ...diaGallerySortByDirectoryAscendingTest.xml | 47 +++++------------- ...iaGallerySortByDirectoryDescendingTest.xml | 48 +++++-------------- .../AdminMediaGallerySortByNameAToZTest.xml | 47 +++++------------- .../AdminMediaGallerySortByNameZToATest.xml | 47 +++++------------- ...AdminMediaGallerySortByNewestFirstTest.xml | 46 +++++------------- ...AdminMediaGallerySortByOldestFirstTest.xml | 46 +++++------------- 6 files changed, 78 insertions(+), 203 deletions(-) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryAscendingTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryAscendingTest.xml index 4dbf3da0752b2..365252095d49e 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryAscendingTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryAscendingTest.xml @@ -24,58 +24,37 @@ <after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFirstFolderForDelete"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectParentFolderForDelete"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFirstFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFirstFolderWasDeleted"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectSecondFolderForDelete"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteSecondFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertSecondFolderWasDeleted"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteParentFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertParentFolderWasDeleted"> + <argument name="name" value="parentFolder"/> </actionGroup> </after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openFirstNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createFirstNewFolder"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openParentFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createParentFolder"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertFirstNewFolderCreated"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertParentFolderCreated"> + <argument name="name" value="parentFolder"/> </actionGroup> - + <waitForPageLoad stepKey="waitForGridToLoadAfterParentFolderCreated"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadSecondImage"> <argument name="image" value="ImageUpload_1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openSecondNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createSecondNewFolder"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertSecondNewFolderCreated"> - <argument name="name" value="secondFolder"/> - </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadThirdImage"> <argument name="image" value="ImageUpload1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="secondWaitForGridToLoad"/> - + <waitForPageLoad stepKey="waitForGridToLoad"/> <actionGroup ref="AdminEnhancedMediaGalleryClickSortActionGroup" stepKey="sortByDirectoryAscending"> <argument name="sortName" value="directory_asc"/> </actionGroup> - <actionGroup ref="AssertAdminEnhancedMediaGallerySortByActionGroup" stepKey="assertImagePositionAfterSortByDirectoryAscending"> <argument name="firstImageFile" value="{{ImageUpload_1.file}}"/> <argument name="secondImageFile" value="{{ImageUpload.file}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryDescendingTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryDescendingTest.xml index 025da24511b73..85c468996d515 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryDescendingTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByDirectoryDescendingTest.xml @@ -21,62 +21,40 @@ <before> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> - <after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFirstFolderForDelete"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFirstFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFirstFolderWasDeleted"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectParentFolderForDelete"> + <argument name="name" value="parentFolder"/> </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectSecondFolderForDelete"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteSecondFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertSecondFolderWasDeleted"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteParentFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertParentFolderWasDeleted"> + <argument name="name" value="parentFolder"/> </actionGroup> </after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openFirstNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createFirstNewFolder"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openParentFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createParentFolder"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertFirstNewFolderCreated"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertParentFolderCreated"> + <argument name="name" value="parentFolder"/> </actionGroup> - + <waitForPageLoad stepKey="waitForGridToLoadAfterParentFolderCreated"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadSecondImage"> <argument name="image" value="ImageUpload_1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openSecondNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createSecondNewFolder"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertSecondNewFolderCreated"> - <argument name="name" value="secondFolder"/> - </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadThirdImage"> <argument name="image" value="ImageUpload1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="secondWaitForGridToLoad"/> - + <waitForPageLoad stepKey="waitForGridToLoad"/> <actionGroup ref="AdminEnhancedMediaGalleryClickSortActionGroup" stepKey="sortByDirectoryDescending"> <argument name="sortName" value="directory_desc"/> </actionGroup> - <actionGroup ref="AssertAdminEnhancedMediaGallerySortByActionGroup" stepKey="assertImagePositionAfterSortByDirectoryDescending"> <argument name="firstImageFile" value="{{ImageUpload1.value}}"/> <argument name="secondImageFile" value="{{ImageUpload.file}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameAToZTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameAToZTest.xml index da0d8a18b75e4..9dca51065124f 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameAToZTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameAToZTest.xml @@ -24,58 +24,37 @@ <after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFirstFolderForDelete"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectParentFolderForDelete"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFirstFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFirstFolderWasDeleted"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectSecondFolderForDelete"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteSecondFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertSecondFolderWasDeleted"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteParentFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertParentFolderWasDeleted"> + <argument name="name" value="parentFolder"/> </actionGroup> </after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openFirstNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createFirstNewFolder"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openParentFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createParentFolder"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertFirstNewFolderCreated"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertParentFolderCreated"> + <argument name="name" value="parentFolder"/> </actionGroup> - + <waitForPageLoad stepKey="waitForGridToLoadAfterParentFolderCreated"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadSecondImage"> <argument name="image" value="ImageUpload_1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openSecondNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createSecondNewFolder"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertSecondNewFolderCreated"> - <argument name="name" value="secondFolder"/> - </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadThirdImage"> <argument name="image" value="ImageUpload1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="secondWaitForGridToLoad"/> - + <waitForPageLoad stepKey="waitForGridToLoad"/> <actionGroup ref="AdminEnhancedMediaGalleryClickSortActionGroup" stepKey="sortByNameAToZ"> <argument name="sortName" value="name_az"/> </actionGroup> - <actionGroup ref="AssertAdminEnhancedMediaGallerySortByActionGroup" stepKey="assertImagePositionAfterSortByNameAToZ"> <argument name="firstImageFile" value="{{ImageUpload.file}}"/> <argument name="secondImageFile" value="{{ImageUpload_1.file}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameZToATest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameZToATest.xml index 4b5086e5d63ff..71d2020d08658 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameZToATest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNameZToATest.xml @@ -24,58 +24,37 @@ <after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFirstFolderForDelete"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectParentFolderForDelete"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFirstFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFirstFolderWasDeleted"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectSecondFolderForDelete"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteSecondFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertSecondFolderWasDeleted"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteParentFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertParentFolderWasDeleted"> + <argument name="name" value="parentFolder"/> </actionGroup> </after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openFirstNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createFirstNewFolder"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openParentFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createParentFolder"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertFirstNewFolderCreated"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertParentFolderCreated"> + <argument name="name" value="parentFolder"/> </actionGroup> - + <waitForPageLoad stepKey="waitForGridToLoadAfterParentFolderCreated"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadSecondImage"> <argument name="image" value="ImageUpload_1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openSecondNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createSecondNewFolder"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertSecondNewFolderCreated"> - <argument name="name" value="secondFolder"/> - </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadThirdImage"> <argument name="image" value="ImageUpload1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="secondWaitForGridToLoad"/> - + <waitForPageLoad stepKey="waitForGridToLoad"/> <actionGroup ref="AdminEnhancedMediaGalleryClickSortActionGroup" stepKey="sortByNameZToA"> <argument name="sortName" value="name_za"/> </actionGroup> - <actionGroup ref="AssertAdminEnhancedMediaGallerySortByActionGroup" stepKey="assertImagePositionAfterSortByNameZToA"> <argument name="firstImageFile" value="{{ImageUpload1.value}}"/> <argument name="secondImageFile" value="{{ImageUpload_1.file}}"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNewestFirstTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNewestFirstTest.xml index 4274b26d5770f..3da0546db090a 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNewestFirstTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByNewestFirstTest.xml @@ -21,57 +21,37 @@ <before> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> - <after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFirstFolderForDelete"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFirstFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFirstFolderWasDeleted"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectSecondFolderForDelete"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectParentFolderForDelete"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteSecondFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertSecondFolderWasDeleted"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteParentFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertParentFolderWasDeleted"> + <argument name="name" value="parentFolder"/> </actionGroup> </after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openFirstNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createFirstNewFolder"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openParentFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createParentFolder"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertFirstNewFolderCreated"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertParentFolderCreated"> + <argument name="name" value="parentFolder"/> </actionGroup> - + <waitForPageLoad stepKey="waitForGridToLoadAfterParentFolderCreated"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadSecondImage"> <argument name="image" value="ImageUpload_1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openSecondNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createSecondNewFolder"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertSecondNewFolderCreated"> - <argument name="name" value="secondFolder"/> - </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadThirdImage"> <argument name="image" value="ImageUpload1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="secondWaitForGridToLoad"/> + <waitForPageLoad stepKey="waitForGridToLoad"/> <actionGroup ref="AdminEnhancedMediaGalleryClickSortActionGroup" stepKey="sortByNewestFirst"> <argument name="sortName" value="newest_first"/> </actionGroup> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByOldestFirstTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByOldestFirstTest.xml index e67fdcfcf40b3..e6191d2d02287 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByOldestFirstTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGallerySortByOldestFirstTest.xml @@ -24,54 +24,34 @@ <after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFirstFolderForDelete"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectParentFolderForDelete"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFirstFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFirstFolderWasDeleted"> - <argument name="name" value="firstFolder"/> - </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> - <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectSecondFolderForDelete"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteSecondFolder"/> - <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertSecondFolderWasDeleted"> - <argument name="name" value="secondFolder"/> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteParentFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertParentFolderWasDeleted"> + <argument name="name" value="parentFolder"/> </actionGroup> </after> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openStandaloneMediaGalleryPage"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openFirstNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createFirstNewFolder"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openParentFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createParentFolder"> + <argument name="name" value="parentFolder"/> </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertFirstNewFolderCreated"> - <argument name="name" value="firstFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertParentFolderCreated"> + <argument name="name" value="parentFolder"/> </actionGroup> - + <waitForPageLoad stepKey="waitForGridToLoadAfterParentFolderCreated"/> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadSecondImage"> <argument name="image" value="ImageUpload_1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="waitForGridToLoad"/> - <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openSecondNewFolderForm"/> - <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createSecondNewFolder"> - <argument name="name" value="secondFolder"/> - </actionGroup> - <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertSecondNewFolderCreated"> - <argument name="name" value="secondFolder"/> - </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadThirdImage"> <argument name="image" value="ImageUpload1"/> </actionGroup> - - <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="secondResetAdminDataGridToDefaultView"/> - <waitForPageLoad stepKey="secondWaitForGridToLoad"/> - + <waitForPageLoad stepKey="waitForGridToLoad"/> <actionGroup ref="AdminEnhancedMediaGalleryClickSortActionGroup" stepKey="sortByOldestFirst"> <argument name="sortName" value="oldest_first"/> </actionGroup> From 7296cdfd222561023571c1a6294115a0d5d512e2 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Mon, 7 Sep 2020 17:45:35 +0100 Subject: [PATCH 157/195] magento/magento2#29715: Removed redundant dependency --- app/code/Magento/MediaGalleryUiApi/composer.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUiApi/composer.json b/app/code/Magento/MediaGalleryUiApi/composer.json index b3b3cc5092cab..d577f50523f13 100644 --- a/app/code/Magento/MediaGalleryUiApi/composer.json +++ b/app/code/Magento/MediaGalleryUiApi/composer.json @@ -3,7 +3,9 @@ "description": "Magento module responsible for the media gallery UI implementation API", "require": { "php": "~7.3.0||~7.4.0", - "magento/framework": "*", + "magento/framework": "*" + }, + "suggest": { "magento/module-cms": "*" }, "type": "magento2-module", From 15e4522a7a18f251105360064eb6ff7be5287537 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Mon, 7 Sep 2020 17:52:58 +0100 Subject: [PATCH 158/195] magento/magento2#29889: Fixed static tests --- .../MediaGalleryRenditions/Plugin/RemoveRenditions.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php index f05c34e703abe..f0ba8c3533722 100644 --- a/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php +++ b/app/code/Magento/MediaGalleryRenditions/Plugin/RemoveRenditions.php @@ -7,7 +7,6 @@ namespace Magento\MediaGalleryRenditions\Plugin; -use Magento\Catalog\Helper\Data as CatalogHelper; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Filesystem; use Magento\MediaGalleryApi\Api\DeleteAssetsByPathsInterface; @@ -19,11 +18,6 @@ */ class RemoveRenditions { - /** - * @var CatalogHelper - */ - private $catalogHelper; - /** * @var GetRenditionPathInterface */ @@ -45,12 +39,10 @@ class RemoveRenditions * @param LoggerInterface $log */ public function __construct( - CatalogHelper $catalogHelper, GetRenditionPathInterface $getRenditionPath, Filesystem $filesystem, LoggerInterface $log ) { - $this->catalogHelper = $catalogHelper; $this->getRenditionPath = $getRenditionPath; $this->filesystem = $filesystem; $this->log = $log; From caaf4699d348236e951ea094a886b38e1391c6bf Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Mon, 7 Sep 2020 22:23:33 +0300 Subject: [PATCH 159/195] add code review recommendations --- .../AdminDeleteTaxRateActionGroup.xml | 16 +++---------- .../AdminFilterTaxRateByCodeActionGroup.xml | 23 +++++++++++++++++++ .../AdminCreateTaxRateAllPostCodesTest.xml | 7 +++++- .../Test/AdminCreateTaxRateLargeRateTest.xml | 7 +++++- ...AdminCreateTaxRateSpecificPostcodeTest.xml | 7 +++++- ...dminCreateTaxRateWiderZipCodeRangeTest.xml | 7 +++++- .../AdminCreateTaxRateZipCodeRangeTest.xml | 7 +++++- .../Mftf/Test/DeleteTaxRateEntityTest.xml | 10 +++++--- .../AdminClickRowInGridActionGroup.xml | 19 +++++++++++++++ .../Section/AdminDataGridTableSection.xml | 2 +- 10 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml create mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.xml diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml index b609ef8827764..1aab6ea2c4eec 100644 --- a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminDeleteTaxRateActionGroup.xml @@ -10,19 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminDeleteTaxRateActionGroup"> <annotations> - <description>Goes to the Admin Tax Rate grid page. Deletes the provided Tax Rate Code.</description> + <description>Delete Tax Rate.</description> </annotations> - <arguments> - <argument name="taxRateCode" type="string"/> - </arguments> - - <click selector="{{AdminDataGridHeaderSection.clearFilters}}" stepKey="clickClearFilters"/> - <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{taxRateCode}}" stepKey="fillNameFilter"/> - <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> - <waitForPageLoad stepKey="waitForTaxRuleSearch"/> - <click selector="{{AdminTaxRateGridSection.nthRow('1')}}" stepKey="clickFirstRow"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <click selector="{{AdminTaxRateFormSection.deleteRate}}" stepKey="clickDeleteRate"/> - <click selector="{{AdminTaxRateFormSection.ok}}" stepKey="clickOk"/> + <click selector="{{AdminMainActionsSection.delete}}" stepKey="clickDeleteRate"/> + <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="clickOk"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml new file mode 100644 index 0000000000000..2b110e969b113 --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminFilterTaxRateByCodeActionGroup.xml @@ -0,0 +1,23 @@ +<?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="AdminFilterTaxRateByCodeActionGroup"> + <annotations> + <description>Filter Tax Rates by tax rate code.</description> + </annotations> + <arguments> + <argument name="taxRateCode" type="string"/> + </arguments> + + <fillField selector="{{AdminTaxRateGridSection.filterByTaxIdentifier}}" userInput="{{taxRateCode}}" stepKey="fillNameFilter"/> + <click selector="{{AdminTaxRateGridSection.search}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForTaxRuleSearch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml index f2f7d78ea2650..494c0ae74c375 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml @@ -23,9 +23,14 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clickClearFilters"/> + <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> + <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> + <argument name="row_number" value="1" /> + </actionGroup> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml index 144f6b644d168..e195a0b86cb26 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml @@ -23,9 +23,14 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clickClearFilters"/> + <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> + <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> + <argument name="row_number" value="1" /> + </actionGroup> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml index 49a89b33d55d0..bff1bb95e9540 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml @@ -23,9 +23,14 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clickClearFilters"/> + <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> + <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> + <argument name="row_number" value="1" /> + </actionGroup> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml index 620ad1909c6f8..570608bb6adf6 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml @@ -23,9 +23,14 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clickClearFilters"/> + <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> + <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> + <argument name="row_number" value="1" /> + </actionGroup> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml index fe8f67f880f49..a8e47b77e27c0 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml @@ -23,9 +23,14 @@ </before> <after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex"/> - <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clickClearFilters"/> + <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> + <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> + <argument name="row_number" value="1" /> + </actionGroup> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml index baae945bd8d1d..341b2e1aa6344 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml @@ -25,10 +25,14 @@ <!-- Search the tax rate on tax grid page --> <actionGroup ref="AdminTaxRateGridOpenPageActionGroup" stepKey="goToTaxRateIndex1"/> - - <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"> - <argument name="taxRateCode" value="{{initialTaxRate.code}}" /> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clickClearFilters"/> + <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> + <argument name="taxRateCode" value="$$initialTaxRate.code$$" /> + </actionGroup> + <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> + <argument name="row_number" value="1" /> </actionGroup> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> <see selector="{{AdminMessagesSection.success}}" userInput="You Deleted the tax rate." stepKey="seeSuccess1"/> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.xml new file mode 100644 index 0000000000000..c538a6d530645 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.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="AdminClickRowInGridActionGroup"> + <arguments> + <argument name="row_number" type="string"/> + </arguments> + + <click selector="{{AdminDataGridTableSection.row(row_number)}}" stepKey="clickOnFirstRow"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml index fcee31c0bd80c..11e42353a0663 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml @@ -13,7 +13,7 @@ <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="column" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{col}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> <element name="rowCheckbox" type="checkbox" selector="table.data-grid tbody > tr:nth-of-type({{row}}) td.data-grid-checkbox-cell input" parameterized="true"/> - <element name="row" type="text" selector="table.data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> + <element name="row" type="text" selector="table.data-grid tbody > tr:nth-of-type({{row}})" parameterized="true" timeout="30"/> <element name="rows" type="text" selector="table.data-grid tbody > tr.data-row"/> <!--Specific cell e.g. {{Section.gridCell('1', 'Name')}}--> <element name="gridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> From 7f0a49594f61ddfe2c0fa57c0fb7b8e9fbdc3329 Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Tue, 8 Sep 2020 09:41:32 +0300 Subject: [PATCH 160/195] adobe-stock-integration#1792: update after review --- ...inUploadSameImageDeleteFromTemporaryFolderTest.xml | 6 +++--- .../AdminMediaGalleryCatalogUiCategoryGridSection.xml | 1 - ...ml => AdminAssertMediaGalleryEmptyActionGroup.xml} | 4 ++-- ...nMediaGalleryFolderSelectByFullPathActionGroup.xml | 11 +++++++---- ...sertAdminEnhancedMediaGallerySortByActionGroup.xml | 6 +++--- ...onSection.xml => AdminMediaGalleryGridSection.xml} | 3 ++- 6 files changed, 17 insertions(+), 14 deletions(-) rename app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/{AdminAssertMediaGalleryEmptyFolderActionGroup.xml => AdminAssertMediaGalleryEmptyActionGroup.xml} (71%) rename app/code/Magento/MediaGalleryUi/Test/Mftf/Section/{AdminEnhancedMediaGalleryGridImagePositionSection.xml => AdminMediaGalleryGridSection.xml} (77%) diff --git a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml index c138ea81e7f7b..252027b27bf6d 100644 --- a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml +++ b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml @@ -39,10 +39,10 @@ <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGallery"/> <actionGroup ref="AdminEnhancedMediaGalleryExpandCatalogTmpFolderActionGroup" stepKey="expandTmpFolder"/> <actionGroup ref="AdminMediaGalleryFolderSelectByFullPathActionGroup" stepKey="selectCategoryFolder"> - <argument name="name" value="catalog/tmp/category"/> + <argument name="path" value="catalog/tmp/category"/> </actionGroup> - <!-- Asset folder is empty --> - <actionGroup ref="AdminAssertMediaGalleryEmptyFolderActionGroup" stepKey="assertEmptyFolder"/> + <!-- Assert folder is empty --> + <actionGroup ref="AdminAssertMediaGalleryEmptyActionGroup" stepKey="assertEmptyFolder"/> </test> </tests> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml index 9baa4bd3c3268..f65ec84bc2ec8 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Section/AdminMediaGalleryCatalogUiCategoryGridSection.xml @@ -14,6 +14,5 @@ <element name="image" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Image')]/preceding-sibling::th) +1]//img[contains(@src, '{{file}}')]" parameterized="true"/> <element name="columnValue" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{columnName}}')]/preceding-sibling::th) +1 ]//div" parameterized="true"/> <element name="edit" type="button" selector="//tr[td//text()[contains(., '{{categoryName}}')]]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., 'Action')]/preceding-sibling::th) +1 ]//*[text()='{{actionButton}}']" parameterized="true"/> - <element name="noDataMessage" type="text" selector="div.no-data-message-container"/> </section> </sections> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyActionGroup.xml similarity index 71% rename from app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml rename to app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyActionGroup.xml index e6f8327b814a5..c212092b657fd 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyFolderActionGroup.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminAssertMediaGalleryEmptyActionGroup.xml @@ -7,11 +7,11 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminAssertMediaGalleryEmptyFolderActionGroup"> + <actionGroup name="AdminAssertMediaGalleryEmptyActionGroup"> <annotations> <description>Requires select folder in directory tree. Assert that selected folder is empty.</description> </annotations> - <seeElement selector="{{AdminMediaGalleryCatalogUiCategoryGridSection.noDataMessage}}" stepKey="assertNoDataMessageDisplayed" /> + <seeElement selector="{{AdminMediaGalleryGridSection.noDataMessage}}" stepKey="assertNoDataMessageDisplayed" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml index 717278d5c8f26..49aa45426152c 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AdminMediaGalleryFolderSelectByFullPathActionGroup.xml @@ -8,9 +8,12 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminMediaGalleryFolderSelectByFullPathActionGroup" - extends="AdminMediaGalleryFolderSelectActionGroup"> - <remove keyForRemoval="selectFolder"/> - <click selector="//li[@id='{{name}}']" stepKey="selectSubFolder" after="waitBeforeClickOnFolder"/> + <actionGroup name="AdminMediaGalleryFolderSelectByFullPathActionGroup"> + <arguments> + <argument name="path" type="string"/> + </arguments> + <wait time="2" stepKey="waitBeforeClickOnFolder"/> + <click selector="//li[@id='{{path}}']" stepKey="selectSubFolder" after="waitBeforeClickOnFolder"/> + <waitForLoadingMaskToDisappear stepKey="waitForFolderContents"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminEnhancedMediaGallerySortByActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminEnhancedMediaGallerySortByActionGroup.xml index 451ef81f0ff9f..53781a65e4898 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminEnhancedMediaGallerySortByActionGroup.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminEnhancedMediaGallerySortByActionGroup.xml @@ -18,11 +18,11 @@ <argument name="thirdImageFile" type="string"/> </arguments> - <grabAttributeFrom selector="{{AdminEnhancedMediaGalleryGridImagePositionSection.nthImageInGrid('0')}}" userInput="src" + <grabAttributeFrom selector="{{AdminMediaGalleryGridSection.nthImageInGrid('0')}}" userInput="src" stepKey="getFirstImageSrcAfterSort"/> - <grabAttributeFrom selector="{{AdminEnhancedMediaGalleryGridImagePositionSection.nthImageInGrid('1')}}" userInput="src" + <grabAttributeFrom selector="{{AdminMediaGalleryGridSection.nthImageInGrid('1')}}" userInput="src" stepKey="getSecondImageSrcAfterSort"/> - <grabAttributeFrom selector="{{AdminEnhancedMediaGalleryGridImagePositionSection.nthImageInGrid('2')}}" userInput="src" + <grabAttributeFrom selector="{{AdminMediaGalleryGridSection.nthImageInGrid('2')}}" userInput="src" stepKey="getThirdImageSrcAfterSort"/> <assertStringContainsString stepKey="assertFirstImagePositionAfterSort"> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryGridImagePositionSection.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminMediaGalleryGridSection.xml similarity index 77% rename from app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryGridImagePositionSection.xml rename to app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminMediaGalleryGridSection.xml index 943f29d5fa851..f35a32b6d3a37 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryGridImagePositionSection.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminMediaGalleryGridSection.xml @@ -7,7 +7,8 @@ --> <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="AdminEnhancedMediaGalleryGridImagePositionSection"> + <section name="AdminMediaGalleryGridSection"> + <element name="noDataMessage" type="text" selector="div.no-data-message-container"/> <element name="nthImageInGrid" type="text" selector="div[class='masonry-image-column'][data-repeat-index='{{row}}'] img" parameterized="true"/> </section> </sections> From ace8a6fd54d920b01533668f7f0b2942ec4b3def Mon Sep 17 00:00:00 2001 From: Vadim Malesh <51680850+engcom-Charlie@users.noreply.github.com> Date: Tue, 8 Sep 2020 09:48:44 +0300 Subject: [PATCH 161/195] add testCaseId --- .../Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml index 4cfdc8197ad84..bc379ec424fce 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/StoreFrontWidgetTitleWithReservedCharsTest.xml @@ -14,6 +14,7 @@ <title value="Create CMS Page via the Admin when widget title contains reserved chairs"/> <description value="See CMS Page title on store front page if titled widget with reserved chairs added"/> <severity value="MAJOR"/> + <testCaseId value="MC-37419"/> <group value="Cms"/> <group value="WYSIWYGDisabled"/> </annotations> From 61d15651ef53bf51ae1ec14fd4f68125280c65ee Mon Sep 17 00:00:00 2001 From: mastiuhin-olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Tue, 8 Sep 2020 11:00:49 +0300 Subject: [PATCH 162/195] MC-35416: It is not possible to Manage Shopping Cart from Customer edit page after adding product to Wish List --- .../Wishlist/Model/ResourceModel/Item/Collection.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php index 73b7b6c827ec1..04e320ce4bc50 100644 --- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php +++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php @@ -398,7 +398,11 @@ protected function _renderFiltersBefore() $availableProductTypes = $this->salesConfig->getAvailableProductTypes(); $this->getSelect()->join( ['cat_prod' => $this->getTable('catalog_product_entity')], - $this->getConnection()->quoteInto('cat_prod.type_id IN (?) AND main_table.product_id = cat_prod.entity_id', $availableProductTypes), + $this->getConnection() + ->quoteInto( + 'cat_prod.type_id IN (?) AND main_table.product_id = cat_prod.entity_id', + $availableProductTypes + ), [] ); } From ce46ca6b9b46a573522686c74e1059cc7ae9ba1c Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Tue, 8 Sep 2020 11:35:36 +0300 Subject: [PATCH 163/195] Update app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml Co-authored-by: Lukasz Bajsarowicz <lukasz.bajsarowicz@gmail.com> --- .../Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml index 0a2eaacfc613c..4da29104797ac 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCmsBlockActionGroup.xml instead."> + <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use AdminOpenCmsBlockActionGroup instead."> <arguments> <argument name="block_id" type="string"/> </arguments> From 2577a871dc2da79d5a0b77424cd12718e83ff09f Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Tue, 8 Sep 2020 12:25:22 +0300 Subject: [PATCH 164/195] MC-37438: Customer Configurations: Create new account options --- .../Customer/Block/Address/EditTest.php | 161 ++++++---- .../Controller/Account/CreatePostTest.php | 303 ++++++++++++++++++ .../Customer/Controller/AccountTest.php | 212 ------------ .../AccountManagement/CreateAccountTest.php | 166 ++++++++++ .../Model/Address/CreateAddressTest.php | 143 +++++++++ .../customer_confirmation_email_template.php | 30 ++ ...r_confirmation_email_template_rollback.php | 25 ++ .../customer_confirmed_email_template.php | 30 ++ ...omer_confirmed_email_template_rollback.php | 25 ++ .../customer_welcome_email_template.php | 30 ++ ...stomer_welcome_email_template_rollback.php | 23 ++ ...mer_welcome_no_password_email_template.php | 30 ++ ...me_no_password_email_template_rollback.php | 25 ++ 13 files changed, 935 insertions(+), 268 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php create mode 100644 dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php index 9c382068ceebc..12585992d084c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Address/EditTest.php @@ -3,126 +3,175 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Block\Address; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Session; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\Result\Page; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + /** * Tests Address Edit Block + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled */ -class EditTest extends \PHPUnit\Framework\TestCase +class EditTest extends TestCase { + /** @var ObjectManagerInterface */ + private $objectManager; + /** @var Edit */ - protected $_block; + private $block; - /** @var \Magento\Customer\Model\Session */ - protected $_customerSession; + /** @var Session */ + private $customerSession; - /** @var \Magento\Backend\Block\Template\Context */ - protected $_context; + /** @var AddressRegistry */ + private $addressRegistry; - /** @var string */ - protected $_requestId; + /** @var CustomerRegistry */ + private $customerRegistry; + /** @var RequestInterface */ + private $request; + + /** + * @inheritdoc + */ protected function setUp(): void { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $this->_customerSession = $objectManager->get(\Magento\Customer\Model\Session::class); - $this->_customerSession->setCustomerId(1); - - $this->_context = $objectManager->get(\Magento\Backend\Block\Template\Context::class); - $this->_requestId = $this->_context->getRequest()->getParam('id'); - $this->_context->getRequest()->setParam('id', '1'); - - $objectManager->get(\Magento\Framework\App\State::class)->setAreaCode('frontend'); - - /** @var $layout \Magento\Framework\View\Layout */ - $layout = $objectManager->get(\Magento\Framework\View\LayoutInterface::class); - $currentCustomer = $objectManager->create( - \Magento\Customer\Helper\Session\CurrentCustomer::class, - ['customerSession' => $this->_customerSession] - ); - $this->_block = $layout->createBlock( - \Magento\Customer\Block\Address\Edit::class, - '', - ['customerSession' => $this->_customerSession, 'currentCustomer' => $currentCustomer] - ); + parent::setUp(); + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerSession = $this->objectManager->get(Session::class); + $this->customerSession->setCustomerId(1); + $this->request = $this->objectManager->get(RequestInterface::class); + $this->request->setParam('id', '1'); + /** @var Page $page */ + $page = $this->objectManager->get(PageFactory::class)->create(); + $page->addHandle(['default', 'customer_address_form']); + $page->getLayout()->generateXml(); + $this->block = $page->getLayout()->getBlock('customer_address_edit'); + $this->addressRegistry = $this->objectManager->get(AddressRegistry::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); } + /** + * @inheritdoc + */ protected function tearDown(): void { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->_customerSession->setCustomerId(null); - $this->_context->getRequest()->setParam('id', $this->_requestId); - /** @var \Magento\Customer\Model\AddressRegistry $addressRegistry */ - $addressRegistry = $objectManager->get(\Magento\Customer\Model\AddressRegistry::class); + parent::tearDown(); + $this->customerSession->setCustomerId(null); + $this->request->setParam('id', null); //Cleanup address from registry - $addressRegistry->remove(1); - $addressRegistry->remove(2); - - /** @var \Magento\Customer\Model\CustomerRegistry $customerRegistry */ - $customerRegistry = $objectManager->get(\Magento\Customer\Model\CustomerRegistry::class); + $this->addressRegistry->remove(1); + $this->addressRegistry->remove(2); //Cleanup customer from registry - $customerRegistry->remove(1); + $this->customerRegistry->remove(1); } /** * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void */ - public function testGetSaveUrl() + public function testGetSaveUrl(): void { - $this->assertEquals('http://localhost/index.php/customer/address/formPost/', $this->_block->getSaveUrl()); + $this->assertEquals('http://localhost/index.php/customer/address/formPost/', $this->block->getSaveUrl()); } /** * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @return void */ - public function testGetRegionId() + public function testGetRegionId(): void { - $this->assertEquals(1, $this->_block->getRegionId()); + $this->assertEquals(1, $this->block->getRegionId()); } /** * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @return void */ - public function testGetCountryId() + public function testGetCountryId(): void { - $this->assertEquals('US', $this->_block->getCountryId()); + $this->assertEquals('US', $this->block->getCountryId()); } /** * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @return void */ - public function testGetCustomerAddressCount() + public function testGetCustomerAddressCount(): void { - $this->assertEquals(2, $this->_block->getCustomerAddressCount()); + $this->assertEquals(2, $this->block->getCustomerAddressCount()); } /** * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void */ - public function testCanSetAsDefaultShipping() + public function testCanSetAsDefaultShipping(): void { - $this->assertEquals(0, $this->_block->canSetAsDefaultShipping()); + $this->assertEquals(0, $this->block->canSetAsDefaultShipping()); } /** * @magentoDataFixture Magento/Customer/_files/customer.php + * @return void */ - public function testIsDefaultBilling() + public function testIsDefaultBilling(): void { - $this->assertFalse($this->_block->isDefaultBilling()); + $this->assertFalse($this->block->isDefaultBilling()); } /** * @magentoDataFixture Magento/Customer/_files/customer.php * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @return void + */ + public function testGetStreetLine(): void + { + $this->assertEquals('Green str, 67', $this->block->getStreetLine(1)); + $this->assertEquals('', $this->block->getStreetLine(2)); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoConfigFixture current_store customer/create_account/vat_frontend_visibility 1 + * @return void + */ + public function testVatIdFieldVisible(): void + { + $html = $this->block->toHtml(); + $labelXpath = "//div[contains(@class, 'taxvat')]//label/span[normalize-space(text()) = '%s']"; + $this->assertEquals(1, Xpath::getElementsCountForXpath(sprintf($labelXpath, __('VAT Number')), $html)); + $inputXpath = "//div[contains(@class, 'taxvat')]//div/input[contains(@id,'vat_id') and @type='text']"; + $this->assertEquals(1, Xpath::getElementsCountForXpath($inputXpath, $html)); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoConfigFixture current_store customer/create_account/vat_frontend_visibility 0 + * @return void */ - public function testGetStreetLine() + public function testVatIdFieldNotVisible(): void { - $this->assertEquals('Green str, 67', $this->_block->getStreetLine(1)); - $this->assertEquals('', $this->_block->getStreetLine(2)); + $html = $this->block->toHtml(); + $labelXpath = "//div[contains(@class, 'taxvat')]//label/span[normalize-space(text()) = '%s']"; + $this->assertEquals(0, Xpath::getElementsCountForXpath(sprintf($labelXpath, __('VAT Number')), $html)); + $inputXpath = "//div[contains(@class, 'taxvat')]//div/input[contains(@id,'vat_id') and @type='text']"; + $this->assertEquals(0, Xpath::getElementsCountForXpath($inputXpath, $html)); } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php new file mode 100644 index 0000000000000..8ce1d2ae9ccf9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/Account/CreatePostTest.php @@ -0,0 +1,303 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller\Account; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\App\Http; +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Stdlib\CookieManagerInterface; +use Magento\Framework\UrlInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\Request; +use Magento\TestFramework\TestCase\AbstractController; +use Magento\Theme\Controller\Result\MessagePlugin; + +/** + * Tests from customer account create post action. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class CreatePostTest extends AbstractController +{ + /** + * @var TransportBuilderMock + */ + private $transportBuilderMock; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var CookieManagerInterface + */ + private $cookieManager; + + /** + * @var UrlInterface + */ + private $urlBuilder; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + parent::setUp(); + + $this->transportBuilderMock = $this->_objectManager->get(TransportBuilderMock::class); + $this->storeManager = $this->_objectManager->get(StoreManagerInterface::class); + $this->customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); + $this->customerRegistry = $this->_objectManager->get(CustomerRegistry::class); + $this->cookieManager = $this->_objectManager->get(CookieManagerInterface::class); + $this->urlBuilder = $this->_objectManager->get(UrlInterface::class); + } + + /** + * Tests that without form key user account won't be created + * and user will be redirected on account creation page again. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @return void + */ + public function testNoFormKeyCreatePostAction(): void + { + $this->fillRequestWithAccountData('test1@email.com'); + $this->getRequest()->setPostValue('form_key', null); + $this->dispatch('customer/account/createPost'); + + $this->assertCustomerNotExists('test1@email.com'); + $this->assertRedirect($this->stringEndsWith('customer/account/create/')); + $this->assertSessionMessages( + $this->stringContains((string)__('Invalid Form Key. Please refresh the page.')), + MessageInterface::TYPE_ERROR + ); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture current_website customer/create_account/confirm 0 + * @magentoConfigFixture current_store customer/create_account/default_group 1 + * @magentoConfigFixture current_store customer/create_account/generate_human_friendly_id 0 + * + * @return void + */ + public function testNoConfirmCreatePostAction(): void + { + $this->fillRequestWithAccountData('test1@email.com'); + $this->dispatch('customer/account/createPost'); + $this->assertRedirect($this->stringEndsWith('customer/account/')); + $this->assertSessionMessages( + $this->containsEqual( + (string)__('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName()) + ), + MessageInterface::TYPE_SUCCESS + ); + $customer = $this->customerRegistry->retrieveByEmail('test1@email.com'); + //Assert customer group + $this->assertEquals(1, $customer->getDataModel()->getGroupId()); + //Assert customer increment id generation + $this->assertNull($customer->getData('increment_id')); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture current_website customer/create_account/confirm 0 + * @magentoConfigFixture current_store customer/create_account/default_group 2 + * @magentoConfigFixture current_store customer/create_account/generate_human_friendly_id 1 + * @return void + */ + public function testCreatePostWithCustomConfiguration(): void + { + $this->fillRequestWithAccountData('test@email.com'); + $this->dispatch('customer/account/createPost'); + $this->assertRedirect($this->stringEndsWith('customer/account/')); + $this->assertSessionMessages( + $this->containsEqual( + (string)__('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName()) + ), + MessageInterface::TYPE_SUCCESS + ); + $customer = $this->customerRegistry->retrieveByEmail('test@email.com'); + //Assert customer group + $this->assertEquals(2, $customer->getDataModel()->getGroupId()); + //Assert customer increment id generation + $this->assertNotNull($customer->getData('increment_id')); + $this->assertMatchesRegularExpression('/\d{8}/', $customer->getData('increment_id')); + } + + /** + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture current_website customer/create_account/confirm 1 + * + * @return void + */ + public function testWithConfirmCreatePostAction(): void + { + $email = 'test2@email.com'; + $this->fillRequestWithAccountData($email); + $this->dispatch('customer/account/createPost'); + $this->assertRedirect($this->stringContains('customer/account/index/')); + $message = 'You must confirm your account.' + . ' Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.'; + $url = $this->urlBuilder->getUrl('customer/account/confirmation', ['_query' => ['email' => $email]]); + $this->assertSessionMessages( + $this->containsEqual((string)__($message, $url)), + MessageInterface::TYPE_SUCCESS + ); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testExistingEmailCreatePostAction(): void + { + $this->fillRequestWithAccountData('customer@example.com'); + $this->dispatch('customer/account/createPost'); + $this->assertRedirect($this->stringContains('customer/account/create/')); + $message = 'There is already an account with this email address.' + . ' If you are sure that it is your email address, <a href="%1">click here</a> ' + . 'to get your password and access your account.'; + $url = $this->urlBuilder->getUrl('customer/account/forgotpassword'); + $this->assertSessionMessages($this->containsEqual((string)__($message, $url)), MessageInterface::TYPE_ERROR); + } + + /** + * Register Customer with email confirmation. + * + * @magentoAppArea frontend + * @magentoConfigFixture current_website customer/create_account/confirm 1 + * + * @return void + */ + public function testRegisterCustomerWithEmailConfirmation(): void + { + $email = 'test_example@email.com'; + $this->fillRequestWithAccountData($email); + $this->dispatch('customer/account/createPost'); + $this->assertRedirect($this->stringContains('customer/account/index/')); + $message = 'You must confirm your account.' + . ' Please check your email for the confirmation link or <a href="%1">click here</a> for a new link.'; + $url = $this->urlBuilder->getUrl('customer/account/confirmation', ['_query' => ['email' => $email]]); + $this->assertSessionMessages($this->containsEqual((string)__($message, $url)), MessageInterface::TYPE_SUCCESS); + /** @var CustomerInterface $customer */ + $customer = $this->customerRepository->get($email); + $confirmation = $customer->getConfirmation(); + $sendMessage = $this->transportBuilderMock->getSentMessage(); + $this->assertNotNull($sendMessage); + $rawMessage = $sendMessage->getBody()->getParts()[0]->getRawContent(); + $this->assertStringContainsString( + (string)__( + 'You must confirm your %customer_email email before you can sign in (link is only valid once):', + ['customer_email' => $email] + ), + $rawMessage + ); + $this->assertStringContainsString( + sprintf('customer/account/confirm/?id=%s&key=%s', $customer->getId(), $confirmation), + $rawMessage + ); + $this->resetRequest(); + $this->getRequest() + ->setParam('id', $customer->getId()) + ->setParam('key', $confirmation); + $this->dispatch('customer/account/confirm'); + $this->assertRedirect($this->stringContains('customer/account/index/')); + $this->assertSessionMessages( + $this->containsEqual( + (string)__('Thank you for registering with %1.', $this->storeManager->getStore()->getFrontendName()) + ), + MessageInterface::TYPE_SUCCESS + ); + $this->assertEmpty($this->customerRepository->get($email)->getConfirmation()); + } + + /** + * Fills request with customer data. + * + * @param string $email + * @return void + */ + private function fillRequestWithAccountData(string $email): void + { + $this->getRequest() + ->setMethod(HttpRequest::METHOD_POST) + ->setParam(CustomerInterface::FIRSTNAME, 'firstname1') + ->setParam(CustomerInterface::LASTNAME, 'lastname1') + ->setParam(CustomerInterface::EMAIL, $email) + ->setParam('password', '_Password1') + ->setParam('password_confirmation', '_Password1') + ->setParam('telephone', '5123334444') + ->setParam('street', ['1234 fake street', '']) + ->setParam('city', 'Austin') + ->setParam('postcode', '78701') + ->setParam('country_id', 'US') + ->setParam('default_billing', '1') + ->setParam('default_shipping', '1') + ->setParam('is_subscribed', '0') + ->setPostValue('create_address', true); + } + + /** + * Asserts that customer does not exists. + * + * @param string $email + * @return void + */ + private function assertCustomerNotExists(string $email): void + { + $this->expectException(NoSuchEntityException::class); + $this->expectExceptionMessage( + (string)__( + 'No such entity with %fieldName = %fieldValue, %field2Name = %field2Value', + [ + 'fieldName' => 'email', + 'fieldValue' => $email, + 'field2Name' => 'websiteId', + 'field2Value' => 1 + ] + ) + ); + $this->assertNull($this->customerRepository->get($email)); + } + + /** + * Clears request. + * + * @return void + */ + protected function resetRequest(): void + { + parent::resetRequest(); + $this->cookieManager->deleteCookie(MessagePlugin::MESSAGES_COOKIES_NAME); + $this->_objectManager->removeSharedInstance(Http::class); + $this->_objectManager->removeSharedInstance(Request::class); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 5527a39ce0507..6abbff18c645c 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -10,13 +10,10 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Model\CustomerRegistry; use Magento\Customer\Model\Session; -use Magento\Framework\Api\FilterBuilder; -use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\Http; use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\MessageInterface; -use Magento\Framework\Phrase; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Store\Model\StoreManager; @@ -220,83 +217,6 @@ public function testConfirmActionAlreadyActive() $this->getResponse()->getBody(); } - /** - * Tests that without form key user account won't be created - * and user will be redirected on account creation page again. - */ - public function testNoFormKeyCreatePostAction() - { - $this->fillRequestWithAccountData('test1@email.com'); - $this->getRequest()->setPostValue('form_key', null); - $this->dispatch('customer/account/createPost'); - - $this->assertNull($this->getCustomerByEmail('test1@email.com')); - $this->assertRedirect($this->stringEndsWith('customer/account/create/')); - $this->assertSessionMessages( - $this->equalTo([new Phrase('Invalid Form Key. Please refresh the page.')]), - MessageInterface::TYPE_ERROR - ); - } - - /** - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_disable.php - */ - public function testNoConfirmCreatePostAction() - { - $this->fillRequestWithAccountDataAndFormKey('test1@email.com'); - $this->dispatch('customer/account/createPost'); - $this->assertRedirect($this->stringEndsWith('customer/account/')); - $this->assertSessionMessages( - $this->equalTo(['Thank you for registering with Main Website Store.']), - MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDbIsolation enabled - * @magentoAppIsolation enabled - * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php - */ - public function testWithConfirmCreatePostAction() - { - $this->fillRequestWithAccountDataAndFormKey('test2@email.com'); - $this->dispatch('customer/account/createPost'); - $this->assertRedirect($this->stringContains('customer/account/index/')); - $this->assertSessionMessages( - $this->equalTo( - [ - 'You must confirm your account. Please check your email for the confirmation link or ' - . '<a href="http://localhost/index.php/customer/account/confirmation/' - . '?email=test2%40email.com">click here</a> for a new link.' - ] - ), - MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoDataFixture Magento/Customer/_files/customer.php - */ - public function testExistingEmailCreatePostAction() - { - $this->fillRequestWithAccountDataAndFormKey('customer@example.com'); - $this->dispatch('customer/account/createPost'); - $this->assertRedirect($this->stringContains('customer/account/create/')); - $this->assertSessionMessages( - $this->equalTo( - [ - 'There is already an account with this email address. ' . - 'If you are sure that it is your email address, ' . - '<a href="http://localhost/index.php/customer/account/forgotpassword/">click here</a>' . - ' to get your password and access your account.', - ] - ), - MessageInterface::TYPE_ERROR - ); - } - /** * @magentoDataFixture Magento/Customer/_files/inactive_customer.php */ @@ -613,70 +533,6 @@ public function testWrongConfirmationEditPostAction() ); } - /** - * Register Customer with email confirmation. - * - * @magentoDataFixture Magento/Customer/_files/customer_confirmation_config_enable.php - * @return void - * @throws \Magento\Framework\Exception\InputException - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - * @throws \Magento\Framework\Stdlib\Cookie\FailureToSendException - */ - public function testRegisterCustomerWithEmailConfirmation(): void - { - $email = 'test_example@email.com'; - $this->fillRequestWithAccountDataAndFormKey($email); - $this->dispatch('customer/account/createPost'); - $this->assertRedirect($this->stringContains('customer/account/index/')); - $this->assertSessionMessages( - $this->equalTo( - [ - 'You must confirm your account. Please check your email for the confirmation link or ' - . '<a href="http://localhost/index.php/customer/account/confirmation/' - . '?email=test_example%40email.com">click here</a> for a new link.' - ] - ), - MessageInterface::TYPE_SUCCESS - ); - /** @var CustomerRepositoryInterface $customerRepository */ - $customerRepository = $this->_objectManager->create(CustomerRepositoryInterface::class); - /** @var CustomerInterface $customer */ - $customer = $customerRepository->get($email); - $confirmation = $customer->getConfirmation(); - $message = $this->transportBuilderMock->getSentMessage(); - $rawMessage = $message->getBody()->getParts()[0]->getRawContent(); - $messageConstraint = $this->logicalAnd( - new StringContains("You must confirm your {$email} email before you can sign in (link is only valid once"), - new StringContains("customer/account/confirm/?id={$customer->getId()}&key={$confirmation}") - ); - $this->assertThat($rawMessage, $messageConstraint); - - /** @var CookieManagerInterface $cookieManager */ - $cookieManager = $this->_objectManager->get(CookieManagerInterface::class); - $cookieManager->deleteCookie(MessagePlugin::MESSAGES_COOKIES_NAME); - - $this->_objectManager->removeSharedInstance(Http::class); - $this->_objectManager->removeSharedInstance(Request::class); - $this->_request = null; - - $this->getRequest() - ->setParam('id', $customer->getId()) - ->setParam('key', $confirmation); - $this->dispatch('customer/account/confirm'); - - /** @var StoreManager $store */ - $store = $this->_objectManager->get(StoreManagerInterface::class); - $name = $store->getStore()->getFrontendName(); - - $this->assertRedirect($this->stringContains('customer/account/index/')); - $this->assertSessionMessages( - $this->equalTo(["Thank you for registering with {$name}."]), - MessageInterface::TYPE_SUCCESS - ); - $this->assertEmpty($customerRepository->get($email)->getConfirmation()); - } - /** * Test that confirmation email address displays special characters correctly. * @@ -867,74 +723,6 @@ protected function resetRequest(): void parent::resetRequest(); } - /** - * @param string $email - * @return void - */ - private function fillRequestWithAccountData($email) - { - $this->getRequest() - ->setMethod('POST') - ->setParam('firstname', 'firstname1') - ->setParam('lastname', 'lastname1') - ->setParam('company', '') - ->setParam('email', $email) - ->setParam('password', '_Password1') - ->setParam('password_confirmation', '_Password1') - ->setParam('telephone', '5123334444') - ->setParam('street', ['1234 fake street', '']) - ->setParam('city', 'Austin') - ->setParam('region_id', 57) - ->setParam('region', '') - ->setParam('postcode', '78701') - ->setParam('country_id', 'US') - ->setParam('default_billing', '1') - ->setParam('default_shipping', '1') - ->setParam('is_subscribed', '0') - ->setPostValue('create_address', true); - } - - /** - * @param string $email - * @return void - */ - private function fillRequestWithAccountDataAndFormKey($email) - { - $this->fillRequestWithAccountData($email); - $formKey = $this->_objectManager->get(FormKey::class); - $this->getRequest()->setParam('form_key', $formKey->getFormKey()); - } - - /** - * Returns stored customer by email. - * - * @param string $email - * @return CustomerInterface - */ - private function getCustomerByEmail($email) - { - /** @var FilterBuilder $filterBuilder */ - $filterBuilder = $this->_objectManager->get(FilterBuilder::class); - $filters = [ - $filterBuilder->setField(CustomerInterface::EMAIL) - ->setValue($email) - ->create() - ]; - - /** @var SearchCriteriaBuilder $searchCriteriaBuilder */ - $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class); - $searchCriteria = $searchCriteriaBuilder->addFilters($filters) - ->create(); - - $customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class); - $customers = $customerRepository->getList($searchCriteria) - ->getItems(); - - $customer = array_pop($customers); - - return $customer; - } - /** * Add new request info (request uri, path info, action name). * diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php index e12068ef62b21..bd2c26e449d72 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php @@ -13,9 +13,12 @@ use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Customer\Model\Customer; use Magento\Customer\Model\CustomerFactory; +use Magento\Customer\Model\EmailNotification; +use Magento\Email\Model\ResourceModel\Template\CollectionFactory as TemplateCollectionFactory; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Api\SimpleDataObjectConverter; +use Magento\Framework\App\Config\MutableScopeConfigInterface; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\LocalizedException; @@ -23,6 +26,7 @@ use Magento\Framework\Math\Random; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Validator\Exception; +use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Helper\Xpath; @@ -101,6 +105,16 @@ class CreateAccountTest extends TestCase */ private $encryptor; + /** + * @var MutableScopeConfigInterface + */ + private $mutableScopeConfig; + + /** + * @var TemplateCollectionFactory + */ + private $templateCollectionFactory; + /** * @inheritdoc */ @@ -117,9 +131,20 @@ protected function setUp(): void $this->customerModelFactory = $this->objectManager->get(CustomerFactory::class); $this->random = $this->objectManager->get(Random::class); $this->encryptor = $this->objectManager->get(EncryptorInterface::class); + $this->mutableScopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class); + $this->templateCollectionFactory = $this->objectManager->get(TemplateCollectionFactory::class); parent::setUp(); } + /** + * @inheritdoc + */ + protected function tearDown(): void + { + parent::tearDown(); + $this->mutableScopeConfig->clean(); + } + /** * @dataProvider createInvalidAccountDataProvider * @param array $customerData @@ -220,6 +245,98 @@ public function createInvalidAccountDataProvider(): array ]; } + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer_welcome_email_template.php + * @return void + */ + public function testCreateAccountWithConfiguredWelcomeEmail(): void + { + $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_template'); + $this->setConfig([EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE => $emailTemplate,]); + $this->accountManagement->createAccount( + $this->populateCustomerEntity($this->defaultCustomerData), + '_Password1' + ); + $this->assertEmailData( + [ + 'name' => 'Owner', + 'email' => 'owner@example.com', + 'message' => 'Customer create account email template', + ] + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer_welcome_no_password_email_template.php + * @magentoConfigFixture current_store customer/create_account/email_identity support + * @return void + */ + public function testCreateAccountWithConfiguredWelcomeNoPasswordEmail(): void + { + $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_no_password_template'); + $this->setConfig([EmailNotification::XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE => $emailTemplate,]); + $this->accountManagement->createAccount($this->populateCustomerEntity($this->defaultCustomerData)); + $this->assertEmailData( + [ + 'name' => 'CustomerSupport', + 'email' => 'support@example.com', + 'message' => 'Customer create account email no password template', + ] + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer_confirmation_email_template.php + * @magentoConfigFixture current_website customer/create_account/confirm 1 + * @magentoConfigFixture current_store customer/create_account/email_identity custom1 + * @return void + */ + public function testCreateAccountWithConfiguredConfirmationEmail(): void + { + $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_confirmation_template'); + $this->setConfig([EmailNotification::XML_PATH_CONFIRM_EMAIL_TEMPLATE => $emailTemplate,]); + $this->accountManagement->createAccount( + $this->populateCustomerEntity($this->defaultCustomerData), + '_Password1' + ); + $this->assertEmailData( + [ + 'name' => 'Custom 1', + 'email' => 'custom1@example.com', + 'message' => 'Customer create account email confirmation template', + ] + ); + } + + /** + * @magentoAppArea frontend + * @magentoDataFixture Magento/Customer/_files/customer_confirmed_email_template.php + * @magentoConfigFixture current_store customer/create_account/email_identity custom1 + * @magentoConfigFixture current_website customer/create_account/confirm 1 + * @return void + */ + public function testCreateAccountWithConfiguredConfirmedEmail(): void + { + $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_confirmed_template'); + $this->setConfig([EmailNotification::XML_PATH_CONFIRMED_EMAIL_TEMPLATE => $emailTemplate,]); + $this->accountManagement->createAccount( + $this->populateCustomerEntity($this->defaultCustomerData), + '_Password1' + ); + $customer = $this->customerRepository->get('customer@example.com'); + $this->accountManagement->activate($customer->getEmail(), $customer->getConfirmation()); + $this->assertEmailData( + [ + 'name' => 'Custom 1', + 'email' => 'custom1@example.com', + 'message' => 'Customer create account email confirmed template', + ] + ); + } + /** * Assert that when you create customer account via admin, link with "set password" is send to customer email. * @@ -589,4 +706,53 @@ private function assertCustomerData( ); } } + + /** + * Sets config data. + * + * @param array $configs + * @return void + */ + private function setConfig(array $configs): void + { + foreach ($configs as $path => $value) { + $this->mutableScopeConfig->setValue($path, $value, ScopeInterface::SCOPE_STORE, 'default'); + } + } + + /** + * Assert email data. + * + * @param array $expectedData + * @return void + */ + private function assertEmailData(array $expectedData): void + { + $message = $this->transportBuilderMock->getSentMessage(); + $this->assertNotNull($message); + $messageFrom = $message->getFrom(); + $this->assertNotNull($messageFrom); + $messageFrom = reset($messageFrom); + $this->assertEquals($expectedData['name'], $messageFrom->getName()); + $this->assertEquals($expectedData['email'], $messageFrom->getEmail()); + $this->assertStringContainsString( + $expectedData['message'], + $message->getBody()->getParts()[0]->getRawContent(), + 'Expected message wasn\'t found in email content.' + ); + } + + /** + * Returns email template id by template code. + * + * @param string $templateCode + * @return int + */ + private function getCustomTemplateId(string $templateCode): int + { + return (int)$this->templateCollectionFactory->create() + ->addFieldToFilter('template_code', $templateCode) + ->getFirstItem() + ->getId(); + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php index ac55f93bc9e4b..eb638eeb329aa 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php @@ -14,11 +14,16 @@ use Magento\Customer\Model\AddressRegistry; use Magento\Customer\Model\CustomerRegistry; use Magento\Customer\Model\ResourceModel\Address; +use Magento\Customer\Model\Vat; +use Magento\Customer\Observer\AfterAddressSaveObserver; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DataObjectFactory; use Magento\Framework\Exception\InputException; use Magento\TestFramework\Directory\Model\GetRegionIdByName; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface as PsrLogger; /** * Assert that address was created as expected or address create throws expected error. @@ -88,6 +93,11 @@ class CreateAddressTest extends TestCase */ private $createdAddressesIds = []; + /** + * @var DataObjectFactory + */ + private $dataObjectFactory; + /** * @inheritdoc */ @@ -101,6 +111,7 @@ protected function setUp(): void $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); $this->addressRegistry = $this->objectManager->get(AddressRegistry::class); $this->addressResource = $this->objectManager->get(Address::class); + $this->dataObjectFactory = $this->objectManager->get(DataObjectFactory::class); parent::setUp(); } @@ -112,6 +123,7 @@ protected function tearDown(): void foreach ($this->createdAddressesIds as $createdAddressesId) { $this->addressRegistry->remove($createdAddressesId); } + $this->objectManager->removeSharedInstance(AfterAddressSaveObserver::class); parent::tearDown(); } @@ -326,6 +338,92 @@ public function createWrongAddressesDataProvider(): array ]; } + /** + * Assert that after address creation customer group is Group for Valid VAT ID - Domestic. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * @magentoConfigFixture current_store general/store_information/country_id AT + * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1 + * @magentoConfigFixture current_store customer/create_account/viv_domestic_group 2 + * @return void + */ + public function testAddressCreatedWithGroupAssignByDomesticVatId(): void + { + $this->createVatMock(true, true); + $addressData = array_merge( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT'] + ); + $customer = $this->customerRepository->get('customer5@example.com'); + $this->createAddress((int)$customer->getId(), $addressData, false, true); + $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com')); + } + + /** + * Assert that after address creation customer group is Group for Valid VAT ID - Intra-Union. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * @magentoConfigFixture current_store general/store_information/country_id GR + * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1 + * @magentoConfigFixture current_store customer/create_account/viv_intra_union_group 2 + * @return void + */ + public function testAddressCreatedWithGroupAssignByIntraUnionVatId(): void + { + $this->createVatMock(true, true); + $addressData = array_merge( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT'] + ); + $customer = $this->customerRepository->get('customer5@example.com'); + $this->createAddress((int)$customer->getId(), $addressData, false, true); + $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com')); + } + + /** + * Assert that after address creation customer group is Group for Invalid VAT ID. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1 + * @magentoConfigFixture current_store customer/create_account/viv_invalid_group 2 + * @return void + */ + public function testAddressCreatedWithGroupAssignByInvalidVatId(): void + { + $this->createVatMock(false, true); + $addressData = array_merge( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT'] + ); + $customer = $this->customerRepository->get('customer5@example.com'); + $this->createAddress((int)$customer->getId(), $addressData, false, true); + $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com')); + } + + /** + * Assert that after address creation customer group is Validation Error Group. + * + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * @magentoConfigFixture current_store customer/create_account/auto_group_assign 1 + * @magentoConfigFixture current_store customer/create_account/viv_error_group 2 + * @return void + */ + public function testAddressCreatedWithGroupAssignByVatIdWithError(): void + { + $this->createVatMock(false, false); + $addressData = array_merge( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::VAT_ID => '111', AddressInterface::COUNTRY_ID => 'AT'] + ); + $customer = $this->customerRepository->get('customer5@example.com'); + $this->createAddress((int)$customer->getId(), $addressData, false, true); + $this->assertEquals(2, $this->getCustomerGroupId('customer5@example.com')); + } + /** * Create customer address with provided address data. * @@ -361,4 +459,49 @@ protected function createAddress( return $address; } + + /** + * Creates mock for vat id validation. + * + * @param bool $isValid + * @param bool $isRequestSuccess + * @return void + */ + private function createVatMock(bool $isValid = false, bool $isRequestSuccess = false): void + { + $gatewayResponse = $this->dataObjectFactory->create( + [ + 'data' => [ + 'is_valid' => $isValid, + 'request_date' => '', + 'request_identifier' => '123123123', + 'request_success' => $isRequestSuccess, + 'request_message' => __(''), + ], + ] + ); + $customerVat = $this->getMockBuilder(Vat::class) + ->setConstructorArgs( + [ + $this->objectManager->get(ScopeConfigInterface::class), + $this->objectManager->get(PsrLogger::class) + ] + ) + ->setMethods(['checkVatNumber']) + ->getMock(); + $customerVat->method('checkVatNumber')->willReturn($gatewayResponse); + $this->objectManager->removeSharedInstance(Vat::class); + $this->objectManager->addSharedInstance($customerVat, Vat::class); + } + + /** + * Returns customer group id by email. + * + * @param string $email + * @return int + */ + private function getCustomerGroupId(string $email): int + { + return (int)$this->customerRepository->get($email)->getGroupId(); + } } diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php new file mode 100644 index 0000000000000..38b607230cbaf --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Framework\Mail\TemplateInterface; +use Magento\Framework\Mail\TemplateInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var TemplateInterfaceFactory $templateFactory */ +$templateFactory = $objectManager->get(TemplateInterfaceFactory::class); +/** @var TemplateInterface $template */ +$template = $templateFactory->create(); + +$content = <<<HTML +{{template config_path="design/email/header_template"}} +<p>{{trans "Customer create account email confirmation template"}}</p> +{{template config_path="design/email/footer_template"}} +HTML; + +$template->setTemplateCode('customer_create_account_email_confirmation_template') + ->setTemplateText($content) + ->setTemplateType(TemplateInterface::TYPE_HTML); +$templateResource->save($template); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php new file mode 100644 index 0000000000000..07fee6e81fe47 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmation_email_template_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Email\Model\ResourceModel\Template\CollectionFactory; +use Magento\Email\Model\ResourceModel\Template\Collection; +use Magento\Framework\Mail\TemplateInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var Collection $collection */ +$collection = $objectManager->get(CollectionFactory::class)->create(); +/** @var TemplateInterface $template */ +$template = $collection + ->addFieldToFilter('template_code', 'customer_create_account_email_confirmation_template') + ->getFirstItem(); +if ($template->getId()) { + $templateResource->delete($template); +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php new file mode 100644 index 0000000000000..859cae92dbd27 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Framework\Mail\TemplateInterface; +use Magento\Framework\Mail\TemplateInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var TemplateInterfaceFactory $templateFactory */ +$templateFactory = $objectManager->get(TemplateInterfaceFactory::class); +/** @var TemplateInterface $template */ +$template = $templateFactory->create(); + +$content = <<<HTML +{{template config_path="design/email/header_template"}} +<p>{{trans "Customer create account email confirmed template"}}</p> +{{template config_path="design/email/footer_template"}} +HTML; + +$template->setTemplateCode('customer_create_account_email_confirmed_template') + ->setTemplateText($content) + ->setTemplateType(TemplateInterface::TYPE_HTML); +$templateResource->save($template); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php new file mode 100644 index 0000000000000..a4e03038d45bd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_confirmed_email_template_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Email\Model\ResourceModel\Template\CollectionFactory; +use Magento\Email\Model\ResourceModel\Template\Collection; +use Magento\Framework\Mail\TemplateInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var Collection $collection */ +$collection = $objectManager->get(CollectionFactory::class)->create(); +/** @var TemplateInterface $template */ +$template = $collection + ->addFieldToFilter('template_code', 'customer_create_account_email_confirmed_template') + ->getFirstItem(); +if ($template->getId()) { + $templateResource->delete($template); +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php new file mode 100644 index 0000000000000..6cc273dbe235a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Framework\Mail\TemplateInterface; +use Magento\Framework\Mail\TemplateInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var TemplateInterfaceFactory $templateFactory */ +$templateFactory = $objectManager->get(TemplateInterfaceFactory::class); +/** @var TemplateInterface $template */ +$template = $templateFactory->create(); + +$content = <<<HTML +{{template config_path="design/email/header_template"}} +<p>{{trans "Customer create account email template"}}</p> +{{template config_path="design/email/footer_template"}} +HTML; + +$template->setTemplateCode('customer_create_account_email_template') + ->setTemplateText($content) + ->setTemplateType(TemplateInterface::TYPE_HTML); +$templateResource->save($template); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php new file mode 100644 index 0000000000000..6bef9822d3e9a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_email_template_rollback.php @@ -0,0 +1,23 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Email\Model\ResourceModel\Template\CollectionFactory; +use Magento\Email\Model\ResourceModel\Template\Collection; +use Magento\Framework\Mail\TemplateInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var Collection $collection */ +$collection = $objectManager->get(CollectionFactory::class)->create(); +/** @var TemplateInterface $template */ +$template = $collection->addFieldToFilter('template_code', 'customer_create_account_email_template')->getFirstItem(); +if ($template->getId()) { + $templateResource->delete($template); +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php new file mode 100644 index 0000000000000..a936bb9a4eb02 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Framework\Mail\TemplateInterface; +use Magento\Framework\Mail\TemplateInterfaceFactory; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var TemplateInterfaceFactory $templateFactory */ +$templateFactory = $objectManager->get(TemplateInterfaceFactory::class); +/** @var TemplateInterface $template */ +$template = $templateFactory->create(); + +$content = <<<HTML +{{template config_path="design/email/header_template"}} +<p>{{trans "Customer create account email no password template"}}</p> +{{template config_path="design/email/footer_template"}} +HTML; + +$template->setTemplateCode('customer_create_account_email_no_password_template') + ->setTemplateText($content) + ->setTemplateType(TemplateInterface::TYPE_HTML); +$templateResource->save($template); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php new file mode 100644 index 0000000000000..4e14b4293cbb5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_welcome_no_password_email_template_rollback.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Email\Model\ResourceModel\Template as TemplateResource; +use Magento\Email\Model\ResourceModel\Template\CollectionFactory; +use Magento\Email\Model\ResourceModel\Template\Collection; +use Magento\Framework\Mail\TemplateInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var TemplateResource $templateResource */ +$templateResource = $objectManager->get(TemplateResource::class); +/** @var Collection $collection */ +$collection = $objectManager->get(CollectionFactory::class)->create(); +/** @var TemplateInterface $template */ +$template = $collection + ->addFieldToFilter('template_code', 'customer_create_account_email_no_password_template') + ->getFirstItem(); +if ($template->getId()) { + $templateResource->delete($template); +} From 9b165382495928d340c4127a264eb636070f2532 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Tue, 8 Sep 2020 12:27:59 +0300 Subject: [PATCH 165/195] minor fix --- .../Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml index 4da29104797ac..c43628f3e60b5 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use AdminOpenCmsBlockActionGroup instead."> + <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use other ActionGroup with the same name instead."> <arguments> <argument name="block_id" type="string"/> </arguments> From d2c7d4cfd8a5c8f385709036990beba266767191 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Tue, 8 Sep 2020 12:48:18 +0300 Subject: [PATCH 166/195] revert name --- .../Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml index c43628f3e60b5..4da29104797ac 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml @@ -7,7 +7,7 @@ --> <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use other ActionGroup with the same name instead."> + <actionGroup name="AdminOpenCmsBlockActionGroup" deprecated="Use AdminOpenCmsBlockActionGroup instead."> <arguments> <argument name="block_id" type="string"/> </arguments> From 047228139f8fd8f10decf7f442e356913dd4f90b Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Tue, 8 Sep 2020 13:43:39 +0300 Subject: [PATCH 167/195] fix static --- .../creditmemo/items/renderer/default.phtml | 24 ++++++++++------- .../invoice/items/renderer/default.phtml | 16 ++++++----- .../order/items/renderer/default.phtml | 27 ++++++++++--------- .../shipment/items/renderer/default.phtml | 19 +++++++------ 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml index c7a1b4b79b91a..029bcb8abcc25 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/creditmemo/items/renderer/default.phtml @@ -10,15 +10,15 @@ <tr id="order-item-row-<?= (int) $_item->getId() ?>"> <td class="col name" data-th="<?= $block->escapeHtmlAttr(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()) : ?> + <?php if ($_options = $block->getItemOptions()): ?> <dl class="item-options"> - <?php foreach ($_options as $_option) : ?> + <?php foreach ($_options as $_option): ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()) : ?> + <?php if (!$block->getPrintStatus()): ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> - <?php if (isset($_formatedOptionValue['full_view'])) : ?> + <?php if (isset($_formatedOptionValue['full_view'])): ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> @@ -27,7 +27,7 @@ </div> <?php endif; ?> </dd> - <?php else : ?> + <?php else: ?> <dd> <?= $block->escapeHtml($_option['print_value'] ?? $_option['value']) ?> </dd> @@ -37,10 +37,10 @@ <?php endif; ?> <?php /* downloadable */ ?> - <?php if ($links = $block->getLinks()) : ?> + <?php if ($links = $block->getLinks()): ?> <dl class="item options"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?></dt> - <?php foreach ($links->getPurchasedItems() as $link) : ?> + <?php foreach ($links->getPurchasedItems() as $link): ?> <dd><?= $block->escapeHtml($link->getLinkTitle()) ?></dd> <?php endforeach; ?> </dl> @@ -48,12 +48,14 @@ <?php /* EOF downloadable */ ?> <?php $addInfoBlock = $block->getProductAdditionalInformationBlock(); ?> - <?php if ($addInfoBlock) : ?> + <?php if ($addInfoBlock): ?> <?= $addInfoBlock->setItem($_item->getOrderItem())->toHtml() ?> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"> + <?= /* @noEscape */ $block->prepareSku($block->getSku()) ?> + </td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getItemPriceHtml() ?> </td> @@ -61,7 +63,9 @@ <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> <?= $block->getItemRowTotalHtml() ?> </td> - <td class="col discount" data-th="<?= $block->escapeHtml(__('Discount Amount')) ?>"><?= /* @noEscape */ $_order->formatPrice(-$_item->getDiscountAmount()) ?></td> + <td class="col discount" data-th="<?= $block->escapeHtml(__('Discount Amount')) ?>"> + <?= /* @noEscape */ $_order->formatPrice(-$_item->getDiscountAmount()) ?> + </td> <td class="col total" data-th="<?= $block->escapeHtml(__('Row Total')) ?>"> <?= $block->getItemRowTotalAfterDiscountHtml() ?> </td> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml index d05d6d01a5340..d9542d13aba6d 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/invoice/items/renderer/default.phtml @@ -10,15 +10,15 @@ <tr id="order-item-row-<?= (int) $_item->getId() ?>"> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()) : ?> + <?php if ($_options = $block->getItemOptions()): ?> <dl class="item-options"> - <?php foreach ($_options as $_option) : ?> + <?php foreach ($_options as $_option): ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()) : ?> + <?php if (!$block->getPrintStatus()): ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> - <?php if (isset($_formatedOptionValue['full_view'])) : ?> + <?php if (isset($_formatedOptionValue['full_view'])): ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> @@ -27,19 +27,21 @@ </div> <?php endif; ?> </dd> - <?php else : ?> + <?php else: ?> <dd><?= $block->escapeHtml($_option['print_value'] ?? $_option['value']) ?></dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> <?php $addInfoBlock = $block->getProductAdditionalInformationBlock(); ?> - <?php if ($addInfoBlock) :?> + <?php if ($addInfoBlock): ?> <?= $addInfoBlock->setItem($_item->getOrderItem())->toHtml() ?> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"> + <?= /* @noEscape */ $block->prepareSku($block->getSku()) ?> + </td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getItemPriceHtml() ?> </td> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml index f0a0f46265a3e..9cae232ca6541 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/items/renderer/default.phtml @@ -10,15 +10,15 @@ $_item = $block->getItem(); <tr id="order-item-row-<?= (int) $_item->getId() ?>"> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()) : ?> + <?php if ($_options = $block->getItemOptions()): ?> <dl class="item-options"> - <?php foreach ($_options as $_option) : ?> + <?php foreach ($_options as $_option): ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()) : ?> + <?php if (!$block->getPrintStatus()): ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> - <?php if (isset($_formatedOptionValue['full_view'])) : ?> + <?php if (isset($_formatedOptionValue['full_view'])): ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> @@ -27,43 +27,46 @@ $_item = $block->getItem(); </div> <?php endif; ?> </dd> - <?php else : ?> - <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> + <?php else: ?> + <?php $optionValue = isset($_option['print_value']) ? $_option['print_value'] : $_option['value'] ?> + <dd><?= $block->escapeHtml($optionValue) ?></dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> <?php $addtInfoBlock = $block->getProductAdditionalInformationBlock(); ?> - <?php if ($addtInfoBlock) : ?> + <?php if ($addtInfoBlock): ?> <?= $addtInfoBlock->setItem($_item)->toHtml() ?> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"> + <?= /* @noEscape */ $block->prepareSku($block->getSku()) ?> + </td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getItemPriceHtml() ?> </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"> <ul class="items-qty"> - <?php if ($block->getItem()->getQtyOrdered() > 0) : ?> + <?php if ($block->getItem()->getQtyOrdered() > 0): ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Ordered')) ?></span> <span class="content"><?= (float) $block->getItem()->getQtyOrdered() ?></span> </li> <?php endif; ?> - <?php if ($block->getItem()->getQtyShipped() > 0) : ?> + <?php if ($block->getItem()->getQtyShipped() > 0): ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Shipped')) ?></span> <span class="content"><?= (float) $block->getItem()->getQtyShipped() ?></span> </li> <?php endif; ?> - <?php if ($block->getItem()->getQtyCanceled() > 0) : ?> + <?php if ($block->getItem()->getQtyCanceled() > 0): ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Canceled')) ?></span> <span class="content"><?= (float) $block->getItem()->getQtyCanceled() ?></span> </li> <?php endif; ?> - <?php if ($block->getItem()->getQtyRefunded() > 0) : ?> + <?php if ($block->getItem()->getQtyRefunded() > 0): ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Refunded')) ?></span> <span class="content"><?= (float) $block->getItem()->getQtyRefunded() ?></span> diff --git a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml index 773100601d2b8..6c7567a8cd14b 100644 --- a/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/order/shipment/items/renderer/default.phtml @@ -9,15 +9,15 @@ <tr id="order-item-row-<?= (int) $_item->getId() ?>"> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()) : ?> + <?php if ($_options = $block->getItemOptions()): ?> <dl class="item options"> - <?php foreach ($_options as $_option) : ?> + <?php foreach ($_options as $_option): ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()) : ?> + <?php if (!$block->getPrintStatus()): ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?>> <?= $block->escapeHtml($_formatedOptionValue['value'], ['a']) ?> - <?php if (isset($_formatedOptionValue['full_view'])) : ?> + <?php if (isset($_formatedOptionValue['full_view'])): ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> @@ -26,18 +26,21 @@ </div> <?php endif; ?> </dd> - <?php else : ?> - <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> + <?php else: ?> + <?php $optionValue = isset($_option['print_value']) ? $_option['print_value'] : $_option['value'] ?> + <dd><?= $block->escapeHtml($optionValue) ?></dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> <?php $addInfoBlock = $block->getProductAdditionalInformationBlock(); ?> - <?php if ($addInfoBlock) : ?> + <?php if ($addInfoBlock): ?> <?= $addInfoBlock->setItem($_item->getOrderItem())->toHtml() ?> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"> + <?= /* @noEscape */ $block->prepareSku($block->getSku()) ?> + </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty Shipped')) ?>"><?= (int) $_item->getQty() ?></td> </tr> From e31ab0041482ae52f08da44548ca70b9e16cdb50 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Tue, 8 Sep 2020 18:48:07 +0800 Subject: [PATCH 168/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - modified test --- ...alogUiVerifyUsedInLinkCategoryGridTest.xml | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml index eb2f75dad0151..40a5cf8e591fc 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml @@ -21,25 +21,31 @@ <before> <createData entity="TestSubCategory" stepKey="category"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> - <actionGroup ref="CliCacheCleanActionGroup" stepKey="cleanFullPageCache"> - <argument name="tags" value="config full_page"/> - </actionGroup> </before> <after> - <actionGroup ref="AdminEnhancedMediaGalleryEnableMassActionModeActionGroup" stepKey="enableMassActionToDeleteImages"/> - <actionGroup ref="AdminEnhancedMediaGallerySelectImageForMassActionActionGroup" stepKey="selectSecondImageToDelete"> - <argument name="imageName" value="{{UpdatedImageDetails.title}}"/> + <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetAdminDataGridToDefaultView"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectFolderForDelete"> + <argument name="name" value="categoryImage"/> + </actionGroup> + <actionGroup ref="AdminMediaGalleryFolderDeleteActionGroup" stepKey="deleteFolder"/> + <actionGroup ref="AdminMediaGalleryAssertFolderDoesNotExistActionGroup" stepKey="assertFolderWasDeleted"> + <argument name="name" value="categoryImage"/> </actionGroup> - <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clickDeleteSelectedButton"/> - <actionGroup ref="AdminEnhancedMediaGalleryConfirmDeleteImagesActionGroup" stepKey="deleteImages"/> </after> - <actionGroup ref="AdminOpenCategoryPageActionGroup" stepKey="openCategoryPage"/> <actionGroup ref="AdminCategoriesOpenCategoryActionGroup" stepKey="openCategory"> <argument name="category" value="$$category$$"/> </actionGroup> <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGalleryFromImageUploader"/> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetCategoryImageGalleryGridToDefaultView"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> + <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openNewFolderForm"/> + <actionGroup ref="AdminMediaGalleryCreateNewFolderActionGroup" stepKey="createCategoryImageFolder"> + <argument name="name" value="categoryImage"/> + </actionGroup> + <actionGroup ref="AdminMediaGalleryAssertFolderNameActionGroup" stepKey="assertCategoryImageFolderCreated"> + <argument name="name" value="categoryImage"/> + </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryUploadImageActionGroup" stepKey="uploadImage"> <argument name="image" value="ImageUpload"/> </actionGroup> @@ -52,6 +58,10 @@ <actionGroup ref="AdminMediaGalleryClickAddSelectedActionGroup" stepKey="clickAddSelectedCategoryImage"/> <actionGroup ref="AdminSaveCategoryActionGroup" stepKey="saveCategory"/> <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGalleryFromImageUploaderToVerifyLink"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectCategoryImageFolder"> + <argument name="name" value="categoryImage"/> + </actionGroup> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetails"/> <actionGroup ref="AdminEnhancedMediaGalleryClickEntityUsedInActionGroup" stepKey="clickUsedInCategories"> <argument name="entityName" value="Categories"/> @@ -85,6 +95,9 @@ <deleteData createDataKey="category" stepKey="deleteCategory"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openMediaGallery"/> + <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="openCategoryImageFolder"> + <argument name="name" value="categoryImage"/> + </actionGroup> <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetailsToVerifyEmptyUsedIn"/> <actionGroup ref="AssertAdminEnhancedMediaGalleryUsedInSectionNotDisplayedActionGroup" stepKey="assertThereIsNoUsedInSection"/> <actionGroup ref="AdminEnhancedMediaGalleryCloseViewDetailsActionGroup" stepKey="closeDetails"/> From ff12e395b639b2f3155efcd730c8c20fc9389b50 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky <engcom-vendorworker-foxtrot@adobe.com> Date: Tue, 8 Sep 2020 14:16:18 +0300 Subject: [PATCH 169/195] magento/magento2#25147: Totals Information Management - setting quote shipping method when address method is null - static test fix. --- app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php index 407d158e6f8d4..2e51bd8d75b89 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/OrderRepositoryTest.php @@ -72,7 +72,7 @@ class OrderRepositoryTest extends TestCase private $paymentAdditionalInfoFactory; /** - * @var OrderExtensionFactory|\PHPUnit_Framework_MockObject_MockObject + * @var OrderExtensionFactory|\MockObject */ private $orderExtensionFactoryMock; From 321a1365f7f45d0d71424c6dd1f4c58d1c3d26eb Mon Sep 17 00:00:00 2001 From: Roman Zhupanyn <roma.dj.elf@gmail.com> Date: Tue, 8 Sep 2020 14:48:38 +0300 Subject: [PATCH 170/195] MC-33493: Test render fields in composite configure block for simple and configurable product --- .../AbstractRenderCustomOptionsTest.php | 2 +- ...nd_store_with_second_currency_rollback.php | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php index 44c9ada3cacab..b575fc5e7033c 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Block/Product/View/Options/AbstractRenderCustomOptionsTest.php @@ -373,7 +373,7 @@ protected function addOptionToProduct( */ protected function getOptionHtml(ProductInterface $product): string { - $this->page->addHandle($this->getHandlesList($product)); + $this->page->addHandle($this->getHandlesList()); $this->page->getLayout()->generateXml(); /** @var Options $optionsBlock */ $optionsBlock = $this->page->getLayout()->getBlock($this->getOptionsBlockName()); diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php index 3151a76327397..547ce78500f49 100644 --- a/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Store/_files/second_store_with_second_currency_rollback.php @@ -4,24 +4,35 @@ * See COPYING.txt for license details. */ declare(strict_types=1); + +use Magento\Config\Model\ResourceModel\Config; +use Magento\Directory\Model\Currency as ModelCurrency; +use Magento\Directory\Model\ResourceModel\Currency as ResourceCurrency; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Workaround\Override\Fixture\Resolver; -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$store = $objectManager->create(\Magento\Store\Model\Store::class); +$objectManager = Bootstrap::getObjectManager(); +$store = $objectManager->create(Store::class); $storeId = $store->load('fixture_second_store', 'code')->getId(); if ($storeId) { - $configResource = $objectManager->get(\Magento\Config\Model\ResourceModel\Config::class); + $configResource = $objectManager->get(Config::class); $configResource->deleteConfig( - \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_DEFAULT, - \Magento\Store\Model\ScopeInterface::SCOPE_STORES, + ModelCurrency::XML_PATH_CURRENCY_DEFAULT, + ScopeInterface::SCOPE_STORES, $storeId ); $configResource->deleteConfig( - \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_ALLOW, - \Magento\Store\Model\ScopeInterface::SCOPE_STORES, + ModelCurrency::XML_PATH_CURRENCY_ALLOW, + ScopeInterface::SCOPE_STORES, $storeId ); } Resolver::getInstance()->requireDataFixture('Magento/Store/_files/second_store_rollback.php'); +$reflectionClass = new \ReflectionClass(ResourceCurrency::class); +$staticProperty = $reflectionClass->getProperty('_rateCache'); +$staticProperty->setAccessible(true); +$staticProperty->setValue(null); From c0960da3827f6bd205d229ef21f780bda3efc1c0 Mon Sep 17 00:00:00 2001 From: mastiuhin-olexandr <mastiuhin.olexandr@transoftgroup.com> Date: Tue, 8 Sep 2020 18:14:42 +0300 Subject: [PATCH 171/195] MC-35416: It is not possible to Manage Shopping Cart from Customer edit page after adding product to Wish List --- .../Magento/Wishlist/Model/ResourceModel/Item/Collection.php | 2 +- .../Wishlist/Model/ResourceModel/Item/CollectionTest.php | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php index 04e320ce4bc50..7d30d958b5228 100644 --- a/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php +++ b/app/code/Magento/Wishlist/Model/ResourceModel/Item/Collection.php @@ -400,7 +400,7 @@ protected function _renderFiltersBefore() ['cat_prod' => $this->getTable('catalog_product_entity')], $this->getConnection() ->quoteInto( - 'cat_prod.type_id IN (?) AND main_table.product_id = cat_prod.entity_id', + "cat_prod.type_id IN (?) AND {$mainTableName}.product_id = cat_prod.entity_id", $availableProductTypes ), [] diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php index e347ee88af902..1cbdf6144d640 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/ResourceModel/Item/CollectionTest.php @@ -69,11 +69,14 @@ public function testLoadedProductAttributes() * * @magentoDataFixture Magento/Catalog/_files/second_product_simple.php * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * @magentoDbIsolation disabled */ - public function testGonnaCutYouDown() + public function testLoadWhenFewProductsPresent() { $this->itemCollection->setSalableFilter(true); + $this->itemCollection->addCustomerIdFilter(1); $this->itemCollection->load(); + $this->assertCount(1, $this->itemCollection->getItems()); } /** From 5584b4cf2258a18fb17ca81022dfb09206f8c852 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Tue, 8 Sep 2020 19:00:17 +0100 Subject: [PATCH 172/195] Added stories annotation for the test --- .../Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml index 252027b27bf6d..ec15693be81e4 100644 --- a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml +++ b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml @@ -14,6 +14,7 @@ <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1792"/> <title value="Image is deleted from tmp folder if is uploaded second time"/> <description value="Image is deleted from tmp folder if is uploaded second time"/> + <stories value="Image is deleted from tmp folder if is uploaded second time"/> <severity value="CRITICAL"/> <group value="media_gallery_ui"/> </annotations> From 35f7747fd853a90b3c9316c13f6da9ce62dc41ec Mon Sep 17 00:00:00 2001 From: yolouiese <honeymay@abovethefray.io> Date: Wed, 9 Sep 2020 02:37:06 +0800 Subject: [PATCH 173/195] magento/adobe-stock-integration#1801: User is not scrolled up to the Login failed. Please check if the Secret Key... error, and can miss the message - modified scroll to top script --- .../view/adminhtml/web/js/grid/columns/image.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js index bf852d0ddae68..4ff8eb36114d0 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js @@ -13,6 +13,8 @@ define([ return Column.extend({ defaults: { bodyTmpl: 'Magento_MediaGalleryUi/grid/columns/image', + messageContentSelector: 'ul.messages', + mediaGalleryContainerSelector: '.media-gallery-container', deleteImageUrl: 'media_gallery/image/delete', addSelectedBtnSelector: '#add_selected', deleteSelectedBtnSelector: '#delete_selected', @@ -270,6 +272,7 @@ define([ */ addMessage: function (code, message) { this.messages().add(code, message); + this.scrollToMessageContent(); this.messages().scheduleCleanup(); }, @@ -284,6 +287,20 @@ define([ !this.massaction().massActionMode()) { this.deselectImage(); } + }, + + /** + * Scroll to the top of media gallery page + */ + scrollToMessageContent: function () { + var scrollTargetElement = $(this.messageContentSelector), + scrollTargetContainer = $(this.mediaGalleryContainerSelector); + + scrollTargetContainer.find(scrollTargetElement).get(0).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'nearest' + }); } }); }); From cef2186708426f7683cbd38d308562b2aa80ff46 Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Wed, 9 Sep 2020 08:26:11 +0300 Subject: [PATCH 174/195] refactor mftf --- ...inGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename app/code/Magento/Ui/Test/Mftf/Test/{AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml => AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml} (98%) diff --git a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml similarity index 98% rename from app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml rename to app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml index 09c87010b5743..c7236c33e7cc0 100644 --- a/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFilters.xml +++ b/app/code/Magento/Ui/Test/Mftf/Test/AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest.xml @@ -7,12 +7,13 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminGridFilterRemoveErrorMessageBeforeApplyFilters"> + <test name="AdminGridFilterRemoveErrorMessageBeforeApplyFiltersTest"> <annotations> <stories value="Reset Error Messages"/> <title value="Remove Error Message Before Apply Filters"/> <description value="Test login to Admin UI and Remove Error Message Before Apply Filters"/> - <severity value="CRITICAL"/> + <severity value="MAJOR"/> + <testCaseId value="MC-37450"/> <group value="ui"/> </annotations> From d8314a13c2d2cc71e78a58be7c907eef56078ae8 Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Wed, 9 Sep 2020 09:46:22 +0300 Subject: [PATCH 175/195] add testQueryDisableRelatedProduct --- .../RelatedProduct/GetRelatedProductsTest.php | 36 ++++++++++++++++- .../_files/products_related_disabled.php | 40 +++++++++++++++++++ .../products_related_disabled_rollback.php | 33 +++++++++++++++ 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php create mode 100644 dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php index c2f94128ef8ec..cb210b180682c 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -10,7 +10,7 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; /** - * Get related products test + * Test coverage for get related products */ class GetRelatedProductsTest extends GraphQlAbstract { @@ -49,6 +49,40 @@ public function testQueryRelatedProducts() self::assertRelatedProducts($relatedProducts); } + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_related_disabled.php + */ + public function testQueryDisableRelatedProduct() + { + $productSku = 'simple_with_cross'; + + $query = <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) + { + items { + related_products + { + sku + name + url_key + created_at + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertCount(1, $response['products']['items']); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('related_products', $response['products']['items'][0]); + $relatedProducts = $response['products']['items'][0]['related_products']; + self::assertCount(0, $relatedProducts); + } + /** * @magentoApiDataFixture Magento/Catalog/_files/products_crosssell.php */ diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php new file mode 100644 index 0000000000000..68d5c43434daa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('Simple Related Product') + ->setSku('simple') + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_DISABLED) + ->setWebsiteIds([1]) + ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) + ->save(); + +/** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */ +$productLink = $objectManager->create(\Magento\Catalog\Api\Data\ProductLinkInterface::class); +$productLink->setSku('simple_with_cross'); +$productLink->setLinkedProductSku('simple'); +$productLink->setPosition(1); +$productLink->setLinkType('related'); + +$product = $objectManager->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('Simple Product With Related Product') + ->setSku('simple_with_cross') + ->setPrice(10) + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) + ->setProductLinks([$productLink]) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php new file mode 100644 index 0000000000000..958398660b132 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/products_related_disabled_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + +try { + $firstProduct = $productRepository->get('simple', false, null, true); + $productRepository->delete($firstProduct); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +try { + $secondProduct = $productRepository->get('simple_with_cross', false, null, true); + $productRepository->delete($secondProduct); +} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); From 699c98fa1a2db63badd7441697c1da4b0dabb370 Mon Sep 17 00:00:00 2001 From: Vadim Malesh <51680850+engcom-Charlie@users.noreply.github.com> Date: Wed, 9 Sep 2020 10:58:16 +0300 Subject: [PATCH 176/195] add testCaseId --- ...torefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml index 4e01f950cd087..0e2ae9bf5cc5f 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontBundlePlaceOrderWithMultipleOptionsSuccessTest.xml @@ -12,7 +12,8 @@ <stories value="Bundle product details page"/> <title value="Customer should be able to see all the bundle items in invoice view"/> <description value="Customer should be able to see all the bundle items in invoice view"/> - <severity value="CRITICAL"/> + <severity value="MAJOR"/> + <testCaseId value="MC-37515"/> <group value="Bundle"/> </annotations> <before> From 86cc39c38f0d6111de014cb0b3f41841f377d67a Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Wed, 9 Sep 2020 09:52:37 +0100 Subject: [PATCH 177/195] magento/magento2#29906: Added test case id for AdminUploadSameImageDeleteFromTemporaryFolderTest --- .../Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml index ec15693be81e4..8add2021f056b 100644 --- a/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml +++ b/app/code/Magento/MediaGalleryCatalogIntegration/Test/Mftf/Test/AdminUploadSameImageDeleteFromTemporaryFolderTest.xml @@ -15,6 +15,7 @@ <title value="Image is deleted from tmp folder if is uploaded second time"/> <description value="Image is deleted from tmp folder if is uploaded second time"/> <stories value="Image is deleted from tmp folder if is uploaded second time"/> + <testCaseId value="https://studio.cucumber.io/projects/131313/test-plan/folders/943908/scenarios/4836631"/> <severity value="CRITICAL"/> <group value="media_gallery_ui"/> </annotations> From 01b7906cd3e339c0467f6cc9af6d681c7627482b Mon Sep 17 00:00:00 2001 From: "vadim.malesh" <engcom-vendorworker-charlie@adobe.com> Date: Wed, 9 Sep 2020 13:32:33 +0300 Subject: [PATCH 178/195] revert changing the obsolete method --- app/code/Magento/Widget/Model/Widget.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Widget/Model/Widget.php b/app/code/Magento/Widget/Model/Widget.php index c8b13ef028592..b05b70cfcbc71 100644 --- a/app/code/Magento/Widget/Model/Widget.php +++ b/app/code/Magento/Widget/Model/Widget.php @@ -143,7 +143,8 @@ public function getWidgetByClassType($type) */ public function getConfigAsXml($type) { - return $this->getWidgetByClassType($type); + // phpstan:ignore + return $this->getXmlElementByType($type); } /** From f8fd731bc3a56a59a89ffc967456d162275476aa Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Wed, 9 Sep 2020 14:01:01 +0300 Subject: [PATCH 179/195] adobe-stock-integration#1813: fix saving default directory in the default bookmark of media gallery --- .../view/adminhtml/web/js/directory/directoryTree.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js index 2e1e9a980cd59..b53896e599b8d 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js @@ -18,12 +18,14 @@ define([ return Component.extend({ defaults: { filterChipsProvider: 'componentType = filters, ns = ${ $.ns }', + bookmarkProvider: 'componentType = bookmark, ns = ${ $.ns }', directoryTreeSelector: '#media-gallery-directory-tree', getDirectoryTreeUrl: 'media_gallery/directories/gettree', createDirectoryUrl: 'media_gallery/directories/create', deleteDirectoryUrl: 'media_gallery/directories/delete', jsTreeReloaded: null, modules: { + bookmarks: '${ $.bookmarkProvider }', directories: '${ $.name }_directories', filterChips: '${ $.filterChipsProvider }' }, @@ -256,7 +258,15 @@ define([ return; } + if (!_.isUndefined(this.bookmarks())) { + if (!_.size(this.bookmarks().getViewData(this.bookmarks().defaultIndex))) { + setTimeout(function () { + this.updateSelectedDirectory(); + }.bind(this), 500); + return; + } + } currentTreePath = this.isFilterApplied(currentFilterPath) || _.isNull(requestedDirectory) ? currentFilterPath : requestedDirectory; From babb4ef472086cf681f73d7c7b6065941e8c2560 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Wed, 9 Sep 2020 15:21:28 +0300 Subject: [PATCH 180/195] remove redundunt file --- .../AdminOpentCmsBlockActionGroup.xml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml deleted file mode 100644 index 4da29104797ac..0000000000000 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpentCmsBlockActionGroup.xml +++ /dev/null @@ -1,16 +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="AdminOpenCmsBlockActionGroup" deprecated="Use AdminOpenCmsBlockActionGroup instead."> - <arguments> - <argument name="block_id" type="string"/> - </arguments> - <amOnPage url="{{AdminEditBlockPage.url(block_id)}}" stepKey="openEditCmsBlock"/> - </actionGroup> -</actionGroups> From 204c4d9f933df1bb2949f060d6f53df68eab3994 Mon Sep 17 00:00:00 2001 From: Oleh Usik <o.usik@atwix.com> Date: Wed, 9 Sep 2020 15:45:35 +0300 Subject: [PATCH 181/195] some changes --- .../AdminCreateTaxRateAllPostCodesTest.xml | 4 +--- .../Test/AdminCreateTaxRateLargeRateTest.xml | 5 ++--- ...AdminCreateTaxRateSpecificPostcodeTest.xml | 4 +--- ...dminCreateTaxRateWiderZipCodeRangeTest.xml | 4 +--- .../AdminCreateTaxRateZipCodeRangeTest.xml | 4 +--- .../Mftf/Test/DeleteTaxRateEntityTest.xml | 4 +--- .../AdminClickRowInGridActionGroup.xml | 19 ------------------- .../Section/AdminDataGridTableSection.xml | 4 ++-- 8 files changed, 9 insertions(+), 39 deletions(-) delete mode 100644 app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.xml diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml index 494c0ae74c375..ceb04a9c42e66 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateAllPostCodesTest.xml @@ -27,9 +27,7 @@ <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> - <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> - <argument name="row_number" value="1" /> - </actionGroup> + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="clickFirstRow"/> <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml index e195a0b86cb26..7497b950a8c0e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateLargeRateTest.xml @@ -27,9 +27,8 @@ <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> - <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> - <argument name="row_number" value="1" /> - </actionGroup> + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="clickFirstRow"/> + <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml index bff1bb95e9540..da89ad3e9337c 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateSpecificPostcodeTest.xml @@ -27,9 +27,7 @@ <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> - <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> - <argument name="row_number" value="1" /> - </actionGroup> + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="clickFirstRow"/> <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml index 570608bb6adf6..da30157d94182 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateWiderZipCodeRangeTest.xml @@ -27,9 +27,7 @@ <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> - <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> - <argument name="row_number" value="1" /> - </actionGroup> + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="clickFirstRow"/> <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml index a8e47b77e27c0..93e0f6514e83b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminCreateTaxRateZipCodeRangeTest.xml @@ -27,9 +27,7 @@ <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="{{SimpleTaxRate.code}}" /> </actionGroup> - <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> - <argument name="row_number" value="1" /> - </actionGroup> + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="clickFirstRow"/> <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> </after> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml index 341b2e1aa6344..751989497d10e 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/DeleteTaxRateEntityTest.xml @@ -29,9 +29,7 @@ <actionGroup ref="AdminFilterTaxRateByCodeActionGroup" stepKey="filterByCode"> <argument name="taxRateCode" value="$$initialTaxRate.code$$" /> </actionGroup> - <actionGroup ref="AdminClickRowInGridActionGroup" stepKey="clickFirstRow"> - <argument name="row_number" value="1" /> - </actionGroup> + <actionGroup ref="AdminSelectFirstGridRowActionGroup" stepKey="clickFirstRow"/> <actionGroup ref="AdminDeleteTaxRateActionGroup" stepKey="deleteTaxRate"/> <see selector="{{AdminMessagesSection.success}}" userInput="You Deleted the tax rate." stepKey="seeSuccess1"/> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.xml deleted file mode 100644 index c538a6d530645..0000000000000 --- a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminClickRowInGridActionGroup.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="AdminClickRowInGridActionGroup"> - <arguments> - <argument name="row_number" type="string"/> - </arguments> - - <click selector="{{AdminDataGridTableSection.row(row_number)}}" stepKey="clickOnFirstRow"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml index 11e42353a0663..c5b000259e265 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml @@ -9,11 +9,11 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminDataGridTableSection"> - <element name="firstRow" type="button" selector="tr.data-row:nth-of-type(1)" timeout="60"/> + <element name="firstRow" type="button" selector="table.data-grid tbody > tr:nth-of-type(1)" timeout="60"/> <element name="columnHeader" type="button" selector="//div[@data-role='grid-wrapper']//table[contains(@class, 'data-grid')]/thead/tr/th[contains(@class, 'data-grid-th')]/span[text() = '{{label}}']" parameterized="true" timeout="30"/> <element name="column" type="text" selector="//tr//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{col}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> <element name="rowCheckbox" type="checkbox" selector="table.data-grid tbody > tr:nth-of-type({{row}}) td.data-grid-checkbox-cell input" parameterized="true"/> - <element name="row" type="text" selector="table.data-grid tbody > tr:nth-of-type({{row}})" parameterized="true" timeout="30"/> + <element name="row" type="text" selector="table.data-grid tbody > tr:nth-of-type({{row}})" parameterized="true"/> <element name="rows" type="text" selector="table.data-grid tbody > tr.data-row"/> <!--Specific cell e.g. {{Section.gridCell('1', 'Name')}}--> <element name="gridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> From 235318a1d715e32ff1d685a9f36c612501e77c9a Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Wed, 9 Sep 2020 17:01:01 +0300 Subject: [PATCH 182/195] adobe-stock-integration#1813: mftf test --- ...iaGalleryDefaultViewWithoutFiltersTest.xml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml new file mode 100644 index 0000000000000..a8df997528ad3 --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMediaGalleryDefaultViewWithoutFiltersTest"> + <annotations> + <features value="AdminMediaGalleryDefaultViewWithoutFiltersTest"/> + <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1813"/> + <title value="User shouldn't see applied filters if media gallery switched to Default View"/> + <description value="No filters should be applied if Default View selected"/> + <severity value="CRITICAL"/> + <group value="media_gallery_ui"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="category"/> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + </before> + <after> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + </after> + + <!-- Open category page --> + <actionGroup ref="AdminOpenCategoryGridPageActionGroup" stepKey="openCategoryPage"/> + <actionGroup ref="AdminEditCategoryInGridPageActionGroup" stepKey="editCategoryItem"> + <argument name="categoryName" value="$category.name$"/> + </actionGroup> + + <!-- Open media gallery folder --> + <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGallery"/> + <actionGroup ref="AdminEnhancedMediaGallerySelectCustomBookmarksViewActionGroup" stepKey="selectDefaultView"> + <argument name="selectView" value="Default View"/> + </actionGroup> + + <!-- Asset folder is empty --> + <actionGroup ref="AdminEnhancedMediaGalleryAssertNoActiveFiltersAppliedActionGroup" stepKey="assertEmptyFolder"/> + </test> +</tests> From dd99a7ffec7c3e34c171b1c53da9fe7f35da45f5 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Thu, 10 Sep 2020 01:34:25 +0800 Subject: [PATCH 183/195] magento/adobe-stock-integration#1802: The three dots context menu is not closed after unsuccessful licensing from Media Gallery - modified image.js --- .../view/adminhtml/web/js/grid/columns/image.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js index bf852d0ddae68..2ddc601385496 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js @@ -270,6 +270,7 @@ define([ */ addMessage: function (code, message) { this.messages().add(code, message); + this.closeContextMenu(); this.messages().scheduleCleanup(); }, @@ -284,6 +285,13 @@ define([ !this.massaction().massActionMode()) { this.deselectImage(); } + }, + + /** + * Action to close the context menu in media gallery. + */ + closeContextMenu: function () { + $('.media-gallery-wrap').click(); } }); }); From 0c906a1a3c19a15b5ea49211daa7900f03bfd791 Mon Sep 17 00:00:00 2001 From: Yurii Sapiha <yurasapiga93@gmail.com> Date: Thu, 10 Sep 2020 13:41:47 +0300 Subject: [PATCH 184/195] MC-37196: Admin: invoice order for customer --- .../Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php index 4bb36ee2dcb0a..13003e40dc0a3 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Invoice/SaveTest.php @@ -196,11 +196,14 @@ public function testAclNoAccess() /** * Checks that order protect code is not changing after invoice submitting * + * @magentoDataFixture Magento/Sales/_files/order.php + * * @return void */ public function testOrderProtectCodePreserveAfterInvoiceSave(): void { - $order = $this->prepareRequest(); + $order = $this->getOrder('100000001'); + $this->prepareRequest([], ['order_id' => $order->getEntityId()]); $protectCode = $order->getProtectCode(); $this->dispatch($this->uri); $invoicedOrder = $this->getOrder('100000001'); From d07e0a9031b0f4942dbeba0d7a32e2b77021cf5c Mon Sep 17 00:00:00 2001 From: engcom-Echo <engcom-vendorworker-echo@adobe.com> Date: Wed, 9 Sep 2020 17:30:53 +0300 Subject: [PATCH 185/195] add MFTF test --- ...frontConfigurableProductMSRPCovertTest.xml | 122 ++++++++++++++++++ .../AdminSetAdvancedPricingActionGroup.xml | 29 +++++ 2 files changed, 151 insertions(+) create mode 100644 app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductMSRPCovertTest.xml create mode 100644 app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminSetAdvancedPricingActionGroup.xml diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductMSRPCovertTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductMSRPCovertTest.xml new file mode 100644 index 0000000000000..9526e8568b26d --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductViewTest/StorefrontConfigurableProductMSRPCovertTest.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontConfigurableProductMSRPCovertTest"> + <annotations> + <features value="ConfigurableProduct"/> + <stories value="View configurable product options, verify convert MSRP currency on storefront."/> + <title value="Verify convert MSRP currency of configurable product options"/> + <description value="Check convert MSRP currency of configurable product options."/> + <testCaseId value="MC-37575"/> + <severity value="MAJOR"/> + <group value="ConfigurableProduct"/> + </annotations> + + <before> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeOption2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <createData entity="ApiSimpleProductWithPrice50" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + <createData entity="ApiSimpleProductWithPrice60" stepKey="createConfigChildProduct2"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <requiredEntity createDataKey="getConfigAttributeOption2"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild2"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct2"/> + </createData> + + <createData entity="MsrpEnableMAP" stepKey="enableMAP"/> + <magentoCLI command="config:set currency/options/allow EUR,USD" stepKey="setCurrencyAllow"/> + </before> + <after> + + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <createData entity="MsrpDisableMAP" stepKey="disableMAP"/> + <magentoCLI command="config:set currency/options/allow USD" stepKey="setCurrencyAllow"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutOfAdmin"/> + </after> + + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToFirstChildProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct1.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <actionGroup ref="AdminSetAdvancedPricingActionGroup" stepKey="setAdvancedPricingFirst"> + <argument name="advancedPrice" value="100"/> + </actionGroup> + + <actionGroup ref="AdminProductPageOpenByIdActionGroup" stepKey="goToSecondChildProductEditPage"> + <argument name="productId" value="$$createConfigChildProduct2.id$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForProductPageLoad1"/> + <actionGroup ref="AdminSetAdvancedPricingActionGroup" stepKey="setAdvancedPricingSecond"> + <argument name="advancedPrice" value="100"/> + </actionGroup> + + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCache"> + <argument name="tags" value=""/> + </actionGroup> + + <actionGroup ref="OpenStoreFrontProductPageActionGroup" stepKey="navigateToProduct"> + <argument name="productUrlKey" value="$$createConfigProduct.custom_attributes[url_key]$$"/> + </actionGroup> + + <actionGroup ref="StorefrontSwitchCurrencyActionGroup" stepKey="switchEURCurrency"> + <argument name="currency" value="EUR"/> + </actionGroup> + + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption1.value$$" stepKey="selectFirstOption"/> + <waitForElement selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="waitForLoad"/> + <grabTextFrom selector="{{StorefrontProductInfoMainSection.mapPrice}}" stepKey="grabProductMapPrice"/> + <assertNotEquals stepKey="assertProductMapPrice"> + <actualResult type="const">($grabProductMapPrice)</actualResult> + <expectedResult type="string">€100.00</expectedResult> + </assertNotEquals> + </test> +</tests> diff --git a/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminSetAdvancedPricingActionGroup.xml b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminSetAdvancedPricingActionGroup.xml new file mode 100644 index 0000000000000..d9a34bbd03e4f --- /dev/null +++ b/app/code/Magento/Msrp/Test/Mftf/ActionGroup/AdminSetAdvancedPricingActionGroup.xml @@ -0,0 +1,29 @@ +<?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="AdminSetAdvancedPricingActionGroup"> + <annotations> + <description>Set advanced pricing and Save product on the Admin Product creation/edit page.</description> + </annotations> + <arguments> + <argument name="advancedPrice" type="string"/> + </arguments> + + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElement selector="{{AdminProductFormAdvancedPricingSection.msrp}}" stepKey="waitForMsrp"/> + <fillField selector="{{AdminProductFormAdvancedPricingSection.msrp}}" userInput="{{advancedPrice}}" stepKey="setMsrpForFirstChildProduct"/> + <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <scrollToTopOfPage stepKey="scrollTopPageProduct"/> + <waitForElementVisible selector="{{AdminProductFormActionSection.saveButton}}" stepKey="waitForSaveProductButton"/> + <click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="clickSaveProduct"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitProductSaveSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You saved the product." stepKey="seeSaveConfirmation"/> + </actionGroup> +</actionGroups> \ No newline at end of file From 5cc8b4d640c3acca3ed949016a4936011db92040 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Thu, 10 Sep 2020 22:52:58 +0800 Subject: [PATCH 186/195] magento/adobe-stock-integration#1802: The three dots context menu is not closed after unsuccessful licensing from Media Gallery - modified selector --- .../MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js index 2ddc601385496..b6e51fc65f3c4 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/grid/columns/image.js @@ -16,6 +16,7 @@ define([ deleteImageUrl: 'media_gallery/image/delete', addSelectedBtnSelector: '#add_selected', deleteSelectedBtnSelector: '#delete_selected', + gridSelector: '[data-id="media-gallery-masonry-grid"]', selected: null, fields: { id: 'id', @@ -291,7 +292,7 @@ define([ * Action to close the context menu in media gallery. */ closeContextMenu: function () { - $('.media-gallery-wrap').click(); + $(this.gridSelector).click(); } }); }); From e786fb14f08545a43b2156a9e08a852e0b366376 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 11 Sep 2020 00:47:07 +0800 Subject: [PATCH 187/195] magento/adobe-stock-integration#1795: [MFTF] Unskip AdminMediaGalleryCatalogUiUsedInProductFilterTest - applied requested changes --- .../Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml index 3caa106ffa245..a66009e9d2045 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiUsedInProductFilterTest.xml @@ -61,6 +61,7 @@ <actionGroup ref="AdminAssertMediaGalleryFilterPlaceHolderGridActionGroup" stepKey="assertFilterApplied"> <argument name="filterPlaceholder" value="{{ImageMetadata.title}}"/> </actionGroup> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFiltersOnProductGrid"/> <deleteData createDataKey="product" stepKey="deleteProduct"/> <actionGroup ref="AdminOpenStandaloneMediaGalleryActionGroup" stepKey="openMediaGallery"/> From b9daa9ea6b7bd57fdbb1dcf741bc1b8be2d0d713 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Thu, 10 Sep 2020 20:46:35 +0100 Subject: [PATCH 188/195] Skipped MFTF tests related to isolated issue --- .../AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml | 3 +++ .../Test/AdminMediaGalleryCmsUiUsedInPagesFilterTest.xml | 3 +++ .../Test/AdminMediaGalleryDisabledContentFilterTest.xml | 7 +++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml b/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml index 4230640e76303..5a375d9153a6d 100644 --- a/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml +++ b/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryAssertUsedInLinkPagesGridTest.xml @@ -9,6 +9,9 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMediaGalleryAssertUsedInLinkPagesGridTest"> <annotations> + <skip> + <issueId value="https://github.com/magento/adobe-stock-integration/issues/1825"/> + </skip> <features value="AdminMediaGalleryUsedInBlocksFilter"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1168"/> <title value="Used in pages link"/> diff --git a/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryCmsUiUsedInPagesFilterTest.xml b/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryCmsUiUsedInPagesFilterTest.xml index 038f1ae077b4a..e72e65cf8de90 100644 --- a/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryCmsUiUsedInPagesFilterTest.xml +++ b/app/code/Magento/MediaGalleryCmsUi/Test/Mftf/Test/AdminMediaGalleryCmsUiUsedInPagesFilterTest.xml @@ -9,6 +9,9 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMediaGalleryCmsUiUsedInPagesFilterTest"> <annotations> + <skip> + <issueId value="https://github.com/magento/adobe-stock-integration/issues/1825"/> + </skip> <features value="AdminMediaGalleryUsedInPagesFilter"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1168"/> <title value="Used in pages filter"/> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDisabledContentFilterTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDisabledContentFilterTest.xml index 963a0b954e45b..5926b115afccf 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDisabledContentFilterTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDisabledContentFilterTest.xml @@ -9,6 +9,9 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminMediaGalleryDisabledContentFilterTest"> <annotations> + <skip> + <issueId value="https://github.com/magento/adobe-stock-integration/issues/1825"/> + </skip> <features value="MediaGallery"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1464"/> <title value="User filter asset by disabled content"/> @@ -58,8 +61,8 @@ <actionGroup ref="AdminEnhancedMediaGallerySelectImageForMassActionActionGroup" stepKey="selectFirstImageToDelete"> <argument name="imageName" value="{{ImageMetadata.title}}"/> </actionGroup> - <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clikDeleteSelectedButton"/> + <actionGroup ref="AdminEnhancedMediaGalleryClickDeleteImagesButtonActionGroup" stepKey="clickDeleteSelectedButton"/> <actionGroup ref="AdminEnhancedMediaGalleryConfirmDeleteImagesActionGroup" stepKey="deleteImages"/> - + </test> </tests> From 2956a4816591ee3e3cc5de681ce0f49f5b3ff44f Mon Sep 17 00:00:00 2001 From: Ihor Sviziev <ihor-sviziev@users.noreply.github.com> Date: Fri, 11 Sep 2020 09:51:03 +0300 Subject: [PATCH 189/195] Update minimum required php version in bootstrap.php --- app/bootstrap.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/bootstrap.php b/app/bootstrap.php index 4974acdf0fc80..4c96d084f4bb6 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -14,15 +14,15 @@ #ini_set('display_errors', 1); /* PHP version validation */ -if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 70103) { +if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 70300) { if (PHP_SAPI == 'cli') { - echo 'Magento supports PHP 7.1.3 or later. ' . - 'Please read https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements-tech.html'; + echo 'Magento supports PHP 7.3.0 or later. ' . + 'Please read https://devdocs.magento.com/guides/v2.4/install-gde/system-requirements-tech.html'; } else { echo <<<HTML <div style="font:12px/1.35em arial, helvetica, sans-serif;"> - <p>Magento supports PHP 7.1.3 or later. Please read - <a target="_blank" href="https://devdocs.magento.com/guides/v2.3/install-gde/system-requirements-tech.html"> + <p>Magento supports PHP 7.3.0 or later. Please read + <a target="_blank" href="https://devdocs.magento.com/guides/v2.4/install-gde/system-requirements-tech.html"> Magento System Requirements</a>. </div> HTML; From 8cf1e27599b571472f33475709a9586612415c9b Mon Sep 17 00:00:00 2001 From: Mykhailo Matiola <mykhailo.matiola@transoftgroup.com> Date: Fri, 11 Sep 2020 10:48:48 +0300 Subject: [PATCH 190/195] MC-36907: Storefront: Create reorder from customer profile page. --- .../TestFramework/Core/Version/View.php | 24 +++++++++++++++++++ .../Sales/Controller/Order/ReorderTest.php | 14 ++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php diff --git a/dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php b/dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php new file mode 100644 index 0000000000000..85007ad560d53 --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Core/Version/View.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Core\Version; + +/** + * Class for magento version flag. + */ +class View +{ + /** + * Returns flag that checks that magento version is clean community version. + * + * @return bool + */ + public function isVersionUpdated(): bool + { + return false; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php index 647dc557485d2..3b32e7238cc76 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php @@ -14,6 +14,7 @@ use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Api\Data\CartInterface; use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\TestFramework\Core\Version\View; use Magento\TestFramework\Request; use Magento\TestFramework\TestCase\AbstractController; @@ -44,6 +45,11 @@ class ReorderTest extends AbstractController /** @var Escaper */ private $escaper; + /** + * @var View + */ + private $versionChecker; + /** * @inheritdoc */ @@ -56,6 +62,7 @@ protected function setUp(): void $this->customerSession = $this->_objectManager->get(Session::class); $this->quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class); $this->escaper = $this->_objectManager->get(Escaper::class); + $this->versionChecker = $this->_objectManager->get(View::class); } /** @@ -121,7 +128,12 @@ public function testReorderByAnotherCustomer(): void $this->customerSession->setCustomerId(1); $order = $this->orderFactory->create()->loadByIncrementId('100000555'); $this->dispatchReorderRequest((int)$order->getId()); - $this->assertRedirect($this->stringContains('sales/order/history')); + + if ($this->versionChecker->isVersionUpdated()) { + $this->assertRedirect($this->stringContains('noroute')); + } else { + $this->assertRedirect($this->stringContains('sales/order/history')); + } } /** From b1ed802014177c6bb1b5d49ecb398fa71ccee1d2 Mon Sep 17 00:00:00 2001 From: Viktor Kopin <viktor.kopin@transoftgroup.com> Date: Fri, 11 Sep 2020 15:06:36 +0300 Subject: [PATCH 191/195] adobe-stock-integration#1813: fix static, add testcaseId --- .../Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml | 1 + .../view/adminhtml/web/js/directory/directoryTree.js | 1 + 2 files changed, 2 insertions(+) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml index a8df997528ad3..a41a27cdd715d 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml @@ -13,6 +13,7 @@ <features value="AdminMediaGalleryDefaultViewWithoutFiltersTest"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1813"/> <title value="User shouldn't see applied filters if media gallery switched to Default View"/> + <testCaseId value="https://studio.cucumber.io/projects/131313/test-plan/folders/1320712/scenarios/5199870"/> <description value="No filters should be applied if Default View selected"/> <severity value="CRITICAL"/> <group value="media_gallery_ui"/> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js index b53896e599b8d..b1224c33d3561 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/web/js/directory/directoryTree.js @@ -258,6 +258,7 @@ define([ return; } + if (!_.isUndefined(this.bookmarks())) { if (!_.size(this.bookmarks().getViewData(this.bookmarks().defaultIndex))) { setTimeout(function () { From be49be365affd0c4d29f42f99335669bd943a8a3 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko <serg.ivashchenko@gmail.com> Date: Fri, 11 Sep 2020 14:22:39 +0100 Subject: [PATCH 192/195] magento/magento2#29959: Added stories tag to AdminMediaGalleryDefaultViewWithoutFiltersTest --- .../Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml index a41a27cdd715d..7c2087247c574 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Test/AdminMediaGalleryDefaultViewWithoutFiltersTest.xml @@ -13,6 +13,7 @@ <features value="AdminMediaGalleryDefaultViewWithoutFiltersTest"/> <useCaseId value="https://github.com/magento/adobe-stock-integration/issues/1813"/> <title value="User shouldn't see applied filters if media gallery switched to Default View"/> + <stories value="Media gallery default directory"/> <testCaseId value="https://studio.cucumber.io/projects/131313/test-plan/folders/1320712/scenarios/5199870"/> <description value="No filters should be applied if Default View selected"/> <severity value="CRITICAL"/> From 18023284e0a443fc21eebdce8556667800e07646 Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 11 Sep 2020 22:08:34 +0800 Subject: [PATCH 193/195] magento/adobe-stock-integration#1802: The three dots context menu is not closed after unsuccessful licensing from Media Gallery - added action group and modified sections in media gallery ui mftf --- ...nMediaGalleryContextMenuOpenedActionGroup.xml | 16 ++++++++++++++++ ...inEnhancedMediaGalleryImageActionsSection.xml | 1 + 2 files changed, 17 insertions(+) create mode 100644 app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminMediaGalleryContextMenuOpenedActionGroup.xml diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminMediaGalleryContextMenuOpenedActionGroup.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminMediaGalleryContextMenuOpenedActionGroup.xml new file mode 100644 index 0000000000000..ede7052712b4b --- /dev/null +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/ActionGroup/AssertAdminMediaGalleryContextMenuOpenedActionGroup.xml @@ -0,0 +1,16 @@ +<?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="AssertAdminMediaGalleryContextMenuOpenedActionGroup"> + <annotations> + <description>Verify that context menu is closed in Media Gallery.</description> + </annotations> + <dontSeeElement selector="{{AdminEnhancedMediaGalleryImageActionsSection.contextMenuItem}}" stepKey="verifyContextMenuIsClosed" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryImageActionsSection.xml b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryImageActionsSection.xml index 3f13a57697e6f..f36fca88dc760 100644 --- a/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryImageActionsSection.xml +++ b/app/code/Magento/MediaGalleryUi/Test/Mftf/Section/AdminEnhancedMediaGalleryImageActionsSection.xml @@ -9,6 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEnhancedMediaGalleryImageActionsSection"> <element name="openContextMenu" type="button" selector=".three-dots"/> + <element name="contextMenuItem" type="block" selector="//div[@class='media-gallery-image']//ul[@class='action-menu _active']//li//a[@class='action-menu-item']"/> <element name="viewDetails" type="button" selector="[data-ui-id='action-image-details']"/> <element name="delete" type="button" selector="[data-ui-id='action-delete']"/> <element name="edit" type="button" selector="[data-ui-id='action-edit']"/> From 7b2d58b11c7ce09f3b9bee538f35ff78f7653f4c Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Fri, 11 Sep 2020 23:36:04 +0800 Subject: [PATCH 194/195] magento/adobe-stock-integration#1829: Duplicated items in Asset filter on Entities grids - added filter rate limit and method configuration to ui component --- .../view/adminhtml/ui_component/cms_block_listing.xml | 2 ++ .../view/adminhtml/ui_component/cms_page_listing.xml | 2 ++ .../view/adminhtml/ui_component/product_listing.xml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_block_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_block_listing.xml index 20988fad5ff35..974171617a19b 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_block_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_block_listing.xml @@ -21,6 +21,8 @@ <item name="identityColumn" xsi:type="string">block_id</item> <item name="filterPlaceholder" xsi:type="string" translate="true">Asset Title</item> <item name="emptyOptionsHtml" xsi:type="string" translate="true">Start typing to find assets</item> + <item name="filterRateLimit" xsi:type="string" translate="true">1000</item> + <item name="filterRateLimitMethod" xsi:type="string" translate="true">notifyWhenChangesStop</item> <item name="searchOptions" xsi:type="boolean">true</item> <item name="filterOptions" xsi:type="boolean">true</item> <item name="searchUrl" xsi:type="url" path="media_gallery/asset/search" /> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_page_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_page_listing.xml index 4abeb71679bde..4e59b485df503 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_page_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/cms_page_listing.xml @@ -23,6 +23,8 @@ <item name="searchOptions" xsi:type="boolean">true</item> <item name="filterPlaceholder" xsi:type="string" translate="true">Asset Title</item> <item name="emptyOptionsHtml" xsi:type="string" translate="true">Start typing to find assets</item> + <item name="filterRateLimit" xsi:type="string" translate="true">1000</item> + <item name="filterRateLimitMethod" xsi:type="string" translate="true">notifyWhenChangesStop</item> <item name="searchUrl" xsi:type="url" path="media_gallery/asset/search" /> <item name="validationUrl" xsi:type="url" path="media_gallery/asset/getSelected"/> <item name="levelsVisibility" xsi:type="number">1</item> diff --git a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/product_listing.xml index 4634652e9cc19..0710479ec0f61 100644 --- a/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/MediaGalleryUi/view/adminhtml/ui_component/product_listing.xml @@ -23,6 +23,8 @@ <item name="searchOptions" xsi:type="boolean">true</item> <item name="filterPlaceholder" xsi:type="string" translate="true">Asset Title</item> <item name="emptyOptionsHtml" xsi:type="string" translate="true">Start typing to find assets</item> + <item name="filterRateLimit" xsi:type="string" translate="true">1000</item> + <item name="filterRateLimitMethod" xsi:type="string" translate="true">notifyWhenChangesStop</item> <item name="searchUrl" xsi:type="url" path="media_gallery/asset/search" /> <item name="validationUrl" xsi:type="url" path="media_gallery/asset/getSelected"/> <item name="levelsVisibility" xsi:type="number">1</item> From a7a1038b116b7c4c962d28defb3feb3eed7664fd Mon Sep 17 00:00:00 2001 From: joweecaquicla <joie@abovethefray.io> Date: Sat, 12 Sep 2020 00:32:41 +0800 Subject: [PATCH 195/195] magento/adobe-stock-integration#1794: [MFTF] Unskip AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest - removed custom category entity and modified test --- .../AdminEnhancedMediaGalleryCategoryData.xml | 17 ----------------- ...atalogUiVerifyUsedInLinkCategoryGridTest.xml | 5 +++-- 2 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml deleted file mode 100644 index d123cce516533..0000000000000 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Data/AdminEnhancedMediaGalleryCategoryData.xml +++ /dev/null @@ -1,17 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> - <entity name="TestSubCategory" type="category"> - <data key="name" unique="suffix">TestSubCategory</data> - <data key="name_lwr" unique="suffix">testsubcategory</data> - <data key="is_active">true</data> - <data key="include_in_menu">true</data> - </entity> -</entities> diff --git a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml index 40a5cf8e591fc..f9ffda43d2547 100644 --- a/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml +++ b/app/code/Magento/MediaGalleryCatalogUi/Test/Mftf/Test/AdminMediaGalleryCatalogUiVerifyUsedInLinkCategoryGridTest.xml @@ -19,7 +19,7 @@ <group value="media_gallery_ui"/> </annotations> <before> - <createData entity="TestSubCategory" stepKey="category"/> + <createData entity="SimpleSubCategory" stepKey="category"/> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> </before> <after> @@ -37,6 +37,7 @@ <argument name="category" value="$$category$$"/> </actionGroup> <actionGroup ref="AdminOpenMediaGalleryFromCategoryImageUploaderActionGroup" stepKey="openMediaGalleryFromImageUploader"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear" /> <actionGroup ref="ResetAdminDataGridToDefaultViewActionGroup" stepKey="resetCategoryImageGalleryGridToDefaultView"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearFilter"/> <actionGroup ref="AdminMediaGalleryOpenNewFolderFormActionGroup" stepKey="openNewFolderForm"/> @@ -61,7 +62,7 @@ <actionGroup ref="AdminMediaGalleryFolderSelectActionGroup" stepKey="selectCategoryImageFolder"> <argument name="name" value="categoryImage"/> </actionGroup> - <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> <actionGroup ref="AdminEnhancedMediaGalleryViewImageDetails" stepKey="openViewImageDetails"/> <actionGroup ref="AdminEnhancedMediaGalleryClickEntityUsedInActionGroup" stepKey="clickUsedInCategories"> <argument name="entityName" value="Categories"/>