diff --git a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php index 623a75015eb2f..415ce7c4c21fc 100644 --- a/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php +++ b/app/code/Magento/Backend/Block/Widget/Grid/Column/Renderer/AbstractRenderer.php @@ -9,6 +9,9 @@ use Magento\Framework\DataObject; /** + * Produce html output using the given data source. + * + * phpcs:disable Magento2.Classes.AbstractApi * Backend grid item abstract renderer * @api * @SuppressWarnings(PHPMD.NumberOfChildren) @@ -53,7 +56,7 @@ public function getColumn() * Renders grid column * * @param DataObject $row - * @return string + * @return string */ public function render(DataObject $row) { @@ -62,7 +65,7 @@ public function render(DataObject $row) $result .= $this->getColumn()->getEditOnly() ? '' : '' . $this->_getValue($row) . ''; - return $result . $this->_getInputValueElement($row) . '' ; + return $result . $this->_getInputValueElement($row) . ''; } return $this->_getValue($row); } @@ -90,6 +93,7 @@ protected function _getValue(DataObject $row) if (is_string($getter)) { return $row->{$getter}(); } elseif (is_callable($getter)) { + //phpcs:ignore Magento2.Functions.DiscouragedFunction return call_user_func($getter, $row); } return ''; diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml index 7068478bb4790..8f61a0a06dd5e 100644 --- a/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/LoginActionGroup.xml @@ -10,7 +10,8 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Login to Backend Admin using ENV Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In. + DEPRECATED. Please use LoginAsAdmin instead. + Login to Backend Admin using ENV Admin credentials. PLEASE NOTE: This Action Group does NOT validate that you are Logged In. diff --git a/app/code/Magento/Backend/etc/adminhtml/system.xml b/app/code/Magento/Backend/etc/adminhtml/system.xml index 3a2b3554cc4a0..1e8ec226d6b88 100644 --- a/app/code/Magento/Backend/etc/adminhtml/system.xml +++ b/app/code/Magento/Backend/etc/adminhtml/system.xml @@ -115,7 +115,7 @@ Magento\Config\Model\Config\Source\Yesno - + 1 @@ -123,7 +123,7 @@ Magento\Config\Model\Config\Source\Yesno Use URL parameter to enable template path hints for Storefront - + 1 diff --git a/app/code/Magento/Backend/i18n/en_US.csv b/app/code/Magento/Backend/i18n/en_US.csv index 51fe8bfe542a2..53f7fe90cbbe5 100644 --- a/app/code/Magento/Backend/i18n/en_US.csv +++ b/app/code/Magento/Backend/i18n/en_US.csv @@ -461,3 +461,5 @@ Pagination,Pagination "Alternative text for the next pages link in the pagination menu. If empty, default arrow image is used.","Alternative text for the next pages link in the pagination menu. If empty, default arrow image is used." "Anchor Text for Next","Anchor Text for Next" "Theme Name","Theme Name" +"Use URL parameter to enable template path hints for Storefront","Use URL parameter to enable template path hints for Storefront" +"Add the following parameter to the URL to show template hints ?templatehints=[parameter_value]","Add the following parameter to the URL to show template hints ?templatehints=[parameter_value]" diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js index 43aec27508ce9..b66725c063414 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js @@ -117,7 +117,7 @@ define([ options.bin = context.paymentPayload.details.bin; } - if (shippingAddress) { + if (shippingAddress && this.isValidShippingAddress(shippingAddress)) { options.additionalInformation = { shippingGivenName: shippingAddress.firstname, shippingSurname: shippingAddress.lastname, @@ -206,6 +206,25 @@ define([ } return false; + }, + + /** + * Validate shipping address + * + * @param {Object} shippingAddress + * @return {Boolean} + */ + isValidShippingAddress: function (shippingAddress) { + var isValid = false; + + // check that required fields are not empty + if (shippingAddress.firstname && shippingAddress.lastname && shippingAddress.telephone && + shippingAddress.street && shippingAddress.city && shippingAddress.regionCode && + shippingAddress.postcode && shippingAddress.countryId) { + isValid = true; + } + + return isValid; } }; }); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index afe22475981ec..21809f186d252 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -91,7 +91,7 @@ define( }) .then(function (hostedFieldsInstance) { self.hostedFieldsInstance = hostedFieldsInstance; - self.isPlaceOrderActionAllowed(true); + self.isPlaceOrderActionAllowed(false); self.initFormValidationEvents(hostedFieldsInstance); return self.hostedFieldsInstance; diff --git a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php index 7608d6e9e4d97..c8e2384fcb99c 100644 --- a/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php +++ b/app/code/Magento/BundleGraphQl/Model/Resolver/Options/Collection.php @@ -78,11 +78,7 @@ public function addParentFilterData(int $parentId, int $parentEntityId, string $ public function getOptionsByParentId(int $parentId) : array { $options = $this->fetch(); - if (!isset($options[$parentId])) { - return []; - } - - return $options[$parentId]; + return $options[$parentId] ?? []; } /** @@ -115,7 +111,7 @@ private function fetch() : array $this->extensionAttributesJoinProcessor->process($optionsCollection); if (empty($optionsCollection->getData())) { - return null; + return []; } /** @var \Magento\Bundle\Model\Option $option */ diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForBackendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForBackendObserverTest.php new file mode 100644 index 0000000000000..b984daa8998f3 --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForBackendObserverTest.php @@ -0,0 +1,53 @@ +createMock(Log::class); + $logMock->expects($this->once())->method('deleteUserAttempts')->willReturnSelf(); + + $resLogFactoryMock = $this->createMock(LogFactory::class); + $resLogFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($logMock); + + /** @var MockObject|Observer $eventObserverMock */ + $eventObserverMock = $this->createPartialMock(Observer::class, ['getUser']); + $eventMock = $this->createMock(Event::class); + $eventObserverMock->expects($this->once()) + ->method('getUser') + ->willReturn($eventMock); + + $objectManager = new ObjectManagerHelper($this); + /** @var ResetAttemptForBackendObserver $observer */ + $observer = $objectManager->getObject( + ResetAttemptForBackendObserver::class, + ['resLogFactory' => $resLogFactoryMock] + ); + $this->assertInstanceOf(Log::class, $observer->execute($eventObserverMock)); + } +} diff --git a/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForFrontendObserverTest.php b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForFrontendObserverTest.php new file mode 100644 index 0000000000000..11866c266845e --- /dev/null +++ b/app/code/Magento/Captcha/Test/Unit/Observer/ResetAttemptForFrontendObserverTest.php @@ -0,0 +1,52 @@ +createMock(Log::class); + $logMock->expects($this->once())->method('deleteUserAttempts')->willReturnSelf(); + + $resLogFactoryMock = $this->createMock(LogFactory::class); + $resLogFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($logMock); + + /** @var MockObject|Observer $eventObserverMock */ + $eventObserverMock = $this->createPartialMock(Observer::class, ['getModel']); + $eventObserverMock->expects($this->once()) + ->method('getModel') + ->willReturn($this->createMock(Customer::class)); + + $objectManager = new ObjectManagerHelper($this); + /** @var ResetAttemptForFrontendObserver $observer */ + $observer = $objectManager->getObject( + ResetAttemptForFrontendObserver::class, + ['resLogFactory' => $resLogFactoryMock] + ); + $this->assertInstanceOf(Log::class, $observer->execute($eventObserverMock)); + } +} diff --git a/app/code/Magento/Catalog/Api/CategoryListDeleteBySkuInterface.php b/app/code/Magento/Catalog/Api/CategoryListDeleteBySkuInterface.php new file mode 100644 index 0000000000000..62eba5987c35d --- /dev/null +++ b/app/code/Magento/Catalog/Api/CategoryListDeleteBySkuInterface.php @@ -0,0 +1,27 @@ +categoryRepository = $categoryRepository; $this->productRepository = $productRepository; + $this->productResource = $productResource ?? ObjectManager::getInstance()->get(Product::class); } /** - * {@inheritdoc} + * @inheritdoc */ public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink) { @@ -60,7 +77,7 @@ public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $pro } /** - * {@inheritdoc} + * @inheritdoc */ public function delete(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink) { @@ -68,7 +85,7 @@ public function delete(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $p } /** - * {@inheritdoc} + * @inheritdoc */ public function deleteByIds($categoryId, $sku) { @@ -101,4 +118,44 @@ public function deleteByIds($categoryId, $sku) } return true; } + + /** + * @inheritdoc + */ + public function deleteBySkus(int $categoryId, array $productSkuList): bool + { + $category = $this->categoryRepository->get($categoryId); + $products = $this->productResource->getProductsIdsBySkus($productSkuList); + + if (!$products) { + throw new InputException(__("The category doesn't contain the specified products.")); + } + + $productPositions = $category->getProductsPosition(); + + foreach ($products as $productId) { + if (isset($productPositions[$productId])) { + unset($productPositions[$productId]); + } + } + + $category->setPostedProducts($productPositions); + + try { + $category->save(); + } catch (\Exception $e) { + throw new CouldNotSaveException( + __( + 'Could not save products "%products" to category %category', + [ + "products" => implode(',', $productSkuList), + "category" => $category->getId() + ] + ), + $e + ); + } + + return true; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index e31180d4ff6cf..afbe279045a38 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -2121,7 +2121,9 @@ private function getChildrenCategories(int $categoryId): array if (in_array($category['parent_id'], $categoryIds) && in_array($category['parent_id'], $anchorCategory)) { $categoryIds[] = (int)$category[$linkField]; - if ($category['is_anchor'] == 1) { + // Storefront approach is to treat non-anchor children of anchor category as anchors. + // Adding their's IDs to $anchorCategory for consistency. + if ($category['is_anchor'] == 1 || in_array($category['parent_id'], $anchorCategory)) { $anchorCategory[] = (int)$category[$linkField]; } } diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryPageOpenProductsInCategorySectionActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryPageOpenProductsInCategorySectionActionGroup.xml new file mode 100644 index 0000000000000..67c68e03fcfa1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminCategoryPageOpenProductsInCategorySectionActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + Open 'Products in Category' section on category edit page in Admin. + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductAttributeByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductAttributeByLabelActionGroup.xml new file mode 100644 index 0000000000000..7b453735994d7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminDeleteProductAttributeByLabelActionGroup.xml @@ -0,0 +1,31 @@ + + + + + + + Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present. + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormAdvancedPricingAddTierPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormAdvancedPricingAddTierPriceActionGroup.xml new file mode 100644 index 0000000000000..f823db0a86548 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormAdvancedPricingAddTierPriceActionGroup.xml @@ -0,0 +1,34 @@ + + + + + + + Add new tier price on Advanced Pricing dialog on the Admin Product creation/edit page. + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCloseAdvancedPricingDialogActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCloseAdvancedPricingDialogActionGroup.xml new file mode 100644 index 0000000000000..03c98c1cb17b7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCloseAdvancedPricingDialogActionGroup.xml @@ -0,0 +1,19 @@ + + + + + + + Close Advanced Pricing dialog from product form. + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormDoneAdvancedPricingDialogActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormDoneAdvancedPricingDialogActionGroup.xml new file mode 100644 index 0000000000000..10f2d32799200 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormDoneAdvancedPricingDialogActionGroup.xml @@ -0,0 +1,19 @@ + + + + + + + Done Advanced Pricing dialog from product form. + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormOpenAdvancedPricingDialogActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormOpenAdvancedPricingDialogActionGroup.xml new file mode 100644 index 0000000000000..1c96ce3469485 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormOpenAdvancedPricingDialogActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + Open Advanced Pricing dialog from product form. + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml new file mode 100644 index 0000000000000..564033f459dc9 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSaveCategoryFormActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Save category edit form in Admin and check success message. + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup.xml new file mode 100644 index 0000000000000..42aee90882400 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup.xml @@ -0,0 +1,32 @@ + + + + + + + Check AdvancedPricing tier price row. + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategorySimpleProductShownActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategorySimpleProductShownActionGroup.xml new file mode 100644 index 0000000000000..a2940708eeb84 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategorySimpleProductShownActionGroup.xml @@ -0,0 +1,26 @@ + + + + + + + + Validate that the provided Simple Product is present and correct on a Category page. + + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup.xml new file mode 100644 index 0000000000000..226bb8468d62e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup.xml @@ -0,0 +1,19 @@ + + + + + + + Validate that the Product Controls Are Not Visible On Category Page Without Hover on Product + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreVisibleOnHoverActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreVisibleOnHoverActionGroup.xml new file mode 100644 index 0000000000000..60d56df04bd2f --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductControlsAreVisibleOnHoverActionGroup.xml @@ -0,0 +1,19 @@ + + + + + + + Validate that the Product Controls Are Visible on Category Page on Hover on Product + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductPriceInCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductPriceInCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..87f6e6e705263 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductPriceInCategoryPageActionGroup.xml @@ -0,0 +1,25 @@ + + + + + + + Goes to Storefront Category page for the provided Category. Validates that the Product price is present and correct. + + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductSpecialPriceInCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductSpecialPriceInCategoryPageActionGroup.xml new file mode 100644 index 0000000000000..b2a7d5f96ec94 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontProductSpecialPriceInCategoryPageActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + Goes to Storefront Category page for the provided Category. Validates that the Product price and special price are correct. + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml index fb78909eab0b6..15c3d55fb9382 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/DeleteProductAttributeByLabelActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present. + DEPRECATED. Please use AdminDeleteProductAttributeByLabelActionGroup instead. Goes to the Admin Product Attributes grid page. Filters the grid for the provided Product Attribute (Label). Deletes the Product Attribute from the grid. Validates that the Success Message is present. diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml index 95bda64202159..0f7145b607f6e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ProductSetAdvancedPricingActionGroup.xml @@ -21,7 +21,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml index ff6afb4aaf0e9..19f11a2402f56 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SaveCategoryFormActionGroup.xml @@ -10,11 +10,11 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Requires navigation to the Category creation/edit page. Checks that the url contains the AdminCategoryPage url. Saves the Category. + DEPRECATED. Use AdminSaveCategoryFormActionGroup instead. Requires navigation to the Category creation/edit page. Checks that the url contains the AdminCategoryPage url. Saves the Category. - - + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml index d4c654523a40b..1882063081f04 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/SetProductUrlKeyByStringActionGroup.xml @@ -16,7 +16,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml index 39cb9ef1a63d4..dab5142c557ca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryPageActionGroup.xml @@ -13,5 +13,6 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/AdminProductAttributeMessageData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/AdminProductAttributeMessageData.xml new file mode 100644 index 0000000000000..834abdad497b8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/Data/AdminProductAttributeMessageData.xml @@ -0,0 +1,14 @@ + + + + + + You deleted the product attribute. + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 46bb6e527608f..bc04b3b1dcd0b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -756,6 +756,7 @@ In Stock Catalog, Search virtual-product + IN STOCK virtual diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml index d89d9a5bd43c0..1627fb9512e64 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategoryProductsSection.xml @@ -9,10 +9,7 @@
- - - - - + +
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml index 77b89a07fb76a..91ac52a91a4c4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormAdvancedPricingSection.xml @@ -25,5 +25,13 @@ + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml index 9a84f90edcfc0..6b67f5609f7f0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryMainSection.xml @@ -17,6 +17,9 @@ + + + @@ -27,6 +30,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml index 4114d64eb39af..61e6a345b9ba5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryProductSection.xml @@ -21,7 +21,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index a5a02ad95b1f7..0b44d9bb0ea6b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -105,5 +105,7 @@ + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml index 41b358bbf760e..bcb8af7209d8f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAssignProductAttributeToAttributeSetTest.xml @@ -33,6 +33,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml index 95620bf75b6d0..cdb9a0a8b75d0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminChangeProductAttributeSet.xml @@ -47,6 +47,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml index b4381a674827d..6a3ff738f1846 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckConfigurableProductPriceWithDisabledChildProductTest.xml @@ -118,6 +118,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml index 7c6f6ab66f63d..4aa9474aba6fe 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCheckCustomAttributeValuesAfterProductSaveTest.xml @@ -43,6 +43,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml index 51518dffaf87e..8f06565c147fa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateAttributeSetEntityTest.xml @@ -26,6 +26,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 557f0768c98a3..00e0758f6e70b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateDropdownProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -34,7 +34,12 @@ + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml index 6bfd012bc88b2..56a6a869d8d35 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateMultipleSelectProductAttributeVisibleInStorefrontAdvancedSearchFormTest.xml @@ -41,6 +41,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml index a105f343d3e21..12d4f825c3764 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateNewGroupForAttributeSetTest.xml @@ -27,7 +27,11 @@ + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 4c3d519106389..b2ddaac65d070 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -10,6 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <description value="Test log in to Create virtual product and Create virtual product with tier price for General group"/> @@ -17,122 +18,96 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-30682"/> - </skip> </annotations> <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> <createData entity="Simple_US_CA_Customer" stepKey="customer" /> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> <after> + <deleteData createDataKey="categoryEntity" stepKey="deleteSimpleSubCategory"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="virtualProductGeneralGroup"/> </actionGroup> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="categoryEntity"/> - <deleteData stepKey="deleteCustomer" createDataKey="customer"/> - <actionGroup ref="logout" stepKey="logout"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppears"/> + <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/> + <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearFiltersAfter"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage"/> - <waitForPageLoad stepKey="waitForProductCatalogPage"/> - <click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductToggle"/> - <waitForPageLoad stepKey="waitForProductToggleToSelectProduct"/> - <click selector="{{AdminProductGridActionSection.addVirtualProduct}}" stepKey="clickVirtualProduct"/> - - <!-- Create virtual product with tier price for general group --> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{virtualProductGeneralGroup.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{virtualProductGeneralGroup.price}}" stepKey="fillProductPrice"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickCustomerGroupPriceAddButton"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.website}}" stepKey="selectProductTierPriceWebsite"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.customer_group}}" stepKey="selectProductTierPriceGroup"/> - <scrollTo selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" x="50" y="0" stepKey="scrollToProductTierPriceQuantityInputTextBox"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnGeneralGroup.qty}}" stepKey="fillProductTierPriceQuantityInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnGeneralGroup.price}}" stepKey="fillProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <!-- Create virtual product--> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId,'virtual')}}" stepKey="openVirtualProductCreatePage"/> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillNewProductData"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialog"/> + <actionGroup ref="AdminProductFormAdvancedPricingAddTierPriceActionGroup" stepKey="addTierPrice"> + <argument name="website" value="{{tierPriceOnGeneralGroup.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnGeneralGroup.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnGeneralGroup.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnGeneralGroup.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormDoneAdvancedPricingDialogActionGroup" stepKey="doneAdvancedPricingModal"/> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{virtualProductGeneralGroup.productTaxClass}}" stepKey="selectProductTaxClass"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{virtualProductGeneralGroup.quantity}}" stepKey="fillProductQuantity"/> - <click selector="{{AdminProductFormSection.productStockStatus}}" stepKey="clickProductStockStatus"/> - <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> - <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> - <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="setNewCategory"> + <argument name="categoryName" value="$categoryEntity.name$"/> + </actionGroup> <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{virtualProductGeneralGroup.visibility}}" stepKey="selectVisibility"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSectionHeader"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{virtualProductGeneralGroup.urlKey}}" stepKey="fillUrlKeyInput"/> - <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForVirtualProductSaved"/> - - <!-- Verify we see success message --> - <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertVirtualProductSuccessMessage"/> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="updateUrlKey"> + <argument name="urlKey" value="{{virtualProductGeneralGroup.urlKey}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductAndCheckSuccessMessage"/> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage1"/> - <waitForPageLoad stepKey="waitForProductCatalogPage1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="checkRetailCustomerTaxClass" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="fillVirtualProductName"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyCreatedVirtualProduct"/> - <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <!-- Search created virtual product(from above steps) in the grid --> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForVirtualProduct"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> - <!-- Verify we see created virtual product with tier price for general group(from the above step) in the product form page --> - <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="seeProductName"/> - <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{virtualProductGeneralGroup.sku}}" stepKey="seeProductSku"/> - <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{virtualProductGeneralGroup.price}}" stepKey="seeProductPrice"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> - <seeOptionIsSelected selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.website}}" stepKey="seeProductTierPriceWebsite"/> - <seeOptionIsSelected selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnGeneralGroup.customer_group}}" stepKey="seeProductTierPriceGroup"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnGeneralGroup.qty}}" stepKey="seeProductTierPriceQuantityInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnGeneralGroup.price}}" stepKey="seeProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickAdvancedPricingCloseButton"/> + <!-- Verify customer see created virtual product with tier price in the product form page --> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="verifyProductInAdminEditForm"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialogAgain"/> + <actionGroup ref="AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup" stepKey="checkTierPrice"> + <argument name="rowNumber" value="0"/> + <argument name="website" value="{{tierPriceOnGeneralGroup.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnGeneralGroup.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnGeneralGroup.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnGeneralGroup.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormCloseAdvancedPricingDialogActionGroup" stepKey="closeAdvancedPricingModal"/> <seeInField selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{virtualProductGeneralGroup.productTaxClass}}" stepKey="seeProductTaxClass"/> - <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{virtualProductGeneralGroup.quantity}}" stepKey="seeProductQuantity"/> - <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{virtualProductGeneralGroup.status}}" stepKey="seeProductStockStatus"/> <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> - <grabMultiple selector="{{AdminProductFormSection.selectMultipleCategories}}" stepKey="selectedCategories" /> + <grabMultiple selector="{{AdminProductFormSection.selectMultipleCategories}}" stepKey="selectedCategories"/> <assertEquals stepKey="assertSelectedCategories"> <actualResult type="variable">selectedCategories</actualResult> - <expectedResult type="array">[$$categoryEntity.name$$]</expectedResult> + <expectedResult type="array">[$categoryEntity.name$]</expectedResult> </assertEquals> <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneOnCategorySelect"/> <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{virtualProductGeneralGroup.visibility}}" stepKey="seeVisibility"/> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.useDefaultUrl}}" visible="false" stepKey="openSearchEngineOptimizationSection"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToAdminProductSEOSection"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{virtualProductGeneralGroup.urlKey}}" stepKey="seeUrlKey"/> - <!--Verify customer see created virtual product on category page --> - <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageToLoad"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="seeVirtualProductNameOnCategoryPage"/> + <!--Verify customer see created virtual product link on category page --> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPageOnFrontend"> + <argument name="category" value="$categoryEntity$"/> + </actionGroup> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> - <!-- Verify customer see created virtual product with tier price for general group(from above step) in storefront page with customer --> + <!--Verify customer see updated virtual product with tier price on product storefront page --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> - <argument name="Customer" value="$$customer$$" /> + <argument name="Customer" value="$customer$" /> </actionGroup> - <amOnPage url="{{StorefrontHomePage.url}}" stepKey="goToStorefront"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="fillVirtualProductNameInSearchTextBox"/> - <waitForPageLoad stepKey="waitForSearchTextBox"/> - <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> - <waitForPageLoad stepKey="waitForSearch"/> - <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{virtualProductGeneralGroup.name}}" stepKey="seeVirtualProductName"/> - <grabTextFrom selector="{{StorefrontQuickSearchResultsSection.asLowAsLabel}}" stepKey="tierPriceTextOnStorefrontPage"/> - - <!-- Verify customer see created virtual product with tier price --> - <assertEquals stepKey="assertTierPriceTextOnCategoryPage"> - <expectedResult type="string">As low as ${{tierPriceOnGeneralGroup.price}}</expectedResult> - <actualResult type="variable">tierPriceTextOnStorefrontPage</actualResult> - </assertEquals> - <click selector="{{StorefrontQuickSearchResultsSection.productLink}}" stepKey="openSearchedProduct"/> - <waitForPageLoad stepKey="waitForProductPageToBeLoaded"/> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.tierPriceText}}" stepKey="tierPriceText"/> - <assertEquals stepKey="assertTierPriceTextOnProductPage"> - <expectedResult type="string">Buy {{tierPriceOnGeneralGroup.qty}} for ${{tierPriceOnGeneralGroup.price}} each and save 20%</expectedResult> - <actualResult type="variable">tierPriceText</actualResult> - </assertEquals> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="verifyProductOnFrontend"> + <argument name="product" value="virtualProductGeneralGroup"/> + </actionGroup> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="{{virtualProductGeneralGroup.storefrontStatus}}" stepKey="assertStockAvailableOnProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.tierPriceText}}" userInput="Buy {{tierPriceOnGeneralGroup.qty}} for ${{tierPriceOnGeneralGroup.price}} each and save 20%" stepKey="assertTierPriceTextOnProductPage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml index 4a305b8dfec75..04a4eff0a26d4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteConfigurableChildProductsTest.xml @@ -82,6 +82,9 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Open Product in Store Front Page --> <amOnPage url="$$createConfigProduct.sku$$.html" stepKey="openProductInStoreFront"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml index 3abe68a503b57..58e60da3bdac5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteDropdownProductAttributeFromAttributeSetTest.xml @@ -29,6 +29,9 @@ <!--Delete Created Data --> <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product Attribute Set Page --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml index 4060182a9bace..d72806cb0991d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteProductAttributeTest.xml @@ -23,6 +23,9 @@ </before> <after> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> <argument name="productAttributeCode" value="$$createProductAttribute.attribute_code$$"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml index 4f05c364fda0e..e12bac55d8bc8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminDeleteTextFieldProductAttributeFromAttributeSetTest.xml @@ -32,6 +32,9 @@ <!--Delete cteated Data --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimplaeProduct"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product Attribute Set Page --> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="goToAttributeSets"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index f178d55b97fca..f01becd2034d8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -14,11 +14,9 @@ <title value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> <description value="Admin should be able to move a category via categories tree and changes should be applied on frontend without a forced cache cleaning"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-76273"/> + <testCaseId value="MC-10022"/> + <useCaseId value="MAGETWO-89248"/> <group value="category"/> - <skip> - <issueId value="MC-30720"/> - </skip> </annotations> <before> <createData entity="SimpleSubCategory" stepKey="simpleSubCategoryOne"/> @@ -32,95 +30,102 @@ <createData entity="_defaultProduct" stepKey="productTwo"> <requiredEntity createDataKey="simpleSubCategoryOne"/> </createData> - <magentoCLI command="cron:run --group=index" stepKey="runIndexerCron"/> + + <!-- TODO: Replace this with CliRunReindexUsingCronJobsActionGroup after MC-29943 delivered--> + <magentoCLI command="cron:run" arguments="--group='index'" stepKey="firstRunToScheduleJobs"/> + <magentoCLI command="cron:run" arguments="--group='index'" stepKey="secondRunToExecuteJobs"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> + <after> - <actionGroup ref="logout" stepKey="logoutAdminUserAfterTest"/> <deleteData createDataKey="productOne" stepKey="deleteProductOne"/> <deleteData createDataKey="productTwo" stepKey="deleteProductTwo"/> <deleteData createDataKey="simpleSubCategoryWithParent" stepKey="deleteSubcategoryWithParent"/> + <deleteData createDataKey="simpleSubCategoryOne" stepKey="deleteSubcategoryOne"/> <deleteData createDataKey="simpleSubCategoryTwo" stepKey="deleteSubcategoryTwo"/> + <actionGroup ref="logout" stepKey="logoutAdminUserAfterTest"/> </after> + <!--Move category one to category two--> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage"/> <waitForPageLoad stepKey="waitForAdminCategoryPageLoad1"/> <actionGroup ref="MoveCategoryActionGroup" stepKey="moveSimpleSubCategoryOneToSimpleSubCategoryTwo"> - <argument name="childCategory" value="$$simpleSubCategoryOne.name$$"/> - <argument name="parentCategory" value="$$simpleSubCategoryTwo.name$$"/> + <argument name="childCategory" value="$simpleSubCategoryOne.name$"/> + <argument name="parentCategory" value="$simpleSubCategoryTwo.name$"/> </actionGroup> <!--Verify that navigation menu categories level is correct--> - <amOnPage url="/" stepKey="amOnStorefrontPage1"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage1"/> <waitForPageLoad stepKey="waitForPageToLoadAfterHomePageOpened1"/> - <seeElement selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryTwo.name$$)}}" stepKey="verifyThatTopCategoryIsSubCategoryTwo"/> - <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryTwo.name$$)}}" stepKey="mouseOverSubCategoryTwo"/> + <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="verifyThatTopCategoryIsSubCategoryTwo"/> + <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="mouseOverSubCategoryTwo"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverSubCategoryTwo"/> - <seeElement selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryOne.name$$)}}" stepKey="verifyThatFirstLevelIsSubCategoryOne"/> - <moveMouseOver selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryOne.name$$)}}" stepKey="mouseOverSubCategoryOne"/> + <seeElement selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryOne.name$)}}" stepKey="verifyThatFirstLevelIsSubCategoryOne"/> + <moveMouseOver selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryOne.name$)}}" stepKey="mouseOverSubCategoryOne"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverSubCategoryOne"/> - <seeElement selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryWithParent.name$$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent1"/> + <seeElement selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryWithParent.name$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent1"/> <!--Open category one via navigation menu. Verify that subcategory is shown in layered navigation--> - <click selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryOne.name$$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu1"/> + <click selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryOne.name$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu1"/> <actionGroup ref="CheckItemInLayeredNavigationActionGroup" stepKey="verifySimpleSubCategoryWithParentInLayeredNavigation1"> <argument name="itemType" value="Category"/> - <argument name="itemName" value="$$simpleSubCategoryWithParent.name$$"/> + <argument name="itemName" value="$simpleSubCategoryWithParent.name$"/> </actionGroup> <!--Open category one by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryOneCategoryPage1"> - <argument name="category" value="$$simpleSubCategoryOne$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryOne$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs1"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryTwo.name$$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryOne"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne1"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryTwo.name$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryOne"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne1"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne1"/> <!--Open category two by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryWithParentCategoryPage"> - <argument name="category" value="$$simpleSubCategoryWithParent$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryWithParent$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryTwo.name$$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryTwo.name$" stepKey="seeSubCategoryTwoInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryWithParent"/> <!--Move category one to the same level as category two--> <amOnPage url="{{AdminCategoryPage.url}}" stepKey="navigateToAdminCategoryPage2"/> <waitForPageLoad stepKey="waitForAdminCategoryPageLoad2"/> <actionGroup ref="MoveCategoryActionGroup" stepKey="moveSimpleSubCategoryOneToDefaultCategory"> - <argument name="childCategory" value="$$simpleSubCategoryOne.name$$"/> + <argument name="childCategory" value="$simpleSubCategoryOne.name$"/> <argument name="parentCategory" value="Default Category"/> </actionGroup> <!--Verify that navigation menu categories level is correct--> - <amOnPage url="/" stepKey="amOnStorefrontPage2"/> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage2"/> <waitForPageLoad stepKey="waitForPageToLoadAfterHomePageOpened2"/> - <seeElement selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryOne.name$$)}}" stepKey="verifyThatSubCategoryOneIsTopCategory"/> - <seeElement selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryTwo.name$$)}}" stepKey="verifyThatSubCategoryTwoIsTopCategory"/> - <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryOne.name$$)}}" stepKey="mouseOverTopSubCategoryOne"/> + <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="verifyThatSubCategoryOneIsTopCategory"/> + <seeElement selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryTwo.name$)}}" stepKey="verifyThatSubCategoryTwoIsTopCategory"/> + <moveMouseOver selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="mouseOverTopSubCategoryOne"/> <waitForAjaxLoad stepKey="waitForAjaxOnMouseOverTopSubCategoryOne"/> - <seeElement selector="{{StorefrontNavigationSection.subCategory($$simpleSubCategoryWithParent.name$$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent2"/> + <seeElement selector="{{StorefrontNavigationSection.subCategory($simpleSubCategoryWithParent.name$)}}" stepKey="verifyThatSecondLevelIsSubCategoryWithParent2"/> <!--Open category one via navigation menu. Verify that subcategory is shown in layered navigation--> - <click selector="{{StorefrontNavigationSection.topCategory($$simpleSubCategoryOne.name$$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu2"/> + <click selector="{{StorefrontNavigationSection.topCategory($simpleSubCategoryOne.name$)}}" stepKey="openSimpleSubCategoryOneByNavigationMenu2"/> <actionGroup ref="CheckItemInLayeredNavigationActionGroup" stepKey="verifySimpleSubCategoryWithParentInLayeredNavigation2"> <argument name="itemType" value="Category"/> - <argument name="itemName" value="$$simpleSubCategoryWithParent.name$$"/> + <argument name="itemName" value="$simpleSubCategoryWithParent.name$"/> </actionGroup> <!--Open category one by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryOneCategoryPage2"> - <argument name="category" value="$$simpleSubCategoryOne$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryOne$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne2"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne2"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne2"/> <!--Open category subcategory by direct URL. Verify simple product is visible on it. Open this product and perform assertions--> <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openFirstProductFromSubCategoryOneCategoryPage3"> - <argument name="category" value="$$simpleSubCategoryWithParent$$"/> - <argument name="product" value="$$productOne$$"/> + <argument name="category" value="$simpleSubCategoryWithParent$"/> + <argument name="product" value="$productOne$"/> </actionGroup> <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="Home" stepKey="seeHomePageInBreadcrumbs3"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne3"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$simpleSubCategoryOne.name$$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent3"/> - <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$$productOne.name$$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne3"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryOneInBreadcrumbsOnSubCategoryOne3"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$simpleSubCategoryOne.name$" stepKey="seeSubCategoryWithParentInBreadcrumbsOnSubCategoryWithParent3"/> + <see selector="{{StorefrontNavigationSection.breadcrumbs}}" userInput="$productOne.name$" stepKey="seeProductInBreadcrumbsOnSubCategoryOne3"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml index 5dd8b2e430941..7bfe3ae50c58b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminNavigateMultipleUpSellProductsTest.xml @@ -90,6 +90,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deletecreateConfigChildProduct2"/> <deleteData createDataKey="createConfigChildProduct1" stepKey="deletecreateConfigChildProduct1"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Open Product Index Page--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml index 6f65865924bad..a4986117380ff 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminProductGridFilteringByCustomAttributeTest.xml @@ -90,6 +90,9 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="NavigateToAndResetProductGridToDefaultViewActionGroup" stepKey="NavigateToAndResetProductGridToDefaultViewAfterTest"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/> <waitForPageLoad stepKey="waitForProductGridPageLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml index 4bcb82372e801..7f62dd14a4f32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminTierPriceNotAvailableForProductOptionsWithoutTierPriceTest.xml @@ -83,6 +83,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Go to storefront product page an check price box css--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml index 44b4e60973907..c508e3ae94d67 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUnassignProductAttributeFromAttributeSetTest.xml @@ -37,6 +37,9 @@ <deleteData createDataKey="attribute" stepKey="deleteAttribute"/> <magentoCLI command="cron:run --group=index" stepKey="runCron"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Assert attribute presence in storefront product additional information --> <amOnPage url="/$$product.custom_attributes[url_key]$$.html" stepKey="onProductPage1"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index 04110dbd73a4c..ea331bfc97db2 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -8,7 +8,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="UpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest"> + <test name="AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest"> <annotations> <stories value="Update Virtual Product"/> <title value="Update Virtual Product with Tier Price (In Stock) Visible in Category and Search"/> @@ -17,147 +17,117 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> - <skip> - <issueId value="MC-30166"/> - </skip> </annotations> <before> - <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> <createData entity="SimpleSubCategory" stepKey="initialCategoryEntity"/> <createData entity="defaultVirtualProduct" stepKey="initialVirtualProduct"> <requiredEntity createDataKey="initialCategoryEntity"/> </createData> <createData entity="SimpleSubCategory" stepKey="categoryEntity"/> + <actionGroup ref = "LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- TODO: Replace this with CliRunReindexUsingCronJobsActionGroup after MC-29943 delivered--> + <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext catalog_category_product" stepKey="reindexIndices"/> </before> + <after> + <deleteData createDataKey="initialCategoryEntity" stepKey="deleteInitialCategory"/> + <deleteData createDataKey="categoryEntity" stepKey="deleteCategory" /> <actionGroup ref="DeleteProductUsingProductGridActionGroup" stepKey="deleteVirtualProduct"> <argument name="product" value="updateVirtualProductTierPriceInStock"/> </actionGroup> - <deleteData stepKey="deleteSimpleSubCategory" createDataKey="initialCategoryEntity"/> - <deleteData stepKey="deleteSimpleSubCategory2" createDataKey="categoryEntity"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> - <!-- Search default virtual product in the grid --> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPage1"/> - <waitForPageLoad stepKey="waitForProductCatalogPage1"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAllFilter" /> - <fillField selector="{{AdminProductGridFilterSection.keywordSearch}}" userInput="$$initialVirtualProduct.name$$" stepKey="fillVirtualProductNameInKeywordSearch"/> - <click selector="{{AdminProductGridFilterSection.keywordSearchButton}}" stepKey="clickKeywordSearchButton"/> - <waitForPageLoad stepKey="waitForProductSearch"/> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyCreatedVirtualProduct"/> - <waitForPageLoad stepKey="waitUntilProductIsOpened"/> + <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="openProductEditPageBySKU"> + <argument name="productSku" value="$initialVirtualProduct.sku$"/> + </actionGroup> <!-- Update virtual product with tier price --> - <fillField selector="{{AdminProductFormSection.productName}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="fillProductName"/> - <fillField selector="{{AdminProductFormSection.productSku}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="fillProductSku"/> - <fillField selector="{{AdminProductFormSection.productPrice}}" userInput="{{updateVirtualProductTierPriceInStock.price}}" stepKey="fillProductPrice"/> + <actionGroup ref="FillMainProductFormNoWeightActionGroup" stepKey="fillNewProductData"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> <!-- Press enter to validate advanced pricing link --> <pressKey selector="{{AdminProductFormSection.productPrice}}" parameterArray="[\Facebook\WebDriver\WebDriverKeys::ENTER]" stepKey="pressEnterKey"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> - <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickCustomerGroupPriceAddButton"/> - <scrollTo selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" x="50" y="0" stepKey="scrollToProductTierPriceQuantityInputTextBox"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.website}}" stepKey="selectProductTierPriceWebsiteInput"/> - <selectOption selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.customer_group}}" stepKey="selectProductTierPriceCustomerGroupInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnVirtualProduct.qty}}" stepKey="fillProductTierPriceQuantityInput"/> - <fillField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnVirtualProduct.price}}" stepKey="selectProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDoneButton"/> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialog"/> + <actionGroup ref="AdminProductFormAdvancedPricingAddTierPriceActionGroup" stepKey="addTierPrice"> + <argument name="website" value="{{tierPriceOnVirtualProduct.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnVirtualProduct.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnVirtualProduct.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnVirtualProduct.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormDoneAdvancedPricingDialogActionGroup" stepKey="doneAdvancedPricingModal"/> <selectOption selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{updateVirtualProductTierPriceInStock.productTaxClass}}" stepKey="selectProductStockClass"/> - <fillField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{updateVirtualProductTierPriceInStock.quantity}}" stepKey="fillProductQuantity"/> - <selectOption selector="{{AdminProductFormSection.stockStatus}}" userInput="{{updateVirtualProductTierPriceInStock.status}}" stepKey="selectStockStatusInStock"/> - <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$initialCategoryEntity.name$$" stepKey="fillSearchForInitialCategory" /> - <waitForPageLoad stepKey="waitForCategory1"/> - <click selector="{{AdminProductFormSection.selectCategory($$initialCategoryEntity.name$$)}}" stepKey="unselectInitialCategory"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$categoryEntity.name$$" stepKey="fillSearchCategory" /> - <waitForPageLoad stepKey="waitForCategory2"/> - <click selector="{{AdminProductFormSection.selectCategory($$categoryEntity.name$$)}}" stepKey="clickOnCategory"/> - <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> + <actionGroup ref="RemoveCategoryFromProductActionGroup" stepKey="unselectInitialCategory"> + <argument name="categoryName" value="$initialCategoryEntity.name$"/> + </actionGroup> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="setNewCategory"> + <argument name="categoryName" value="$categoryEntity.name$"/> + </actionGroup> <selectOption selector="{{AdminProductFormSection.visibility}}" userInput="{{updateVirtualProductTierPriceInStock.visibility}}" stepKey="selectVisibility"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection"/> - <fillField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{updateVirtualProductTierPriceInStock.urlKey}}" stepKey="fillUrlKey"/> - <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForVirtualProductSaved"/> - <!-- Verify we see success message --> - <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertVirtualProductSaveSuccessMessage"/> + <actionGroup ref="SetProductUrlKeyByStringActionGroup" stepKey="updateUrlKey"> + <argument name="urlKey" value="{{updateVirtualProductTierPriceInStock.urlKey}}"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProductAndCheckSuccessMessage"/> <!-- Search updated virtual product(from above step) in the grid --> - <amOnPage url="{{ProductCatalogPage.url}}" stepKey="OpenProductCatalogPageToSearchUpdatedVirtualProduct"/> - <waitForPageLoad stepKey="waitForProductCatalogPageToLoad"/> - <conditionalClick selector="{{AdminProductGridFilterSection.clearAll}}" dependentSelector="{{AdminProductGridFilterSection.clearAll}}" visible="true" stepKey="clickClearAll"/> - <click selector="{{AdminProductGridFilterSection.filters}}" stepKey="clickFiltersButton"/> - <fillField selector="{{AdminProductGridFilterSection.nameFilter}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="fillVirtualProductNameInNameFilter"/> - <fillField selector="{{AdminProductGridFilterSection.skuFilter}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="fillVirtualProductSku"/> - <click selector="{{AdminProductGridFilterSection.applyFilters}}" stepKey="clickApplyFiltersButton"/> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowToVerifyUpdatedVirtualProductVisibleInGrid"/> - <waitForPageLoad stepKey="waitUntilVirtualProductPageIsOpened"/> + <actionGroup ref="SearchForProductOnBackendActionGroup" stepKey="searchForSimpleProduct"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> <!-- Verify customer see updated virtual product with tier price(from the above step) in the product form page --> - <seeInField selector="{{AdminProductFormSection.productName}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeProductName"/> - <seeInField selector="{{AdminProductFormSection.productSku}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="seeProductSku"/> - <seeInField selector="{{AdminProductFormSection.productPrice}}" userInput="{{updateVirtualProductTierPriceInStock.price}}" stepKey="seeProductPrice"/> - <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink1"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceWebsiteSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.website}}" stepKey="seeProductTierPriceWebsiteInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{tierPriceOnVirtualProduct.customer_group}}" stepKey="seeProductTierPriceCustomerGroupInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceQtyInput('0')}}" userInput="{{tierPriceOnVirtualProduct.qty}}" stepKey="seeProductTierPriceQuantityInput"/> - <seeInField selector="{{AdminProductFormAdvancedPricingSection.productTierPriceFixedPriceInput('0')}}" userInput="{{tierPriceOnVirtualProduct.price}}" stepKey="seeProductTierPriceFixedPrice"/> - <click selector="{{AdminProductFormAdvancedPricingSection.advancedPricingCloseButton}}" stepKey="clickAdvancedPricingCloseButton"/> + <actionGroup ref="AssertProductInfoOnEditPageActionGroup" stepKey="verifyProductInAdminEditForm"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> + <actionGroup ref="AdminProductFormOpenAdvancedPricingDialogActionGroup" stepKey="openAdvancedPricingDialogAgain"/> + <actionGroup ref="AssertAdminProductFormAdvancedPricingCheckTierPriceActionGroup" stepKey="checkTierPrice"> + <argument name="rowNumber" value="0"/> + <argument name="website" value="{{tierPriceOnVirtualProduct.website}}"/> + <argument name="customerGroup" value="{{tierPriceOnVirtualProduct.customer_group}}"/> + <argument name="quantity" value="{{tierPriceOnVirtualProduct.qty}}"/> + <argument name="priceType" value="Fixed"/> + <argument name="amount" value="{{tierPriceOnVirtualProduct.price}}"/> + </actionGroup> + <actionGroup ref="AdminProductFormCloseAdvancedPricingDialogActionGroup" stepKey="closeAdvancedPricingModal"/> <seeInField selector="{{AdminProductFormSection.productTaxClass}}" userInput="{{updateVirtualProductTierPriceInStock.productTaxClass}}" stepKey="seeProductTaxClass"/> - <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="{{updateVirtualProductTierPriceInStock.quantity}}" stepKey="seeProductQuantity"/> - <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="{{updateVirtualProductTierPriceInStock.status}}" stepKey="seeProductStockStatus"/> <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDownToVerify"/> <grabMultiple selector="{{AdminProductFormSection.selectMultipleCategories}}" stepKey="selectedCategories" /> <assertEquals stepKey="assertSelectedCategories"> <actualResult type="variable">selectedCategories</actualResult> - <expectedResult type="array">[$$categoryEntity.name$$]</expectedResult> + <expectedResult type="array">[$categoryEntity.name$]</expectedResult> </assertEquals> <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneOnCategorySelect"/> <seeInField selector="{{AdminProductFormSection.visibility}}" userInput="{{updateVirtualProductTierPriceInStock.visibility}}" stepKey="seeVisibility"/> - <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" x="0" y="-80" stepKey="scrollToAdminProductSEOSection1"/> - <click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="clickAdminProductSEOSection1"/> + + <conditionalClick selector="{{AdminProductSEOSection.sectionHeader}}" dependentSelector="{{AdminProductSEOSection.useDefaultUrl}}" visible="false" stepKey="openSearchEngineOptimizationSection"/> + <scrollTo selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="scrollToAdminProductSEOSection"/> <seeInField selector="{{AdminProductSEOSection.urlKeyInput}}" userInput="{{updateVirtualProductTierPriceInStock.urlKey}}" stepKey="seeUrlKey"/> <!--Verify customer see updated virtual product link on category page --> - <amOnPage url="{{StorefrontCategoryPage.url($$categoryEntity.name$$)}}" stepKey="openCategoryPage"/> - <waitForPageLoad stepKey="waitForCategoryPageLoad"/> - <see selector="{{StorefrontCategoryMainSection.productLink}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeVirtualProductLinkOnCategoryPage"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="openCategoryPageOnFrontend"> + <argument name="category" value="$categoryEntity$"/> + </actionGroup> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="checkProductOnCategoryPage"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> <!--Verify customer see updated virtual product with tier price on product storefront page --> - <amOnPage url="{{StorefrontProductPage.url(updateVirtualProductTierPriceInStock.urlKey)}}" stepKey="goToProductPage"/> - <waitForPageLoad stepKey="waitForStorefrontProductPageLoad"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeVirtualProductNameOnStoreFrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="{{updateVirtualProductTierPriceInStock.price}}" stepKey="seeVirtualProductPriceOnStoreFrontPage"/> - <see selector="{{StorefrontProductInfoMainSection.productSku}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="seeVirtualProductSku"/> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.productStockStatus}}" stepKey="productStockAvailableStatus"/> - <assertEquals stepKey="assertStockAvailableOnProductPage"> - <expectedResult type="string">{{updateVirtualProductTierPriceInStock.storefrontStatus}}</expectedResult> - <actualResult type="variable">productStockAvailableStatus</actualResult> - </assertEquals> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.productPrice}}" stepKey="productPriceAmount"/> - <assertEquals stepKey="assertOldPriceTextOnProductPage"> - <expectedResult type="string">${{updateVirtualProductTierPriceInStock.price}}</expectedResult> - <actualResult type="variable">productPriceAmount</actualResult> - </assertEquals> - <grabTextFrom selector="{{StorefrontProductInfoMainSection.tierPriceText}}" stepKey="tierPriceText"/> - <assertEquals stepKey="assertTierPriceTextOnProductPage"> - <expectedResult type="string">Buy {{tierPriceOnVirtualProduct.qty}} for ${{tierPriceOnVirtualProduct.price}} each and save 38%</expectedResult> - <actualResult type="variable">tierPriceText</actualResult> - </assertEquals> + <actionGroup ref="AssertProductInStorefrontProductPageActionGroup" stepKey="verifyProductOnFrontend"> + <argument name="product" value="updateVirtualProductTierPriceInStock"/> + </actionGroup> + <see selector="{{StorefrontProductInfoMainSection.productStockStatus}}" userInput="{{updateVirtualProductTierPriceInStock.storefrontStatus}}" stepKey="assertStockAvailableOnProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.tierPriceText}}" userInput="Buy {{tierPriceOnVirtualProduct.qty}} for ${{tierPriceOnVirtualProduct.price}} each and save 38%" stepKey="assertTierPriceTextOnProductPage"/> <!--Verify customer see updated virtual product link on magento storefront page and is searchable by sku --> - <amOnPage url="{{StorefrontProductPage.url(updateVirtualProductTierPriceInStock.urlKey)}}" stepKey="goToMagentoStorefrontPage"/> - <waitForPageLoad stepKey="waitForStoreFrontProductPageLoad"/> - <fillField selector="{{StorefrontQuickSearchResultsSection.searchTextBox}}" userInput="{{updateVirtualProductTierPriceInStock.sku}}" stepKey="fillVirtualProductName"/> - <waitForPageLoad stepKey="waitForSearchTextBox"/> - <click selector="{{StorefrontQuickSearchResultsSection.searchTextBoxButton}}" stepKey="clickSearchTextBoxButton"/> - <waitForPageLoad stepKey="waitForSearch"/> - <see selector="{{StorefrontQuickSearchResultsSection.productLink}}" userInput="{{updateVirtualProductTierPriceInStock.name}}" stepKey="seeVirtualProductName"/> - <grabTextFrom selector="{{StorefrontQuickSearchResultsSection.asLowAsLabel}}" stepKey="tierPriceTextOnStorefrontPage"/> - <assertEquals stepKey="assertTierPriceTextOnCategoryPage"> - <expectedResult type="string">As low as ${{tierPriceOnVirtualProduct.price}}</expectedResult> - <actualResult type="variable">tierPriceTextOnStorefrontPage</actualResult> - </assertEquals> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchProductBySku"> + <argument name="phrase" value="{{updateVirtualProductTierPriceInStock.sku}}"/> + </actionGroup> + <actionGroup ref="StorefrontQuickSearchSeeProductByNameActionGroup" stepKey="checkProductInSearchResults"> + <argument name="productName" value="{{updateVirtualProductTierPriceInStock.name}}"/> + </actionGroup> + <see selector="{{StorefrontQuickSearchResultsSection.asLowAsLabel}}" userInput="As low as ${{tierPriceOnVirtualProduct.price}}" stepKey="assertTierPriceTextOnCategoryPage"/> </test> </tests> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml index 4e0e8d03f59d5..f80cfed54c8f3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/CheckTierPricingOfProductsTest.xml @@ -36,6 +36,7 @@ <createData entity="Simple_US_Customer" stepKey="customer"/> <!--Login as admin--> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <createData entity="CustomerAccountSharingGlobal" stepKey="setConfigCustomerAccountToGlobal"/> </before> <!--Create website, Sore adn Store View--> @@ -328,6 +329,7 @@ <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite"> <argument name="websiteName" value="secondWebsite"/> </actionGroup> + <createData entity="CustomerAccountSharingDefault" stepKey="setConfigCustomerAccountDefault"/> <actionGroup ref="logout" stepKey="logout"/> <!--Do reindex and flush cache--> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml index 0daf8361ef9d1..0d382798a6266 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/DeleteUsedInConfigurableProductAttributeTest.xml @@ -73,6 +73,9 @@ <!-- Logout --> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to Stores > Attributes > Products. Search and select the product attribute that was used to create the configurable product--> <actionGroup ref="OpenProductAttributeFromSearchResultInGridActionGroup" stepKey="openProductAttributeFromSearchResultInGrid"> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml index 30461a30e53cb..2695c0f07f19e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontEnsureThatAccordionAnchorIsVisibleOnViewportOnceClickedTest.xml @@ -69,6 +69,9 @@ <deleteData createDataKey="createThirdAttribute" stepKey="deleteThirdAttribute"/> <deleteData createDataKey="createFourthAttribute" stepKey="deleteFourthAttribute"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Edit the product and set those attributes values --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml index db693b7229b17..7ba8f26ba1c05 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductWithEmptyAttributeTest.xml @@ -29,6 +29,9 @@ <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCategory"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml index d7f98c4cdd307..3aa12a7268593 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontProductsCompareWithEmptyAttributeTest.xml @@ -33,6 +33,9 @@ <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin1"/> <amOnPage url="{{AdminProductAttributeSetGridPage.url}}" stepKey="amOnAttributeSetPage"/> diff --git a/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php index b42262f1f0384..909b952078b58 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/CategoryLinkRepositoryTest.php @@ -6,40 +6,74 @@ namespace Magento\Catalog\Test\Unit\Model; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryProductLinkInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\CategoryLinkRepository; +use Magento\Catalog\Model\Product as ProductModel; +use Magento\Catalog\Model\ResourceModel\Product; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\InputException; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * Test for \Magento\Catalog\Model\CategoryLinkRepository + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class CategoryLinkRepositoryTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Catalog\Model\CategoryLinkRepository + * @var CategoryLinkRepository */ - protected $model; + private $model; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CategoryRepositoryInterface|MockObject */ - protected $categoryRepositoryMock; + private $categoryRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var ProductRepositoryInterface|MockObject */ - protected $productRepositoryMock; + private $productRepositoryMock; /** - * @var \PHPUnit_Framework_MockObject_MockObject + * @var CategoryProductLinkInterface|MockObject */ - protected $productLinkMock; + private $productLinkMock; + /** + * @var Product|MockObject + */ + private $productResourceMock; + + /** + * Initialize required data + */ protected function setUp() { - $this->categoryRepositoryMock = $this->createMock(\Magento\Catalog\Api\CategoryRepositoryInterface::class); - $this->productRepositoryMock = $this->createMock(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->productLinkMock = $this->createMock(\Magento\Catalog\Api\Data\CategoryProductLinkInterface::class); - $this->model = new \Magento\Catalog\Model\CategoryLinkRepository( + $this->productResourceMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods(['getProductsIdsBySkus']) + ->getMock(); + $this->categoryRepositoryMock = $this->createMock(CategoryRepositoryInterface::class); + $this->productRepositoryMock = $this->createMock(ProductRepositoryInterface::class); + $this->productLinkMock = $this->createMock(CategoryProductLinkInterface::class); + $this->model = new CategoryLinkRepository( $this->categoryRepositoryMock, - $this->productRepositoryMock + $this->productRepositoryMock, + $this->productResourceMock ); } - public function testSave() + /** + * Assign a product to the category + * + * @return void + */ + public function testSave(): void { $categoryId = 42; $productId = 55; @@ -47,10 +81,10 @@ public function testSave() $sku = 'testSku'; $productPositions = [$productId => $productPosition]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getPostedProducts', 'getProductsPosition', 'setPostedProducts', 'save'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($sku); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) @@ -61,14 +95,16 @@ public function testSave() $this->productLinkMock->expects($this->once())->method('getPosition')->willReturn($productPosition); $categoryMock->expects($this->once())->method('setPostedProducts')->with($productPositions); $categoryMock->expects($this->once())->method('save'); + $this->assertTrue($this->model->save($this->productLinkMock)); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Could not save product "55" with position 1 to category 42 + * Assign a product to the category with `CouldNotSaveException` + * + * @return void */ - public function testSaveWithCouldNotSaveException() + public function testSaveWithCouldNotSaveException(): void { $categoryId = 42; $productId = 55; @@ -76,10 +112,10 @@ public function testSaveWithCouldNotSaveException() $sku = 'testSku'; $productPositions = [$productId => $productPosition]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($sku); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) @@ -91,20 +127,28 @@ public function testSaveWithCouldNotSaveException() $categoryMock->expects($this->once())->method('setPostedProducts')->with($productPositions); $categoryMock->expects($this->once())->method('getId')->willReturn($categoryId); $categoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + + $this->expectExceptionMessage('Could not save product "55" with position 1 to category 42'); + $this->expectException(CouldNotSaveException::class); $this->model->save($this->productLinkMock); } - public function testDeleteByIds() + /** + * Remove the product assignment from the category + * + * @return void + */ + public function testDeleteByIds(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 55; $productPositions = [55 => 1]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) @@ -113,24 +157,26 @@ public function testDeleteByIds() $productMock->expects($this->once())->method('getId')->willReturn($productId); $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); $categoryMock->expects($this->once())->method('save'); + $this->assertTrue($this->model->deleteByIds($categoryId, $productSku)); } /** - * @expectedException \Magento\Framework\Exception\CouldNotSaveException - * @expectedExceptionMessage Could not save product "55" with position 1 to category 42 + * Delete the product assignment from the category with `CouldNotSaveException` + * + * @return void */ - public function testDeleteByIdsWithCouldNotSaveException() + public function testDeleteByIdsWithCouldNotSaveException(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 55; $productPositions = [55 => 1]; $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) @@ -140,50 +186,61 @@ public function testDeleteByIdsWithCouldNotSaveException() $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); $categoryMock->expects($this->once())->method('getId')->willReturn($categoryId); $categoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + + $this->expectExceptionMessage('Could not save product "55" with position 1 to category 42'); + $this->expectException(CouldNotSaveException::class); $this->model->deleteByIds($categoryId, $productSku); } /** - * @expectedException \Magento\Framework\Exception\InputException - * @expectedExceptionMessage The category doesn't contain the specified product. + * Delete the product assignment from the category with `InputException` + * + * @return void */ - public function testDeleteWithInputException() + public function testDeleteWithInputException(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 60; $productPositions = [55 => 1]; $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($productSku); $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) ->willReturn($productMock); $categoryMock->expects($this->once())->method('getProductsPosition')->willReturn($productPositions); $productMock->expects($this->once())->method('getId')->willReturn($productId); - $categoryMock->expects($this->never())->method('save'); + + $this->expectExceptionMessage('The category doesn\'t contain the specified product.'); + $this->expectException(InputException::class); $this->assertTrue($this->model->delete($this->productLinkMock)); } - public function testDelete() + /** + * Delete the product assignment from the category + * + * @return void + */ + public function testDelete(): void { - $categoryId = "42"; - $productSku = "testSku"; + $categoryId = 42; + $productSku = 'testSku'; $productId = 55; $productPositions = [55 => 1]; $this->productLinkMock->expects($this->once())->method('getCategoryId')->willReturn($categoryId); $this->productLinkMock->expects($this->once())->method('getSku')->willReturn($productSku); $categoryMock = $this->createPartialMock( - \Magento\Catalog\Model\Category::class, + Category::class, ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] ); - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); + $productMock = $this->createMock(ProductModel::class); $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) ->willReturn($categoryMock); $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku) @@ -192,6 +249,82 @@ public function testDelete() $productMock->expects($this->once())->method('getId')->willReturn($productId); $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); $categoryMock->expects($this->once())->method('save'); + $this->assertTrue($this->model->delete($this->productLinkMock)); } + + /** + * Delete by products skus + * + * @return void + */ + public function testDeleteBySkus(): void + { + $categoryId = 42; + $productSkus = ['testSku', 'testSku1', 'testSku2', 'testSku3']; + $productPositions = [55 => 1, 56 => 2, 57 => 3, 58 => 4]; + $categoryMock = $this->createPartialMock( + Category::class, + ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] + ); + $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) + ->willReturn($categoryMock); + $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus') + ->willReturn(['testSku' => 55, 'testSku1' => 56, 'testSku2' => 57, 'testSku3' => 58]); + $categoryMock->expects($this->once())->method('getProductsPosition')->willReturn($productPositions); + $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); + $categoryMock->expects($this->once())->method('save'); + + $this->assertTrue($this->model->deleteBySkus($categoryId, $productSkus)); + } + + /** + * Delete by products skus with `InputException` + * + * @return void + */ + public function testDeleteBySkusWithInputException(): void + { + $categoryId = 42; + $productSku = 'testSku'; + $categoryMock = $this->createPartialMock( + Category::class, + ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] + ); + $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) + ->willReturn($categoryMock); + + $this->expectExceptionMessage('The category doesn\'t contain the specified products.'); + $this->expectException(InputException::class); + $this->model->deleteBySkus($categoryId, [$productSku]); + } + + /** + * Delete by products skus with `CouldNotSaveException` + * + * @return void + */ + public function testDeleteSkusIdsWithCouldNotSaveException(): void + { + $categoryId = 42; + $productSku = 'testSku'; + $productId = 55; + $productPositions = [55 => 1]; + $categoryMock = $this->createPartialMock( + Category::class, + ['getProductsPosition', 'setPostedProducts', 'save', 'getId'] + ); + $this->categoryRepositoryMock->expects($this->once())->method('get')->with($categoryId) + ->willReturn($categoryMock); + $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus') + ->willReturn(['testSku' => $productId]); + $categoryMock->expects($this->once())->method('getProductsPosition')->willReturn($productPositions); + $categoryMock->expects($this->once())->method('setPostedProducts')->with([]); + $categoryMock->expects($this->once())->method('getId')->willReturn($categoryId); + $categoryMock->expects($this->once())->method('save')->willThrowException(new \Exception()); + + $this->expectExceptionMessage('Could not save products "testSku" to category 42'); + $this->expectException(CouldNotSaveException::class); + $this->assertTrue($this->model->deleteBySkus($categoryId, [$productSku])); + } } diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index eda6dbd2d9d6f..223d690d28327 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -74,6 +74,7 @@ <preference for="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverInterface" type="Magento\Catalog\Model\Product\Configuration\Item\ItemResolverComposite" /> <preference for="Magento\Catalog\Api\Data\MassActionInterface" type="\Magento\Catalog\Model\MassAction" /> <preference for="Magento\Catalog\Model\ProductLink\Data\ListCriteriaInterface" type="Magento\Catalog\Model\ProductLink\Data\ListCriteria" /> + <preference for="Magento\Catalog\Api\CategoryListDeleteBySkuInterface" type="Magento\Catalog\Model\CategoryLinkRepository"/> <type name="Magento\Customer\Model\ResourceModel\Visitor"> <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" /> </type> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml index da0b3b36d561e..89d005a178fac 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml @@ -22,7 +22,7 @@ require(['prototype'], function(){ initializeFile: function(inputBox) { this.inputFile = inputBox.select('input[name="<?= /* @noEscape */ $_fileName ?>"]')[0]; this.inputFileAction = inputBox.select('input[name="<?= /* @noEscape */ $_fieldNameAction ?>"]')[0]; - this.fileNameBox = inputBox.up('dd').select('.<?= /* @noEscape */ $_fileNamed ?>')[0]; + this.fileNameBox = inputBox.up('div').select('.<?= /* @noEscape */ $_fileNamed ?>')[0]; }, toggleFileChange: function(inputBox) { diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php index 53d3b624285e9..1057d21283ea8 100644 --- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php +++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php @@ -123,7 +123,7 @@ private function addVisibilityFilter(SearchCriteriaInterface $searchCriteria, bo { if ($isFilter && $isSearch) { // Index already contains products filtered by visibility: catalog, search, both - return ; + return; } $visibilityIds = $isSearch ? $this->visibility->getVisibleInSearchIds() diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 369805a94dd84..7fb4d8b025b07 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -107,6 +107,9 @@ </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml index 642bbfc0453c5..c27a1716e84e5 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportImportConfigurableProductWithImagesTest.xml @@ -153,6 +153,9 @@ <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetProductGridColumnsInitial"/> <!-- Admin logout--> <actionGroup ref="logout" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to System > Export --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index 397c1ee57e7f5..a55e92d64ce00 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -95,6 +95,9 @@ </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index e00346654ecf4..7131fe41ea5ec 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -111,6 +111,9 @@ </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index 04be8f3ae823e..ea27d61e3b00c 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -96,6 +96,9 @@ </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to export page --> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml index 8458fcf3b94e0..07354d0b4c89f 100644 --- a/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Test/AssociatedProductToConfigurableOutOfStockTest.xml @@ -80,13 +80,16 @@ </before> <after> - <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> - <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> - <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> - <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> - <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> - <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> - </after> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="simplecategory" stepKey="deleteSimpleCategory"/> + <deleteData createDataKey="createSimpleUsCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> + <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> + </after> <!-- Login as a customer --> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUpNewUser"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml index d99ebac94fbec..453c64a32ce32 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithSpecialPricesTest.xml @@ -92,6 +92,9 @@ <deleteData createDataKey="createSecondConfigChildProduct" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add special prices for products --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml index 02d998870fb65..d5efaec971bba 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml @@ -182,6 +182,9 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute1" stepKey="deleteConfigProductAttribute1"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Delete the simple product and catalog price rule --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml index 1615985370182..c3cb4d6180f18 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogPriceRuleByProductAttributeTest.xml @@ -105,6 +105,9 @@ <click stepKey="resetFilters" selector="{{AdminSecondaryGridSection.resetFilters}}"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add values to your attribute ( ex: red , green) --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index 02d6d90ae5d0e..dad3063dae45f 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -96,6 +96,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Begin creating a new catalog price rule --> <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="newCatalogPriceRuleByUIWithConditionIsCategory"> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml index 86d3dccba7595..102054c315f4c 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/CatalogPriceRuleAndCustomerGroupMembershipArePersistedUnderLongTermCookieTest.xml @@ -15,71 +15,88 @@ <title value="Verify that Catalog Price Rule and Customer Group Membership are persisted under long-term cookie"/> <description value="Verify that Catalog Price Rule and Customer Group Membership are persisted under long-term cookie"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-69455"/> + <testCaseId value="MC-27571"/> <group value="persistent"/> </annotations> <before> - <createData entity="PersistentConfigEnabled" stepKey="enablePersistent"/> - <createData entity="PersistentLogoutClearDisable" stepKey="persistentLogoutClearDisable"/> + + <createData entity="PersistentConfigSettings" stepKey="enablePersistent"/> <createData entity="_defaultCategory" stepKey="createCategory"/> - <createData entity="_defaultProduct" stepKey="createProduct"> + <createData entity="productWithHTMLEntityOne" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> - <field key="price">50</field> - </createData> - <createData entity="Simple_US_Customer" stepKey="createCustomer"> - <field key="group_id">1</field> </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <!--Delete all Catalog Price Rule if exist--> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + - <actionGroup ref="LoginAsAdmin" stepKey="login"/> <!--Create Catalog Rule--> - <actionGroup ref="NewCatalogPriceRuleByUIWithConditionIsCategoryActionGroup" stepKey="createCatalogPriceRule"> - <argument name="catalogRule" value="_defaultCatalogRule"/> - <argument name="categoryId" value="$$createCategory.id$$"/> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> + <argument name="groups" value="'General'"/> + </actionGroup> + <actionGroup ref="AdminFillCatalogRuleConditionActionGroup" stepKey="createCatalogPriceRule"> + <argument name="conditionValue" value="$createCategory.id$"/> </actionGroup> - <actionGroup ref="SelectGeneralCustomerGroupActionGroup" stepKey="selectCustomerGroup"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="clickSaveAndApplyRules"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="assertSuccess"/> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="clickSaveAndApplyRule"/> + + <!-- Perform reindex --> + <magentoCLI command="indexer:reindex" arguments="catalogrule_rule" stepKey="reindex"/> </before> <after> - <!-- Delete the rule --> - <amOnPage url="{{CatalogRulePage.url}}" stepKey="goToCatalogPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> <createData entity="PersistentConfigDefault" stepKey="setDefaultPersistentState"/> <createData entity="PersistentLogoutClearEnabled" stepKey="persistentLogoutClearEnabled"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <!-- Delete the rule --> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + <actionGroup ref="logout" stepKey="logout"/> </after> <!--Go to category and check price--> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="checkPriceSimpleProduct"/> + <actionGroup ref="AssertStorefrontProductPriceInCategoryPageActionGroup" stepKey="assertProductPriceInCategoryPage"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> <!--Login to storefront from customer and check price--> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="logInFromCustomer"> - <argument name="Customer" value="$$createCustomer$$"/> + <argument name="Customer" value="$createCustomer$"/> + </actionGroup> + <actionGroup ref="AssertCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeMessageForJohnDoeCustomer"> + <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> + </actionGroup> + + <!--Go to category and check special price--> + <actionGroup ref="AssertStorefrontProductSpecialPriceInCategoryPageActionGroup" stepKey="assertProductSpecialPriceInCategoryPage"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> </actionGroup> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage2"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome"/> - <see selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="45.00" stepKey="checkPriceSimpleProduct2"/> - <!--Click *Sign Out* and check the price of the Simple Product--> + + <!--Click *Sign Out*--> <actionGroup ref="StorefrontSignOutActionGroup" stepKey="storefrontSignOut"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage3"/> - <see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome2"/> - <seeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkLinkNotYoy"/> - <see selector="{{StorefrontCategoryProductSection.ProductSpecialPriceByNumber('1')}}" userInput="45.00" stepKey="checkPriceSimpleProduct3"/> + <actionGroup ref="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeForJohnDoeCustomer"> + <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> + </actionGroup> + + <!--Go to category and check special price--> + <actionGroup ref="AssertStorefrontProductSpecialPriceInCategoryPageActionGroup" stepKey="assertProductSpecialPriceInCategoryPageAfterLogout"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> <!--Click the *Not you?* link and check the price for Simple Product--> - <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickNext"/> - <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onStorefrontCategoryPage4"/> - <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="homeCheckWelcome3"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="checkPriceSimpleProduct4"/> + <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickNotYouLink"/> + <actionGroup ref="AssertStorefrontDefaultWelcomeMessageActionGroup" stepKey="seeWelcomeMessageForJohnDoeCustomerAfterLogout"/> + <actionGroup ref="AssertStorefrontProductPriceInCategoryPageActionGroup" stepKey="assertProductPriceInCategoryPageAfterLogout"> + <argument name="categoryUrl" value="$createCategory.custom_attributes[url_key]$"/> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductCatalogRuleSpecialPriceTitleByName($createProduct.name$)}}" stepKey="dontSeeSpecialPrice"/> </test> </tests> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index 3423baf7970eb..45b0c1bcaa5a0 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -16,55 +16,51 @@ <description value="Customer should not see the catalog price rule promotion if status is inactive"/> <severity value="CRITICAL"/> <testCaseId value="MC-79"/> - <group value="CatalogRule"/> - <skip> - <issueId value="MC-30384"/> - </skip> + <group value="catalogRule"/> </annotations> + <before> - <actionGroup ref="LoginAsAdmin" stepKey="login"/> <createData entity="ApiCategory" stepKey="createCategory"/> <createData entity="ApiSimpleProduct" stepKey="createProduct"> <requiredEntity createDataKey="createCategory"/> </createData> - <actionGroup stepKey="createNewPriceRule" ref="NewCatalogPriceRuleByUIActionGroup"/> - <actionGroup stepKey="selectLoggedInCustomers" ref="SelectNotLoggedInCustomerGroupActionGroup"/> - <scrollToTopOfPage stepKey="scrollToTop"/> - <click stepKey="setInactive" selector="{{AdminCategoryBasicFieldSection.enableCategoryLabel}}"/> - <click selector="{{AdminNewCatalogPriceRule.saveAndApply}}" stepKey="saveAndApply"/> - <see selector="{{AdminCategoryMessagesSection.SuccessMessage}}" userInput="You saved the rule." stepKey="seeSuccess"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> - <!-- Perform reindex and flush cache --> - <magentoCLI command="indexer:reindex" stepKey="reindex"/> - <magentoCLI command="cache:flush" stepKey="flushCache"/> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> + <argument name="active" value="0"/> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyFirstPriceRule"/> + <!-- Perform reindex --> + <magentoCLI command="indexer:reindex" arguments="catalogrule_rule" stepKey="reindex"/> </before> + <after> <deleteData createDataKey="createProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <amOnPage url="admin/catalog_rule/promo_catalog/" stepKey="goToPriceRulePage"/> - <actionGroup ref="deleteEntitySecondaryGrid" stepKey="deletePriceRule"> - <argument name="name" value="{{_defaultCatalogRule.name}}"/> - <argument name="searchInput" value="{{AdminSecondaryGridSection.catalogRuleIdentifierSearch}}"/> - </actionGroup> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="logout"/> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> <!-- Verify price is not discounted on category page --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategory"/> - <waitForPageLoad stepKey="waitForCategory"/> - <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="seePrice1"/> + <amOnPage url="{{StorefrontCategoryPage.url($createCategory.custom_attributes[url_key]$)}}" stepKey="openCategoryPageOnFrontend"/> + <waitForPageLoad stepKey="waitForCategoryPageLoaded"/> + <see selector="{{StorefrontCategoryProductSection.ProductPriceByNumber('1')}}" userInput="$$createProduct.price$$" stepKey="seeProductPriceOnCategoryPage"/> <!-- Verify price is not discounted on the product page --> - <amOnPage url="$$createProduct.sku$$.html" stepKey="goToProduct"/> - <waitForPageLoad stepKey="waitForProduct"/> - <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$$createProduct.price$$" stepKey="seePrice2"/> + <amOnPage url="{{StorefrontProductPage.url($createProduct.custom_attributes[url_key]$)}}" stepKey="openProductPageOnFrontend"/> + <waitForPageLoad stepKey="waitForProductPageLoaded"/> + <see selector="{{StorefrontProductInfoMainSection.productPrice}}" userInput="$createProduct.price$" stepKey="seePriceOnProductPage"/> <!-- Verify price is not discounted in the cart --> - <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> - <waitForPageLoad stepKey="waitForCart"/> - <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> - <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> - <waitForPageLoad stepKey="waitForCheckout"/> - <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$$createProduct.price$$" stepKey="seePrice3"/> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"> + <argument name="productName" value="$createProduct.name$"/> + </actionGroup> + <actionGroup ref="StorefrontOpenCartPageActionGroup" stepKey="openCartPage" /> + <waitForElementVisible selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="waitForSubtotalAppears"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$createProduct.price$" stepKey="seeProductPriceOnCartPage"/> </test> </tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml index 8b72b7616b6ff..80ff2a447052c 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml @@ -172,6 +172,9 @@ <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> <!-- Admin log out --> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create catalog price rule --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml index c110daee35428..ea0d8e01d540f 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml @@ -176,6 +176,9 @@ <!-- Admin log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create price rule --> <actionGroup ref="NewCatalogPriceRuleByUIActionGroup" stepKey="createPriceRule"> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml index bc6c89f2f1155..2a5786b38107f 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml @@ -106,6 +106,9 @@ <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create price rule for first configurable product option --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml index 05f30fd6fcbde..c077719dfa80b 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml @@ -123,6 +123,9 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create price rule for first configurable product option --> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetCategoryActionGroup.xml b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetCategoryActionGroup.xml new file mode 100644 index 0000000000000..ecc5780da0e02 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/ActionGroup/AdminFillCatalogProductsListWidgetCategoryActionGroup.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="AdminFillCatalogProductsListWidgetCategoryActionGroup"> + <annotations> + <description>Fill catalog products list widget category.</description> + </annotations> + + <arguments> + <argument name="categoryName" type="string" defaultValue="{{_defaultCategory.name}}"/> + </arguments> + + <waitForElementVisible selector="{{WidgetSection.AddParam}}" stepKey="waitForAddParamElement"/> + <click selector="{{WidgetSection.AddParam}}" stepKey="clickOnAddParamElement"/> + <waitForElementVisible selector="{{WidgetSection.ConditionsDropdown}}" stepKey="waitForConditionsDropdownVisible"/> + <selectOption selector="{{WidgetSection.ConditionsDropdown}}" userInput="Category" stepKey="selectCategoryAsCondition"/> + <waitForElementVisible selector="{{WidgetSection.RuleParam}}" stepKey="waitForRuleParamElementVisible"/> + <click selector="{{WidgetSection.RuleParam}}" stepKey="clickToAddRuleParam"/> + <click selector="{{WidgetSection.Chooser}}" stepKey="clickToSelectFromList"/> + <waitForPageLoad stepKey="waitForPageLoadAfterSelectingRuleParam"/> + <waitForElementVisible selector="{{WidgetSection.PreCreateCategory(categoryName)}}" stepKey="waitForCategoryElementVisible"/> + <click selector="{{WidgetSection.PreCreateCategory(categoryName)}}" stepKey="selectCategoryFromArguments"/> + <click selector="{{AdminNewWidgetSection.applyParameter}}" stepKey="clickApplyButton"/> + <waitForElementNotVisible selector="{{InsertWidgetSection.categoryTreeWrapper}}" stepKey="waitForCategoryTreeIsNotVisible"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml index 855d325c9850c..66aa4252f4b5f 100644 --- a/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Section/CatalogWidgetSection.xml @@ -22,5 +22,6 @@ <element name="conditionOperator" type="button" selector="#conditions__1--{{arg3}}__operator" parameterized="true"/> <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"/> </section> </sections> diff --git a/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.xml new file mode 100644 index 0000000000000..f1753f4c0b649 --- /dev/null +++ b/app/code/Magento/CatalogWidget/Test/Mftf/Test/StorefrontProductGridUIUpdatesOnDesktopTest.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="StorefrontProductGridUIUpdatesOnDesktopTest"> + <annotations> + <features value="Catalog"/> + <stories value="New products list widget"/> + <title value="Storefront product grid UI updates on Desktop"/> + <description value="Storefront product grid UI updates on Desktop"/> + <severity value="MAJOR"/> + <testCaseId value="MC-27569"/> + <group value="catalog"/> + <group value="WYSIWYGDisabled"/> + </annotations> + <before> + <!-- 1. Create multiple products and assign to a category. --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createFirstSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createSecondSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createThirdSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createFourthSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!-- 2. Create new CMS page and add "Catalog Product List" widget type via content >insert widget option --> + <createData entity="_emptyCmsPage" stepKey="createCmsPage"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="NavigateToCreatedCMSPageActionGroup" stepKey="navigateToCreatedCmsPage"> + <argument name="CMSPage" value="$createCmsPage$"/> + </actionGroup> + <actionGroup ref="AdminInsertWidgetToCmsPageContentActionGroup" stepKey="insertWidgetToCmsPageContentActionGroup"> + <argument name="widgetType" value="Catalog Products List"/> + </actionGroup> + <actionGroup ref="AdminFillCatalogProductsListWidgetCategoryActionGroup" stepKey="fillCatalogProductsListWidgetOptions"> + <argument name="categoryName" value="$createCategory.name$"/> + </actionGroup> + <actionGroup ref="AdminClickInsertWidgetActionGroup" stepKey="clickInsertWidgetButton"/> + <actionGroup ref="SaveCmsPageActionGroup" stepKey="clickSaveButton"/> + </before> + <after> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createThirdSimpleProduct" stepKey="deleteThirdSimpleProduct"/> + <deleteData createDataKey="createFourthSimpleProduct" stepKey="deleteFourthSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createCmsPage" stepKey="deleteCmsPage"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateAndOpenCreatedCmsPage"> + <argument name="identifier" value="$createCmsPage.identifier$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup" stepKey="assertProductControlsAreNotVisibleWithoutHoverOnCmsPage"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($createFirstSimpleProduct.custom_attributes[url_key]$)}}" stepKey="assertProductName"/> + <seeElement selector="{{StorefrontCategoryMainSection.productPriceByName($createFirstSimpleProduct.name$)}}" stepKey="assertProductPrice"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($createFirstSimpleProduct.name$)}}" stepKey="hoverProduct"/> + <actionGroup ref="AssertStorefrontProductControlsAreVisibleOnHoverActionGroup" stepKey="assertProductControlsAreVisibleOnHoverOnCmsPage"/> + <actionGroup ref="AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup" stepKey="assertAddToWishListIconIsClickable"/> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="navigateAndOpenCreatedCmsPageToVerifyAddToCompareIconIsClickable"> + <argument name="identifier" value="$createCmsPage.identifier$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="assertAddToCompareIconIsClickable"> + <argument name="productVar" value="$createFirstSimpleProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateCategoryCreatedInPreconditions"> + <argument name="category" value="$createCategory$"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductControlsAreNotVisibleWithoutHoverActionGroup" stepKey="assertProductControlsAreNotVisibleWithoutHoverOnCategoryPage"/> + <seeElement selector="{{StorefrontCategoryMainSection.productLinkByHref($createFirstSimpleProduct.custom_attributes[url_key]$)}}" stepKey="assertProductNameOnCategoryPage"/> + <seeElement selector="{{StorefrontCategoryMainSection.productPriceByName($createFirstSimpleProduct.name$)}}" stepKey="assertProductPriceOnCategoryPage"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName($createFirstSimpleProduct.name$)}}" stepKey="hoverProductOnCategoryPage"/> + <actionGroup ref="AssertStorefrontProductControlsAreVisibleOnHoverActionGroup" stepKey="assertProductControlsAreVisibleOnHoverOnCategoryPage"/> + <actionGroup ref="AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup" stepKey="assertAddToWishListIconIsClickableOnCategoryPage"/> + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateCategoryCreatedInPreconditionsToVerifyAddToCompareIconIsClickable"> + <argument name="category" value="$createCategory$"/> + </actionGroup> + <actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="assertAddToCompareIconIsClickableOnCategoryPage"> + <argument name="productVar" value="$createFirstSimpleProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontClearCompareActionGroup" stepKey="clearAllCompareProducts"/> + </test> +</tests> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml index 891f647e3d5ef..9d092b4b84a3c 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/DeleteConfigurableProductFromShoppingCartTest.xml @@ -60,6 +60,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add configurable product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index 797172de5de45..4a4428712ac9d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -111,6 +111,9 @@ <!-- Logout customer --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogoutStorefront"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add Simple Product to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml index e716ba294f578..7d91b13b7b833 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml @@ -120,6 +120,9 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add Configurable Product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml index a9db81620d329..3f286df8337de 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckoutWithSpecialPriceProductsTest.xml @@ -102,6 +102,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Open Product page in StoreFront and assert product and price range --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml index 34264e5982651..d302289b27f19 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromMiniShoppingCartTest.xml @@ -72,6 +72,9 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Add Configurable Product to the cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml index b800ef758d65e..e544cbb497f2d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutUsingFreeShippingAndTaxesTest.xml @@ -109,6 +109,9 @@ <createData entity="DefaultShippingMethodsConfig" stepKey="defaultShippingMethodsConfig"/> <createData entity="DefaultMinimumOrderAmount" stepKey="defaultMinimumOrderAmount"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create a Tax Rule --> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js b/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js index 086859fa8021d..1f14ab444e0c1 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/action/select-billing-address.js @@ -18,7 +18,7 @@ define([ if (quote.shippingAddress() && billingAddress.getCacheKey() == //eslint-disable-line eqeqeq quote.shippingAddress().getCacheKey() ) { - address = $.extend({}, billingAddress); + address = $.extend(true, {}, billingAddress); address.saveInAddressBook = null; } else { address = billingAddress; diff --git a/app/code/Magento/Cms/Model/ResourceModel/Block.php b/app/code/Magento/Cms/Model/ResourceModel/Block.php index 30e817713755c..1324b9bd127e9 100644 --- a/app/code/Magento/Cms/Model/ResourceModel/Block.php +++ b/app/code/Magento/Cms/Model/ResourceModel/Block.php @@ -185,13 +185,9 @@ public function getIsUniqueBlockToStores(AbstractModel $object) $entityMetadata = $this->metadataPool->getMetadata(BlockInterface::class); $linkField = $entityMetadata->getLinkField(); - $stores = (array)$object->getData('store_id'); - $isDefaultStore = $this->_storeManager->isSingleStoreMode() - || array_search(Store::DEFAULT_STORE_ID, $stores) !== false; - - if (!$isDefaultStore) { - $stores[] = Store::DEFAULT_STORE_ID; - } + $stores = $this->_storeManager->isSingleStoreMode() + ? [Store::DEFAULT_STORE_ID] + : (array)$object->getData('store_id'); $select = $this->getConnection()->select() ->from(['cb' => $this->getMainTable()]) @@ -200,11 +196,8 @@ public function getIsUniqueBlockToStores(AbstractModel $object) 'cb.' . $linkField . ' = cbs.' . $linkField, [] ) - ->where('cb.identifier = ? ', $object->getData('identifier')); - - if (!$isDefaultStore) { - $select->where('cbs.store_id IN (?)', $stores); - } + ->where('cb.identifier = ? ', $object->getData('identifier')) + ->where('cbs.store_id IN (?)', $stores); if ($object->getId()) { $select->where('cb.' . $entityMetadata->getIdentifierField() . ' <> ?', $object->getId()); diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminClickInsertWidgetActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminClickInsertWidgetActionGroup.xml new file mode 100644 index 0000000000000..c221dd62cd7f6 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminClickInsertWidgetActionGroup.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="AdminClickInsertWidgetActionGroup"> + <annotations> + <description>Click "Insert Widget" button in the opened widget popup</description> + </annotations> + + <click selector="{{WidgetSection.InsertWidget}}" stepKey="clickInsertWidgetButton"/> + <waitForElementNotVisible selector="{{WidgetSection.InsertWidgetTitle}}" stepKey="waitForWidgetPopupDisappear"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertWidgetToCmsPageContentActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertWidgetToCmsPageContentActionGroup.xml new file mode 100644 index 0000000000000..734d8d98722b2 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminInsertWidgetToCmsPageContentActionGroup.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="AdminInsertWidgetToCmsPageContentActionGroup"> + <annotations> + <description>Insert widget to CMS Page content field. You are on CMS edit page, content tab is expanded.</description> + </annotations> + + <arguments> + <argument name="widgetType" type="string" defaultValue="CMS Page Link"/> + </arguments> + <waitForElementVisible selector="{{CmsNewPagePageActionsSection.insertWidget}}" stepKey="waitForInsertWidgetElementVisible"/> + <click selector="{{CmsNewPagePageActionsSection.insertWidget}}" stepKey="clickOnInsertWidgetButton"/> + <waitForPageLoad stepKey="waitForPageLoadAfterClickOnInsertWidgetButton"/> + <waitForElementVisible selector="{{WidgetSection.InsertWidgetTitle}}" stepKey="waitForInsertWidgetTitle"/> + <selectOption selector="{{WidgetSection.WidgetType}}" userInput="{{widgetType}}" stepKey="selectWidgetType"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml index 95ce3c499b6b8..44e29f7e2fe55 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/SaveAndCloseCMSBlockWithSplitButtonActionGroup.xml @@ -17,6 +17,7 @@ <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitButton"/> <click selector="{{BlockNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveBlock"/> <waitForPageLoad stepKey="waitForPageLoadAfterClickingSave"/> - <see userInput="You saved the block." stepKey="assertSaveBlockSuccessMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" stepKey="waitForSuccessMessageAppear"/> + <see userInput="You saved the block." selector="{{AdminMessagesSection.success}}" stepKey="assertSaveBlockSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml index fee2f984c42d2..bc30098cb43c7 100644 --- a/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/VerifyCmsBlockSaveSplitButtonActionGroup.xml @@ -17,6 +17,7 @@ <waitForPageLoad stepKey="waitForPageLoad1"/> <!--Verify Save&Duplicate button and Save&Close button--> <click selector="{{BlockNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> + <waitForElementVisible selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" stepKey="waitForButtonMenuOpened"/> <see selector="{{BlockNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> <see selector="{{BlockNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> </actionGroup> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml new file mode 100644 index 0000000000000..ee06b4ce86656 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest.xml @@ -0,0 +1,44 @@ +<?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="AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest"> + <annotations> + <features value="Cms"/> + <stories value="Default layout configuration MAGETWO-88793"/> + <title value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> + <description value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-89025"/> + <group value="Cms"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> + <waitForElementVisible selector="{{DefaultLayoutsSection.pageLayout}}" stepKey="DefaultProductLayout" /> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.pageLayout}}" userInput="1 column" stepKey="seeOneColumnSelected" /> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.productLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected1" /> + <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected2" /> + <selectOption selector="{{DefaultLayoutsSection.pageLayout}}" userInput="2 columns with right bar" stepKey="selectColumnsWithRightBar"/> + <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPagePagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForLoadingMaskToDisappear stepKey="wait2" /> + <click selector="{{CmsDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> + <waitForElementVisible selector="{{CmsDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> + <seeOptionIsSelected selector="{{CmsDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="seeColumnsWithRightBar" /> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml index e6fcbab4de644..7fd39ab5bb1d6 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateCmsPageTest.xml @@ -29,76 +29,8 @@ <actionGroup ref="CreateNewPageWithBasicValues" stepKey="createNewPageWithBasicValues" /> <actionGroup ref="SaveCmsPageActionGroup" stepKey="clickSaveCmsPageButton" /> <actionGroup ref="VerifyCreatedCmsPage" stepKey="verifyCmsPage" /> - </test> - <test name="AdminConfigDefaultCMSPageLayoutFromConfigurationSettingTest"> - <annotations> - <features value="Cms"/> - <stories value="Default layout configuration MAGETWO-88793"/> - <title value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> - <description value="Admin should be able to configure the default layout for CMS Page from System Configuration"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89025"/> - <group value="Cms"/> - </annotations> - <before> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - </before> - <after> - <actionGroup ref="RestoreLayoutSetting" stepKey="sampleActionGroup"/> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{WebConfigurationPage.url}}" stepKey="navigateToWebConfigurationPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <conditionalClick stepKey="expandDefaultLayouts" selector="{{WebSection.DefaultLayoutsTab}}" dependentSelector="{{WebSection.CheckIfTabExpand}}" visible="true" /> - <waitForElementVisible selector="{{DefaultLayoutsSection.pageLayout}}" stepKey="DefaultProductLayout" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.pageLayout}}" userInput="1 column" stepKey="seeOneColumnSelected" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.productLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected1" /> - <seeOptionIsSelected selector="{{DefaultLayoutsSection.categoryLayout}}" userInput="No layout updates" stepKey="seeNoLayoutUpdatesSelected2" /> - <selectOption selector="{{DefaultLayoutsSection.pageLayout}}" userInput="2 columns with right bar" stepKey="selectColumnsWithRightBar"/> - <click selector="{{ContentManagementSection.Save}}" stepKey="clickSaveConfig" /> - <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPagePagesGrid"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <waitForLoadingMaskToDisappear stepKey="wait2" /> - <click selector="{{CmsDesignSection.DesignTab}}" stepKey="clickOnDesignTab"/> - <waitForElementVisible selector="{{CmsDesignSection.LayoutDropdown}}" stepKey="waitForLayoutDropDown" /> - <seeOptionIsSelected selector="{{CmsDesignSection.LayoutDropdown}}" userInput="2 columns with right bar" stepKey="seeColumnsWithRightBar" /> - </test> - <test name="AdminCreateDuplicatedCmsPageTest"> - <annotations> - <features value="Cms"/> - <stories value="CMS Page Duplication and Reset Removal MAGETWO-87096"/> - <title value="Admin should be able to duplicate a CMS Page"/> - <description value="Admin should be able to duplicate a CMS Page"/> - <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-89184"/> - <group value="Cms"/> - </annotations> - <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> - </before> - <after> - <actionGroup ref="logout" stepKey="logout"/> - </after> - <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPageCreationForm"/> - <waitForPageLoad stepKey="waitForPageLoad1"/> - <!--Verify Save&Duplicate button and Save&Close button--> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> - <see selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> - <see selector="{{CmsNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> - <!--Create new CMS Page page--> - <actionGroup ref="FillOutCMSPageContent" stepKey="FillOutBlockContent"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn2" /> - <click selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" stepKey="clickSaveAndDuplicate" /> - <waitForPageLoad stepKey="waitForPageLoad3"/> - <see userInput="You saved the page." stepKey="seeSavedPageMsgOnForm"/> - <see userInput="You duplicated the page." stepKey="seeDuplicatedPageMsg"/> - <!--Verify duplicated CMS Page--> - <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockNotEnable" /> - <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="assertContent"/> - <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn3" /> - <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndClose"/> - <see userInput="You saved the page." stepKey="seeSavedCMSPageMsgOnGrid"/> - <seeElement selector="div[data-role='grid-wrapper']" stepKey="seeGridPage" /> + <actionGroup ref="DeletePageByUrlKeyActionGroup" stepKey="deletePage"> + <argument name="UrlKey" value="{{_defaultCmsPage.identifier}}"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDuplicatedCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDuplicatedCmsPageTest.xml new file mode 100644 index 0000000000000..cf333f8b559d0 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCreateDuplicatedCmsPageTest.xml @@ -0,0 +1,49 @@ +<?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="AdminCreateDuplicatedCmsPageTest"> + <annotations> + <features value="Cms"/> + <stories value="CMS Page Duplication and Reset Removal MAGETWO-87096"/> + <title value="Admin should be able to duplicate a CMS Page"/> + <description value="Admin should be able to duplicate a CMS Page"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-89184"/> + <group value="Cms"/> + </annotations> + <before> + <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> + <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <amOnPage url="{{CmsNewPagePage.url}}" stepKey="amOnPageCreationForm"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <!--Verify Save&Duplicate button and Save&Close button--> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn1" /> + <see selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" userInput="Save & Duplicate" stepKey="seeSaveAndDuplicate"/> + <see selector="{{CmsNewPagePageActionsSection.saveAndClose}}" userInput="Save & Close" stepKey="seeSaveAndClose"/> + <!--Create new CMS Page page--> + <actionGroup ref="FillOutCMSPageContent" stepKey="FillOutBlockContent"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn2" /> + <click selector="{{CmsNewPagePageActionsSection.saveAndDuplicate}}" stepKey="clickSaveAndDuplicate" /> + <waitForPageLoad stepKey="waitForPageLoad3"/> + <see userInput="You saved the page." stepKey="seeSavedPageMsgOnForm"/> + <see userInput="You duplicated the page." stepKey="seeDuplicatedPageMsg"/> + <!--Verify duplicated CMS Page--> + <seeElement selector="{{BlockNewPageBasicFieldsSection.isActive('0')}}" stepKey="seeBlockNotEnable" /> + <actionGroup ref="AssertCMSPageContentActionGroup" stepKey="assertContent"/> + <click selector="{{CmsNewPagePageActionsSection.expandSplitButton}}" stepKey="expandSplitBtn3" /> + <click selector="{{CmsNewPagePageActionsSection.saveAndClose}}" stepKey="clickSaveAndClose"/> + <see userInput="You saved the page." stepKey="seeSavedCMSPageMsgOnGrid"/> + <seeElement selector="div[data-role='grid-wrapper']" stepKey="seeGridPage" /> + </test> +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml index 8c89c47ab5077..23f602d7b1005 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminSaveAndCloseCmsBlockTest.xml @@ -16,21 +16,25 @@ <description value="Admin should be able to create a CMS block using save and close"/> <severity value="CRITICAL"/> <group value="Cms"/> + <group value="WYSIWYGDisabled"/> </annotations> + <before> - <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> - <actionGroup ref="DisabledWYSIWYGActionGroup" stepKey="disableWYSIWYG"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> </before> + <after> <actionGroup ref="deleteBlock" stepKey="deleteCreatedBlock"> <argument name="Block" value="_defaultBlock"/> </actionGroup> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="resetGridFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> </after> + <!-- Navigate to create cms block page and verify save split button --> - <actionGroup ref="VerifyCmsBlockSaveSplitButtonActionGroup" stepKey="verifyCmsBlockSaveSplitButton" /> + <actionGroup ref="VerifyCmsBlockSaveSplitButtonActionGroup" stepKey="assertCmsBlockSaveSplitButton"/> <!--Create new CMS Block page--> - <actionGroup ref="FillOutBlockContent" stepKey="FillOutBlockContent"/> + <actionGroup ref="FillOutBlockContent" stepKey="fillOutBlockContent"/> <actionGroup ref="SaveAndCloseCMSBlockWithSplitButtonActionGroup" stepKey="saveCmsBlockContent" /> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml index e6ab1c130606b..385616dcca9b9 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/CheckStaticBlocksTest.xml @@ -56,12 +56,23 @@ <seeInCurrentUrl url="cms/block/new" stepKey="VerifyNewBlockPageIsOpened1"/> <!--Add new BLock with the same data--> <actionGroup ref="FillOutBlockContent" stepKey="FillOutBlockContent1"/> - <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="Default Store View" stepKey="selectDefaultStoreView" /> - <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="{{customStore.name}}" stepKey="selectSecondStoreView1" /> <click selector="{{BlockNewPagePageActionsSection.saveBlock}}" stepKey="ClickToSaveBlock1"/> <waitForPageLoad stepKey="waitForPageLoad6"/> <!--Verify that corresponding message is displayed--> <see userInput="A block identifier with the same properties already exists in the selected store." stepKey="VerifyBlockIsSaved1"/> + <!--Click to go back and add new block--> + <click selector="{{BlockNewPagePageActionsSection.back}}" stepKey="ClickToGoBack1"/> + <waitForPageLoad stepKey="waitForPageLoad7"/> + <click selector="{{BlockPageActionsSection.addNewBlock}}" stepKey="ClickToAddNewBlock2"/> + <waitForPageLoad stepKey="waitForPageLoad8"/> + <seeInCurrentUrl url="cms/block/new" stepKey="VerifyNewBlockPageIsOpened2"/> + <!--Add new BLock with the same data for another store view--> + <actionGroup ref="FillOutBlockContent" stepKey="FillOutBlockContent2"/> + <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="Default Store View" stepKey="selectDefaultStoreView" /> + <selectOption selector="{{BlockNewPageBasicFieldsSection.storeView}}" userInput="{{customStore.name}}" stepKey="selectSecondStoreView1" /> + <click selector="{{BlockNewPagePageActionsSection.saveBlock}}" stepKey="ClickToSaveBlock2"/> + <waitForPageLoad stepKey="waitForPageLoad9"/> + <see userInput="You saved the block." stepKey="VerifyBlockIsSaved2"/> <after> <actionGroup ref="AdminDeleteWebsiteActionGroup" stepKey="DeleteWebsite"> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml index 2f9347157ef33..a10545ba415fe 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminAddDefaultImageConfigurableTest.xml @@ -89,6 +89,9 @@ <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml index 98cb03916bdab..4288684e53d27 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckConfigurableProductAttributeValueUniquenessTest.xml @@ -34,6 +34,9 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteAttribute"/> <actionGroup ref="logout" stepKey="logOut"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product--> <comment userInput="Create configurable product" stepKey="createConfProd"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml index fabd4a2c253b6..0b3d013660b6e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckResultsOfColorAndOtherFiltersTest.xml @@ -110,6 +110,9 @@ <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductsGridFilter"/> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create three configurable products with options --> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml index 5bdccf15b19d3..92a6dcad27b30 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCheckValidatorConfigurableProductTest.xml @@ -44,6 +44,9 @@ <argument name="ProductAttribute" value="productDropDownAttribute"/> </actionGroup> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Find the product that we just created using the product grid --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml index 83e428b454c46..68afa17c4d539 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductDeleteTest.xml @@ -70,6 +70,9 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- assert product visible in storefront --> @@ -225,6 +228,9 @@ <deleteData createDataKey="createConfigChildProduct6" stepKey="deleteConfigChildProduct6"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Search for prefix of the 3 products we created via api --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml index baea299581052..fc1f6dad36345 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductLongSkuTest.xml @@ -53,6 +53,9 @@ <!--Clean up category--> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create a configurable product with long name and sku--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml index 6bba4aa6b43ce..48a33bfd14d9c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductOutOfStockTest.xml @@ -83,6 +83,9 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Check to make sure that the configurable product shows up as in stock --> @@ -204,6 +207,9 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Check to make sure that the configurable product shows up as in stock --> @@ -306,6 +312,9 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Check to make sure that the configurable product shows up as in stock --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml index 889ca5b24b242..a73a7082502d8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductSearchTest.xml @@ -73,6 +73,9 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> @@ -152,6 +155,9 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="goToProductList"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml index c437b39a405cd..e95a73dcebba0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateAttributeTest.xml @@ -105,6 +105,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createModifiableProductAttribute" stepKey="deleteModifiableProductAttribute"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Get the current option of the attribute before it was changed --> @@ -221,6 +224,9 @@ <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct1"/> <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Find the product that we just created using the product grid --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index da0c5a65f944d..1eb1d8df6030c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -145,6 +145,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--check storefront for both options--> @@ -238,6 +241,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--check storefront for both options--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml index f64ba4a63cec9..d3db180cb7999 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithDisabledChildrenProductsTest.xml @@ -54,6 +54,9 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml index b448131e75f15..37d5d515bb9e0 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithImagesTest.xml @@ -57,6 +57,9 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml index 1c6908818b4da..299fbca412fac 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDisplayOutOfStockProductsTest.xml @@ -77,6 +77,9 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml index 1b7fc2c153208..0aa0f22d6af2e 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithThreeProductDontDisplayOutOfStockProductsTest.xml @@ -76,6 +76,9 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml index e67788d5e7bb0..a2cd949a77d13 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminCreateConfigurableProductWithTierPriceForOneItemTest.xml @@ -65,6 +65,9 @@ <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create configurable product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml index f1535d62861ac..f441579a6ef42 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminProductTypeSwitchingOnEditingTest.xml @@ -44,6 +44,9 @@ </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Add configurations to product--> <comment userInput="Add configurations to product" stepKey="commentAddConfigs"/> @@ -148,6 +151,9 @@ </actionGroup> <actionGroup ref="AdminClearFiltersActionGroup" stepKey="clearProductFilters"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Add configurations to product--> <comment userInput="Add configurations to product" stepKey="commentAddConfigurations"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml index 710430cf123dc..d764876d3b5cc 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRelatedProductsTest.xml @@ -89,6 +89,9 @@ <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <comment userInput="Filter and edit simple product 1" stepKey="filterAndEditComment1"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml index 0cc73f117aaad..e0c4fda005666 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminRemoveDefaultImageConfigurableTest.xml @@ -89,6 +89,8 @@ <deleteData createDataKey="baseConfigProductHandle" stepKey="deleteConfig"/> <deleteData createDataKey="categoryHandle" stepKey="deleteCategory"/> <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductAttribute"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="productIndexPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml index 0370280309272..05a9222eacaf9 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdvanceCatalogSearchConfigurableTest.xml @@ -82,6 +82,10 @@ <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> </test> <test name="AdvanceCatalogSearchConfigurableBySkuTest" extends="AdvanceCatalogSearchSimpleProductBySkuTest"> @@ -158,6 +162,10 @@ <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> </test> <test name="AdvanceCatalogSearchConfigurableByDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByDescriptionTest"> @@ -234,6 +242,10 @@ <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> </test> <test name="AdvanceCatalogSearchConfigurableByShortDescriptionTest" extends="AdvanceCatalogSearchSimpleProductByShortDescriptionTest"> @@ -310,6 +322,10 @@ <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 124f0eea2e77a..7bb8661627b8c 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -78,6 +78,8 @@ <argument name="websiteName" value="Second Website"/> </actionGroup> <actionGroup ref="logout" stepKey="adminLogout"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="EnableWebUrlOptionsActionGroup" stepKey="addStoreCodeToUrls"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 59bb7f53f0aa8..a4904c67e0ef8 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -73,6 +73,9 @@ <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Verify Configurable Product in checkout cart items --> @@ -282,6 +285,9 @@ <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Verify Configurable Product in checkout cart items --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index dd0673563838e..3d797e62c806a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -73,6 +73,9 @@ <!--<deleteData createDataKey="createConfigProductImage" stepKey="deleteConfigProductImage"/>--> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Verify Configurable Product in checkout cart items --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml index eadc7dadaf708..5caba34def165 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NewProductsListWidgetConfigurableProductTest.xml @@ -70,6 +70,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- A Cms page containing the New Products Widget gets created here via extends --> @@ -88,4 +91,4 @@ <waitForPageLoad stepKey="waitForCmsPage"/> <see selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductName"/> </test> -</tests> \ No newline at end of file +</tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml index 5224cc6a9cced..0e46027a69378 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/NoOptionAvailableToConfigureDisabledProductTest.xml @@ -103,6 +103,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCustomer" stepKey="deleteCreatedCustomer"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Disable child product --> <comment userInput="Disable child product" stepKey="disableChildProduct"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml index a8e982475253f..020e5dbbdfc77 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchConfigurableBySkuWithHyphenTest.xml @@ -83,6 +83,10 @@ <deleteData createDataKey="simple2Handle" stepKey="deleteSimple2" before="delete"/> <deleteData createDataKey="childProductHandle1" stepKey="deleteChildProduct1" before="delete"/> <deleteData createDataKey="childProductHandle2" stepKey="deleteChildProduct2" before="delete"/> + <deleteData createDataKey="productAttributeHandle" stepKey="deleteProductDropDownAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml index 294ab9fd0664d..a0f3689b9c170 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductChildSearchTest.xml @@ -140,6 +140,9 @@ <deleteData createDataKey="createConfigProductAttributeMultiSelect" stepKey="deleteConfigProductAttributeMultiSelect"/> <deleteData createDataKey="createConfigProductAttributeSelect" stepKey="deleteConfigProductAttributeSelect"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Quick search the storefront for the first attribute option --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml index 42b9dfc92760a..5d5481938f404 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductDetailsTest.xml @@ -220,6 +220,9 @@ <deleteData createDataKey="createSecondAttribute" stepKey="deleteSecondAttribute"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearProductsGridFilters"/> <actionGroup ref="logout" stepKey="adminLogout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductIndexPage"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml index 56e5bedc9eab1..324050608829a 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontShouldSeeOnlyConfigurableProductChildAssignedToSeparateCategoryTest.xml @@ -95,6 +95,9 @@ <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> <deleteData createDataKey="secondCategory" stepKey="deleteSecondCategory"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Go to the product page for the first product --> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml index 15df342ca47a8..ce5bad8c333a6 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontSortingByPriceForConfigurableWithCatalogRuleAppliedTest.xml @@ -99,8 +99,8 @@ <argument name="option" value="Yes"/> </actionGroup> <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllRules"/> - <!-- TODO: Replace this with CliRunReindexUsingCronJobsActionGroup after MC-29943 delivered--> - <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext catalog_category_product" stepKey="reindexIndices"/> + + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </before> <after> @@ -123,8 +123,8 @@ <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllRules"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> - <!-- TODO: Replace this with CliRunReindexUsingCronJobsActionGroup after MC-29943 delivered--> - <magentoCLI command="indexer:reindex" arguments="catalogsearch_fulltext catalog_category_product" stepKey="reindexIndices"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Open category with products and Sort by price desc--> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml index 3ebd9d6ef5367..adaa302a23a52 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontVerifyConfigurableProductLayeredNavigationTest.xml @@ -117,6 +117,9 @@ <deleteData createDataKey="createConfigChildProduct3" stepKey="deleteConfigChildProduct3"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteAttribute"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Open Product Index Page and Filter First Child product --> diff --git a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php index 285d37a1b6270..ed4d4a6fde51d 100644 --- a/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php +++ b/app/code/Magento/Csp/Model/Collector/CspWhitelistXml/SchemaLocator.php @@ -22,7 +22,7 @@ class SchemaLocator implements SchemaLocatorInterface * * @var string */ - private $schema ; + private $schema; /** * @param Reader $moduleReader diff --git a/app/code/Magento/Csp/Test/Unit/Observer/RenderTest.php b/app/code/Magento/Csp/Test/Unit/Observer/RenderTest.php new file mode 100644 index 0000000000000..8c42a4d051bde --- /dev/null +++ b/app/code/Magento/Csp/Test/Unit/Observer/RenderTest.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Csp\Test\Unit\Observer; + +use Magento\Csp\Api\CspRendererInterface; +use Magento\Csp\Observer\Render; +use Magento\Framework\App\Response\Http as ResponseHttp; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit Test for \Magento\Csp\Observer\Render + */ +class RenderTest extends TestCase +{ + /** + * Check if the render method is called + */ + public function testExecuteExpectsRenderCalled() + { + $eventMock = $this->createMock(Event::class); + $responseMock = $this->createMock(ResponseHttp::class); + $eventMock->expects($this->once()) + ->method('getData') + ->with('response') + ->willReturn($responseMock); + + /** @var MockObject|Observer $eventObserverMock */ + $eventObserverMock = $this->createMock(Observer::class); + $eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + + $cspRendererMock = $this->createMock(CspRendererInterface::class); + $cspRendererMock->expects($this->once())->method('render'); + + $objectManagerHelper = new ObjectManager($this); + /** @var Render $renderObserver */ + $renderObserver = $objectManagerHelper->getObject( + Render::class, + ['cspRenderer' => $cspRendererMock] + ); + $renderObserver->execute($eventObserverMock); + } +} diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index b85b735ea9c4f..a65bfa5d77f9e 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -348,8 +348,14 @@ public function execute() ['customer' => $customer, 'request' => $this->getRequest()] ); - if (isset($customerData['sendemail_store_id'])) { + if (isset($customerData['sendemail_store_id']) && $customerData['sendemail_store_id'] !== false) { $customer->setStoreId($customerData['sendemail_store_id']); + try { + $this->customerAccountManagement->validateCustomerStoreIdByWebsiteId($customer); + } catch (LocalizedException $exception) { + throw new LocalizedException(__("The Store View selected for sending Welcome email from". + " is not related to the customer's associated website.")); + } } // Save customer diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 55da6a62f0625..e2d997ed445b8 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -872,7 +872,6 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash if ($customer->getId()) { $customer = $this->customerRepository->get($customer->getEmail()); $websiteId = $customer->getWebsiteId(); - if ($this->isCustomerInStore($websiteId, $customer->getStoreId())) { throw new InputException(__('This customer already exists in this store.')); } @@ -896,13 +895,10 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash $customer->setWebsiteId($websiteId); } + $this->validateCustomerStoreIdByWebsiteId($customer); + // Update 'created_in' value with actual store name if ($customer->getId() === null) { - $websiteId = $customer->getWebsiteId(); - if ($websiteId && !$this->isCustomerInStore($websiteId, $customer->getStoreId())) { - throw new LocalizedException(__('The store view is not in the associated website.')); - } - $storeName = $this->storeManager->getStore($customer->getStoreId())->getName(); $customer->setCreatedIn($storeName); } @@ -1144,6 +1140,22 @@ public function isCustomerInStore($customerWebsiteId, $storeId) return in_array($storeId, $ids); } + /** + * Validate customer store id by customer website id. + * + * @param CustomerInterface $customer + * @return bool + * @throws LocalizedException + */ + public function validateCustomerStoreIdByWebsiteId(CustomerInterface $customer) + { + if (!$this->isCustomerInStore($customer->getWebsiteId(), $customer->getStoreId())) { + throw new LocalizedException(__('The store view is not in the associated website.')); + } + + return true; + } + /** * Validate the Reset Password Token for a customer. * diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.xml new file mode 100644 index 0000000000000..eb66cafe43dd3 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertStorefrontDefaultWelcomeMessageActionGroup.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="AssertStorefrontDefaultWelcomeMessageActionGroup"> + <annotations> + <description>Validates that the Welcome message is present and correct and not you link absent.</description> + </annotations> + + <waitForElementVisible selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="waitDefaultMessage"/> + <see userInput="Default welcome msg!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="verifyDefaultMessage"/> + <dontSeeElement selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="checkAbsenceLinkNotYou"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml index ed221350918a0..98a7f4c8d1544 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontCustomerLogoutActionGroup.xml @@ -24,7 +24,7 @@ <click selector="{{StoreFrontSignOutSection.customerAccount}}" stepKey="clickCustomerButton"/> <click selector="{{StoreFrontSignOutSection.signOut}}" stepKey="clickToSignOut"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <see userInput="You are signed out" stepKey="signOut"/> + <waitForText selector="{{StorefrontCMSPageSection.mainTitle}}" userInput="You are signed out" stepKey="signOut"/> + <waitForText selector="{{StorefrontCMSPageSection.mainTitle}}" userInput="Home Page" stepKey="waitForHomePageLoad"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml index 40b05153f1a74..8651c9b4db62e 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontClearAllCompareProductsTest.xml @@ -18,7 +18,7 @@ <group value="catalog"/> <group value="mtf_migrated"/> </annotations> - + <before> <magentoCLI stepKey="addDownloadableDomain" command="downloadable:domains:add static.magento.com"/> <!-- Create Simple Customer --> @@ -124,6 +124,10 @@ <deleteData createDataKey="createBundleProduct1" stepKey="deleteBundleProduct1"/> <deleteData createDataKey="createGroupedProduct1" stepKey="deleteGroupedProduct1"/> <deleteData createDataKey="createDownloadableProduct1" stepKey="deleteDownloadableProduct1"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer1"> diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 2e729873961c0..51663861fc8d1 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -269,7 +269,7 @@ protected function setUp() ->getMock(); $this->managementMock = $this->getMockBuilder(AccountManagement::class) ->disableOriginalConstructor() - ->setMethods(['createAccount']) + ->setMethods(['createAccount', 'validateCustomerStoreIdByWebsiteId']) ->getMock(); $this->addressDataFactoryMock = $this->getMockBuilder(AddressInterfaceFactory::class) ->disableOriginalConstructor() @@ -522,6 +522,9 @@ public function testExecuteWithExistentCustomer() ->with('customer/*/edit', ['id' => $customerId, '_current' => true]) ->willReturn(true); + $this->managementMock->method('validateCustomerStoreIdByWebsiteId') + ->willReturn(true); + $this->assertEquals($redirectMock, $this->model->execute()); } diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 3c38cd0f7b4e2..2344e0c8bce02 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -329,7 +329,6 @@ public function testCreateAccountWithPasswordHashWithExistingCustomer() public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -359,9 +358,9 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -378,9 +377,7 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share - ->expects($this->atLeastOnce()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager ->expects($this->atLeastOnce()) @@ -405,7 +402,6 @@ public function testCreateAccountWithPasswordHashWithCustomerWithoutStoreId() public function testCreateAccountWithPasswordHashWithLocalizedException() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -419,8 +415,7 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() ->method('getId') ->willReturn($defaultStoreId); $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); - $website->expects($this->once()) - ->method('getStoreIds') + $website->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) ->method('getDefaultStore') @@ -435,9 +430,9 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -454,9 +449,7 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share - ->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager ->expects($this->atLeastOnce()) @@ -481,7 +474,6 @@ public function testCreateAccountWithPasswordHashWithLocalizedException() public function testCreateAccountWithPasswordHashWithAddressException() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -498,8 +490,7 @@ public function testCreateAccountWithPasswordHashWithAddressException() ->method('getId') ->willReturn($defaultStoreId); $website = $this->getMockBuilder(\Magento\Store\Model\Website::class)->disableOriginalConstructor()->getMock(); - $website->expects($this->once()) - ->method('getStoreIds') + $website->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) ->method('getDefaultStore') @@ -514,9 +505,9 @@ public function testCreateAccountWithPasswordHashWithAddressException() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -533,9 +524,7 @@ public function testCreateAccountWithPasswordHashWithAddressException() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share - ->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager ->expects($this->atLeastOnce()) @@ -648,7 +637,6 @@ public function testCreateAccountWithPasswordHashWithNewCustomerAndLocalizedExce public function testCreateAccountWithoutPassword() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -683,9 +671,8 @@ public function testCreateAccountWithoutPassword() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) - ->method('getStoreId') - ->willReturn($storeId); + $customer->expects($this->at(10))->method('getStoreId') + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -700,8 +687,7 @@ public function testCreateAccountWithoutPassword() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager->expects($this->atLeastOnce()) ->method('getWebsite') @@ -861,7 +847,6 @@ public function testCreateAccountInputExceptionExtraLongPassword() public function testCreateAccountWithPassword() { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -940,9 +925,9 @@ public function testCreateAccountWithPassword() $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(11)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -957,8 +942,7 @@ public function testCreateAccountWithPassword() ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager->expects($this->atLeastOnce()) ->method('getWebsite') @@ -1810,62 +1794,37 @@ public function dataProviderGetConfirmationStatus() /** * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Exception message */ - public function testCreateAccountWithPasswordHashForGuest() + public function testCreateAccountWithPasswordHashForGuestException() { $storeId = 1; - $storeName = 'store_name'; $websiteId = 1; $hash = '4nj54lkj5jfi03j49f8bgujfgsd'; $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) ->disableOriginalConstructor() ->getMock(); - $storeMock->expects($this->once()) - ->method('getId') + $storeMock->method('getId') ->willReturn($storeId); - $storeMock->expects($this->once()) - ->method('getWebsiteId') - ->willReturn($websiteId); - $storeMock->expects($this->once()) - ->method('getName') - ->willReturn($storeName); - - $this->storeManager->expects($this->exactly(3)) - ->method('getStore') - ->willReturn($storeMock); + $this->storeManager->method('getStores') + ->willReturn([$storeMock]); $customerMock = $this->getMockBuilder(Customer::class) ->disableOriginalConstructor() ->getMock(); - $customerMock->expects($this->exactly(2)) - ->method('getId') - ->willReturn(null); - $customerMock->expects($this->exactly(3)) + $customerMock->expects($this->at(1)) ->method('getStoreId') - ->willReturn(null); - $customerMock->expects($this->exactly(3)) + ->willReturn($storeId); + $customerMock->expects($this->at(4)) + ->method('getStoreId') + ->willReturn($storeId); + $customerMock->expects($this->at(2)) ->method('getWebsiteId') - ->willReturn(null); - $customerMock->expects($this->once()) - ->method('setStoreId') - ->with($storeId) - ->willReturnSelf(); - $customerMock->expects($this->once()) - ->method('setWebsiteId') - ->with($websiteId) - ->willReturnSelf(); - $customerMock->expects($this->once()) - ->method('setCreatedIn') - ->with($storeName) - ->willReturnSelf(); - $customerMock->expects($this->once()) - ->method('getAddresses') - ->willReturn(null); - $customerMock->expects($this->once()) - ->method('setAddresses') - ->with(null) - ->willReturnSelf(); + ->willReturn($websiteId); + $customerMock->expects($this->at(5)) + ->method('getId') + ->willReturn(1); $this->customerRepository ->expects($this->once()) @@ -2030,7 +1989,6 @@ private function prepareDateTimeFactory() public function testCreateAccountUnexpectedValueException(): void { $websiteId = 1; - $storeId = null; $defaultStoreId = 1; $customerId = 1; $customerEmail = 'email@email.com'; @@ -2048,8 +2006,7 @@ public function testCreateAccountUnexpectedValueException(): void ->method('getId') ->willReturn($defaultStoreId); $website = $this->createMock(\Magento\Store\Model\Website::class); - $website->expects($this->atLeastOnce()) - ->method('getStoreIds') + $website->method('getStoreIds') ->willReturn([1, 2, 3]); $website->expects($this->once()) ->method('getDefaultStore') @@ -2064,9 +2021,9 @@ public function testCreateAccountUnexpectedValueException(): void $customer->expects($this->atLeastOnce()) ->method('getWebsiteId') ->willReturn($websiteId); - $customer->expects($this->atLeastOnce()) + $customer->expects($this->at(10)) ->method('getStoreId') - ->willReturn($storeId); + ->willReturn(1); $customer->expects($this->once()) ->method('setStoreId') ->with($defaultStoreId); @@ -2080,8 +2037,7 @@ public function testCreateAccountUnexpectedValueException(): void ->method('get') ->with($customerEmail) ->willReturn($customer); - $this->share->expects($this->once()) - ->method('isWebsiteScope') + $this->share->method('isWebsiteScope') ->willReturn(true); $this->storeManager->expects($this->atLeastOnce()) ->method('getWebsite') @@ -2162,4 +2118,49 @@ public function testCreateAccountWithStoreNotInWebsite() ->willReturn($website); $this->accountManagement->createAccountWithPasswordHash($customerMock, $hash); } + + /** + * Test for validating customer store id by customer website id. + * + * @return void + */ + public function testValidateCustomerStoreIdByWebsiteId(): void + { + $customerMock = $this->getMockBuilder(CustomerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $customerMock->method('getWebsiteId')->willReturn(1); + $customerMock->method('getStoreId')->willReturn(1); + $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->disableOriginalConstructor() + ->getMock(); + $storeMock->method('getId') + ->willReturn(1); + $this->storeManager->method('getStores') + ->willReturn([$storeMock]); + + $this->assertTrue($this->accountManagement->validateCustomerStoreIdByWebsiteId($customerMock)); + } + + /** + * Test for validating customer store id by customer website id with Exception + * + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage The store view is not in the associated website. + */ + public function testValidateCustomerStoreIdByWebsiteIdException(): void + { + $customerMock = $this->getMockBuilder(CustomerInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) + ->disableOriginalConstructor() + ->getMock(); + $storeMock->method('getId') + ->willReturn(1); + $this->storeManager->method('getStores') + ->willReturn([$storeMock]); + + $this->assertTrue($this->accountManagement->validateCustomerStoreIdByWebsiteId($customerMock)); + } } diff --git a/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/StoreTest.php b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/StoreTest.php new file mode 100644 index 0000000000000..2e34bcf7ab698 --- /dev/null +++ b/app/code/Magento/Customer/Test/Unit/ViewModel/Customer/StoreTest.php @@ -0,0 +1,210 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\Test\Unit\ViewModel\Customer; + +use Magento\Customer\Model\Config\Share as ConfigShare; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Store\Model\StoreManagerInterface; +use PHPUnit\Framework\TestCase; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Customer\ViewModel\Customer\Store as CustomerStore; +use Magento\Store\Model\System\Store as SystemStore; +use Magento\Store\Model\Store; + +/** + * Test for customer's store view model + */ +class StoreTest extends TestCase +{ + /** @var ObjectManagerHelper */ + private $objectManagerHelper; + + /** + * @var CustomerStore + */ + private $customerStore; + + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var Store + */ + private $store; + + /** + * @var ConfigShare + */ + protected $configShare; + + /** + * @var StoreManagerInterface + */ + protected $storeManager; + + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + + protected function setUp() + { + $this->systemStore = $this->createMock(SystemStore::class); + $this->store = $this->createMock(Store::class); + $this->configShare = $this->createMock(ConfigShare::class); + $this->storeManager = $this->createMock(StoreManagerInterface::class); + $this->dataPersistor = $this->createMock(DataPersistorInterface::class); + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->customerStore = $this->objectManagerHelper->getObject( + CustomerStore::class, + [ + 'systemStore' => $this->systemStore, + 'configShare' => $this->configShare, + 'storeManager' => $this->storeManager, + 'dataPersistor' => $this->dataPersistor + ] + ); + } + + /** + * Test that method return correct array of options + * + * @param array $options + * @param bool $isWebsiteScope + * @param bool $isCustomerDataInSession + * @dataProvider dataProviderOptionsArray + * @return void + */ + public function testToOptionArray(array $options, bool $isWebsiteScope, bool $isCustomerDataInSession): void + { + $this->configShare->method('isWebsiteScope') + ->willReturn($isWebsiteScope); + $this->store->method('getWebsiteId') + ->willReturn(1); + + if ($isCustomerDataInSession) { + $this->dataPersistor->method('get') + ->with('customer') + ->willReturn([ + 'account' => ['website_id' => '1'] + ]); + } else { + $this->storeManager->method('getDefaultStoreView') + ->willReturn($this->store); + } + + $this->systemStore->method('getStoreData') + ->willReturn($this->store); + $this->systemStore->method('getStoreValuesForForm') + ->willReturn([ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + ] + ], + '__disableTmpl' => true, + ] + ]); + + $this->assertEquals($options, $this->customerStore->toOptionArray()); + } + + /** + * Data provider for testToOptionArray test + * + * @return array + */ + public function dataProviderOptionsArray(): array + { + return [ + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + 'website_id' => '1', + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + 'website_id' => '1', + ] + ], + '__disableTmpl' => true, + 'website_id' => '1', + ] + ], + 'isWebsiteScope' => true, + 'isCustomerDataInSession' => false, + ], + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + 'website_id' => '1', + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + 'website_id' => '1', + ] + ], + '__disableTmpl' => true, + 'website_id' => '1', + ] + ], + 'isWebsiteScope' => false, + 'isCustomerDataInSession' => false, + ], + [ + 'options' => [ + [ + 'label' => 'Main Website', + 'value' => [], + '__disableTmpl' => true, + 'website_id' => '1', + ], + [ + 'label' => 'Main Website', + 'value' => [ + [ + 'label' => '    Default Store View', + 'value' => '1', + 'website_id' => '1', + ] + ], + '__disableTmpl' => true, + 'website_id' => '1', + ] + ], + 'isWebsiteScope' => false, + 'isCustomerDataInSession' => true, + ] + ]; + } +} diff --git a/app/code/Magento/Customer/ViewModel/Customer/Store.php b/app/code/Magento/Customer/ViewModel/Customer/Store.php new file mode 100644 index 0000000000000..1e6ca69e2d77a --- /dev/null +++ b/app/code/Magento/Customer/ViewModel/Customer/Store.php @@ -0,0 +1,131 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Customer\ViewModel\Customer; + +use Magento\Customer\Model\Config\Share as ConfigShare; +use Magento\Framework\App\Request\DataPersistorInterface; +use Magento\Framework\Data\OptionSourceInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Store\Model\System\Store as SystemStore; + +/** + * Customer's store view model + */ +class Store implements OptionSourceInterface +{ + /** + * @var SystemStore + */ + private $systemStore; + + /** + * @var ConfigShare + */ + private $configShare; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + + /** + * Store constructor. + * + * @param SystemStore $systemStore + * @param ConfigShare $configShare + * @param StoreManagerInterface $storeManager + * @param DataPersistorInterface $dataPersistor + */ + public function __construct( + SystemStore $systemStore, + ConfigShare $configShare, + StoreManagerInterface $storeManager, + DataPersistorInterface $dataPersistor + ) { + $this->systemStore = $systemStore; + $this->configShare = $configShare; + $this->storeManager = $storeManager; + $this->dataPersistor = $dataPersistor; + } + + /** + * @inheritdoc + */ + public function toOptionArray(): array + { + return (bool)$this->configShare->isWebsiteScope() ? $this->getStoreOptions() + : $this->getStoreOptionsWithCurrentWebsiteId(); + } + + /** + * Adding website ID to options list + * + * @return array + */ + private function getStoreOptions(): array + { + $options = $this->systemStore->getStoreValuesForForm(); + + $websiteKey = null; + foreach ($options as $key => $option) { + if ($websiteKey === null) { + $websiteKey = $key; + } + if (is_array($option['value']) && !empty($option['value'])) { + $websiteId = null; + foreach ($option['value'] as $storeViewKey => $storeView) { + $websiteId = $this->systemStore->getStoreData($storeView['value'])->getWebsiteId(); + $options[$key]['value'][$storeViewKey]['website_id'] = $websiteId; + } + if ($websiteId) { + $options[$key]['website_id'] = $websiteId; + if ($websiteKey !== null) { + $options[$websiteKey]['website_id'] = $websiteId; + $websiteKey = null; + } + } + } + } + + return $options; + } + + /** + * Adding current website ID to options list + * + * @return array + */ + private function getStoreOptionsWithCurrentWebsiteId(): array + { + $options = $this->systemStore->getStoreValuesForForm(); + + if (!empty($this->dataPersistor->get('customer')['account'])) { + $currentWebsiteId = (string)$this->dataPersistor->get('customer')['account']['website_id']; + } else { + $currentWebsiteId = $this->storeManager->getDefaultStoreView()->getWebsiteId(); + } + + foreach ($options as $key => $option) { + $options[$key]['website_id'] = $currentWebsiteId; + if (is_array($option['value']) && !empty($option['value'])) { + foreach ($option['value'] as $storeViewKey => $storeView) { + $storeView['website_id'] = $currentWebsiteId; + $options[$key]['value'][$storeViewKey] = $storeView; + } + } + } + + return $options; + } +} diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv index f1b82bd5a4395..bb6c4b10bf75e 100644 --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -539,3 +539,5 @@ Addresses,Addresses "Middle Name/Initial","Middle Name/Initial" "Suffix","Suffix" "The Date of Birth should not be greater than today.","The Date of Birth should not be greater than today." +"The store view is not in the associated website.","The store view is not in the associated website." +"The Store View selected for sending Welcome email from is not related to the customer's associated website.","The Store View selected for sending Welcome email from is not related to the customer's associated website." diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index 7caaeab4f39d6..d5c7154a30f54 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -301,7 +301,7 @@ <visible>true</visible> </settings> </field> - <field name="sendemail_store_id" formElement="select"> + <field name="sendemail_store_id" component="Magento_Ui/js/form/element/select" formElement="select"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="source" xsi:type="string">customer</item> @@ -317,7 +317,11 @@ <formElements> <select> <settings> - <options class="Magento\Store\Model\System\Store"/> + <options class="Magento\Customer\ViewModel\Customer\Store"/> + <filterBy> + <field>website_id</field> + <target>${ $.provider }:${ $.parentScope }.website_id</target> + </filterBy> </settings> </select> </formElements> diff --git a/app/code/Magento/Directory/Test/Mftf/Data/GeneralStateOptionsConfigData.xml b/app/code/Magento/Directory/Test/Mftf/Data/GeneralStateOptionsConfigData.xml new file mode 100644 index 0000000000000..8b72e3e93efb9 --- /dev/null +++ b/app/code/Magento/Directory/Test/Mftf/Data/GeneralStateOptionsConfigData.xml @@ -0,0 +1,21 @@ +<?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="AllowToChooseStateIfItIsOptionalForCountryEnabled"> + <data key="path">general/region/display_all</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="AllowToChooseStateIfItIsOptionalForCountryDisabled"> + <data key="path">general/region/display_all</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/Directory/Test/Unit/Block/Adminhtml/Frontend/Currency/BaseTest.php b/app/code/Magento/Directory/Test/Unit/Block/Adminhtml/Frontend/Currency/BaseTest.php new file mode 100644 index 0000000000000..9d38a2c72f3ac --- /dev/null +++ b/app/code/Magento/Directory/Test/Unit/Block/Adminhtml/Frontend/Currency/BaseTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Directory\Test\Unit\Block\Adminhtml\Frontend\Currency; + +use Magento\Directory\Block\Adminhtml\Frontend\Currency\Base; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\RequestInterface; +use Magento\Framework\Data\Form\Element\AbstractElement; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Unit test for \Magento\Directory\Block\Adminhtml\Frontend\Currency\Base + */ +class BaseTest extends TestCase +{ + const STUB_WEBSITE_PARAM = 'website'; + + /** + * @var AbstractElement|MockObject + */ + private $elementMock; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Base + */ + private $baseCurrency; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->elementMock = $this->createMock(AbstractElement::class); + $this->scopeConfigMock = $this->createMock(ScopeConfigInterface::class); + $this->requestMock = $this->getMockBuilder(RequestInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getParam']) + ->getMockForAbstractClass(); + + $this->baseCurrency = (new ObjectManagerHelper($this))->getObject( + Base::class, + ['_request' => $this->requestMock, '_scopeConfig' => $this->scopeConfigMock] + ); + } + + /** + * Test case when no Website param provided + */ + public function testRenderWithoutWebsiteParam() + { + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturn(''); + $this->scopeConfigMock->expects($this->never())->method('getValue'); + + $result = $this->baseCurrency->render(($this->elementMock)); + $this->assertFalse(empty($result), 'Result should not be empty.'); + } + + /** + * Test case when Website param is provided and Price Scope is set to Global + */ + public function testRenderWhenWebsiteParamSetAndPriceScopeGlobal() + { + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturn(self::STUB_WEBSITE_PARAM); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->willReturn(Store::PRICE_SCOPE_GLOBAL); + + $result = $this->baseCurrency->render(($this->elementMock)); + $this->assertEquals('', $result, 'Result should be an empty string.'); + } + + /** + * Test case when Website param is provided and Price Scope is not Global + */ + public function testRenderWhenWebsiteParamSetAndPriceScopeOther() + { + $this->requestMock->expects($this->once()) + ->method('getParam') + ->willReturn(self::STUB_WEBSITE_PARAM); + $this->scopeConfigMock->expects($this->once()) + ->method('getValue') + ->willReturn(Store::PRICE_SCOPE_WEBSITE); + + $result = $this->baseCurrency->render(($this->elementMock)); + $this->assertFalse(empty($result), 'Result should not be empty.'); + } +} diff --git a/app/code/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/Grouped.php b/app/code/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/Grouped.php new file mode 100644 index 0000000000000..6df890b3e94dc --- /dev/null +++ b/app/code/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/Grouped.php @@ -0,0 +1,40 @@ +<?php +/** + * Order Email items grouped renderer + * + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\GroupedProduct\Block\Order\Email\Items\CreditMemo; + +use Magento\Sales\Block\Order\Email\Items\DefaultItems; + +/** + * Class renders grouped product(s) in the CreditMemo email + * + * @api + */ +class Grouped extends DefaultItems +{ + /** + * Prepare item html + * + * This method uses renderer for real product type + * + * @return string + */ + protected function _toHtml() + { + if ($this->getItem()->getOrderItem()) { + $item = $this->getItem()->getOrderItem(); + } else { + $item = $this->getItem(); + } + if ($productType = $item->getRealProductType()) { + $renderer = $this->getRenderedBlock()->getItemRenderer($productType); + $renderer->setItem($this->getItem()); + return $renderer->toHtml(); + } + return parent::_toHtml(); + } +} diff --git a/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml b/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml index 77875ac70427c..2e64b3047cbbb 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml +++ b/app/code/Magento/GroupedProduct/view/frontend/layout/sales_email_order_creditmemo_renderers.xml @@ -8,7 +8,7 @@ <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd" label="Email Creditmemo Items List" design_abstraction="custom"> <body> <referenceBlock name="sales.email.order.creditmemo.renderers"> - <block class="Magento\GroupedProduct\Block\Order\Email\Items\Order\Grouped" name="sales.email.order.creditmemo.renderers.grouped" as="grouped" template="Magento_Sales::email/items/creditmemo/default.phtml"/> + <block class="Magento\GroupedProduct\Block\Order\Email\Items\CreditMemo\Grouped" name="sales.email.order.creditmemo.renderers.grouped" as="grouped" template="Magento_Sales::email/items/creditmemo/default.phtml"/> </referenceBlock> </body> </page> diff --git a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml index 6a2f6ca60acf4..f5e8c91c31950 100644 --- a/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml +++ b/app/code/Magento/ImportExport/Test/Mftf/Test/AdminCheckThatSomeAttributesChangedValueToEmptyAfterImportTest.xml @@ -42,6 +42,9 @@ <!--Delete attribute--> <deleteData createDataKey="productAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Create product--> <actionGroup ref="GoToSpecifiedCreateProductPageActionGroup" stepKey="openProductFillForm"/> diff --git a/app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml new file mode 100644 index 0000000000000..9dc7547db40d6 --- /dev/null +++ b/app/code/Magento/Indexer/Test/Mftf/ActionGroup/CliRunReindexUsingCronJobsActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="CliRunReindexUsingCronJobsActionGroup"> + <annotations> + <description>Run cron 'index' group which reindex all invalidated indices.</description> + </annotations> + + <magentoCLI command="cron:run" arguments="--group='index'" stepKey="firstRunToScheduleJobs"/> + <magentoCLI command="cron:run" arguments="--group='index'" stepKey="secondRunToExecuteJobs"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml index ccb724e8bf199..29f14185a0518 100644 --- a/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml +++ b/app/code/Magento/Msrp/Test/Mftf/Test/StorefrontProductWithMapAssignedConfigProductIsCorrectTest.xml @@ -105,6 +105,9 @@ <createData entity="MsrpDisableMAP" stepKey="disableMAP"/> <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Set Manufacturer's Suggested Retail Price to products--> diff --git a/app/code/Magento/PageCache/Observer/FlushAllCache.php b/app/code/Magento/PageCache/Observer/FlushAllCache.php index 0a45ebf265715..aca9fc5e2a8c0 100644 --- a/app/code/Magento/PageCache/Observer/FlushAllCache.php +++ b/app/code/Magento/PageCache/Observer/FlushAllCache.php @@ -1,18 +1,25 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Observer; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\PageCache\Cache; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +/** + * Observer used to flush all caches with built-in full page cache + */ class FlushAllCache implements ObserverInterface { /** - * @var \Magento\Framework\App\PageCache\Cache + * @var Cache * * @deprecated 100.1.0 */ @@ -21,48 +28,42 @@ class FlushAllCache implements ObserverInterface /** * Application config object * - * @var \Magento\PageCache\Model\Config + * @var Config */ protected $_config; /** - * @var \Magento\PageCache\Model\Cache\Type + * @var Type */ private $fullPageCache; /** - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\PageCache\Cache $cache + * @param Config $config + * @param Cache $cache + * @param Type $fullPageCache */ - public function __construct(\Magento\PageCache\Model\Config $config, \Magento\Framework\App\PageCache\Cache $cache) - { + public function __construct( + Config $config, + Cache $cache, + Type $fullPageCache + ) { $this->_config = $config; $this->_cache = $cache; + $this->fullPageCache = $fullPageCache; } /** * Flash Built-In cache - * @param \Magento\Framework\Event\Observer $observer + * + * @param Observer $observer + * * @return void * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - if ($this->_config->getType() == \Magento\PageCache\Model\Config::BUILT_IN) { - $this->getCache()->clean(); - } - } - - /** - * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547 - * - * @return \Magento\PageCache\Model\Cache\Type - */ - private function getCache() + public function execute(Observer $observer) { - if (!$this->fullPageCache) { - $this->fullPageCache = ObjectManager::getInstance()->get(\Magento\PageCache\Model\Cache\Type::class); + if ($this->_config->getType() == Config::BUILT_IN) { + $this->fullPageCache->clean(); } - return $this->fullPageCache; } } diff --git a/app/code/Magento/PageCache/Observer/FlushCacheByTags.php b/app/code/Magento/PageCache/Observer/FlushCacheByTags.php index 8ce26f7d31781..778557ee0dd2f 100644 --- a/app/code/Magento/PageCache/Observer/FlushCacheByTags.php +++ b/app/code/Magento/PageCache/Observer/FlushCacheByTags.php @@ -1,18 +1,27 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\PageCache\Observer; -use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\App\PageCache\Cache; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +use Zend_Cache; +/** + * Observer used to cache by tags when using built-in full page cache + */ class FlushCacheByTags implements ObserverInterface { /** - * @var \Magento\Framework\App\PageCache\Cache + * @var Cache * * @deprecated 100.1.0 */ @@ -21,78 +30,59 @@ class FlushCacheByTags implements ObserverInterface /** * Application config object * - * @var \Magento\PageCache\Model\Config + * @var Config */ protected $_config; /** - * @var \Magento\PageCache\Model\Cache\Type + * @var Type */ private $fullPageCache; /** * Invalidation tags resolver * - * @var \Magento\Framework\App\Cache\Tag\Resolver + * @var Resolver */ private $tagResolver; /** - * @param \Magento\PageCache\Model\Config $config - * @param \Magento\Framework\App\PageCache\Cache $cache + * @param Config $config + * @param Cache $cache + * @param Type $fullPageCache + * @param Resolver $tagResolver */ - public function __construct(\Magento\PageCache\Model\Config $config, \Magento\Framework\App\PageCache\Cache $cache) - { + public function __construct( + Config $config, + Cache $cache, + Type $fullPageCache, + Resolver $tagResolver + ) { $this->_config = $config; $this->_cache = $cache; + $this->fullPageCache = $fullPageCache; + $this->tagResolver = $tagResolver; } /** - * If Built-In caching is enabled it collects array of tags - * of incoming object and asks to clean cache. + * If Built-In caching is enabled it collects array of tags of incoming object and asks to clean cache. + * + * @param Observer $observer * - * @param \Magento\Framework\Event\Observer $observer * @return void */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { - if ($this->_config->getType() == \Magento\PageCache\Model\Config::BUILT_IN && $this->_config->isEnabled()) { + if ($this->_config->getType() == Config::BUILT_IN && $this->_config->isEnabled()) { $object = $observer->getEvent()->getObject(); if (!is_object($object)) { return; } - $tags = $this->getTagResolver()->getTags($object); + $tags = $this->tagResolver->getTags($object); if (!empty($tags)) { - $this->getCache()->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, array_unique($tags)); + $this->fullPageCache->clean(Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, array_unique($tags)); } } } - - /** - * TODO: Workaround to support backwards compatibility, will rework to use Dependency Injection in MAGETWO-49547 - * - * - * @return \Magento\PageCache\Model\Cache\Type - */ - private function getCache() - { - if (!$this->fullPageCache) { - $this->fullPageCache = ObjectManager::getInstance()->get(\Magento\PageCache\Model\Cache\Type::class); - } - return $this->fullPageCache; - } - - /** - * @deprecated 100.1.2 - * @return \Magento\Framework\App\Cache\Tag\Resolver - */ - private function getTagResolver() - { - if ($this->tagResolver === null) { - $this->tagResolver = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\App\Cache\Tag\Resolver::class); - } - return $this->tagResolver; - } } diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php index 27e1da5a9f144..a0fa99035c8b0 100644 --- a/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Observer/FlushAllCacheTest.php @@ -1,48 +1,61 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\PageCache\Test\Unit\Observer; -class FlushAllCacheTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\PageCache\Observer\FlushAllCache */ - private $_model; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +use Magento\PageCache\Observer\FlushAllCache; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Config */ - private $_configMock; +/** + * Test class for \Magento\PageCache\Observer\FlushAllCache + */ +class FlushAllCacheTest extends TestCase +{ + /** + * @var FlushAllCache + */ + private $model; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\PageCache\Cache */ - private $_cacheMock; + /** + * @var Config|MockObject + */ + private $configMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\Event\Observer */ + /** + * @var Observer|MockObject + */ private $observerMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */ + /** + * @var Type|MockObject + */ private $fullPageCacheMock; /** - * Set up all mocks and data for test + * @inheritDoc */ protected function setUp() { - $this->_configMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['getType', 'isEnabled']); - $this->_cacheMock = $this->createPartialMock(\Magento\Framework\App\PageCache\Cache::class, ['clean']); - $this->fullPageCacheMock = $this->createPartialMock(\Magento\PageCache\Model\Cache\Type::class, ['clean']); - $this->observerMock = $this->createMock(\Magento\Framework\Event\Observer::class); + $this->configMock = $this->createPartialMock(Config::class, ['getType', 'isEnabled']); + $this->fullPageCacheMock = $this->createPartialMock(Type::class, ['clean']); + $this->observerMock = $this->createMock(Observer::class); - $this->_model = new \Magento\PageCache\Observer\FlushAllCache( - $this->_configMock, - $this->_cacheMock + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + FlushAllCache::class, + [ + 'config' => $this->configMock, + 'fullPageCache' => $this->fullPageCacheMock + ] ); - - $reflection = new \ReflectionClass(\Magento\PageCache\Observer\FlushAllCache::class); - $reflectionProperty = $reflection->getProperty('fullPageCache'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->_model, $this->fullPageCacheMock); } /** @@ -50,15 +63,32 @@ protected function setUp() */ public function testExecute() { - $this->_configMock->expects( + $this->configMock->expects( $this->once() )->method( 'getType' - )->will( - $this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN) + )->willReturn( + Config::BUILT_IN ); $this->fullPageCacheMock->expects($this->once())->method('clean'); - $this->_model->execute($this->observerMock); + $this->model->execute($this->observerMock); + } + + /** + * Test case for flushing all the cache with varnish enabled + */ + public function testExecuteWithVarnish() + { + $this->configMock->expects( + $this->once() + )->method( + 'getType' + )->willReturn( + Config::VARNISH + ); + + $this->fullPageCacheMock->expects($this->never())->method('clean'); + $this->model->execute($this->observerMock); } } diff --git a/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php b/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php index 8019c6b2e810f..cc3df162fd35a 100644 --- a/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php +++ b/app/code/Magento/PageCache/Test/Unit/Observer/FlushCacheByTagsTest.php @@ -1,51 +1,66 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace Magento\PageCache\Test\Unit\Observer; -class FlushCacheByTagsTest extends \PHPUnit\Framework\TestCase -{ - /** @var \Magento\PageCache\Observer\FlushCacheByTags */ - protected $_model; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\Event; +use Magento\Framework\Event\Observer; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\PageCache\Model\Cache\Type; +use Magento\PageCache\Model\Config; +use Magento\PageCache\Observer\FlushCacheByTags; +use Magento\Store\Model\Store; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Config */ - protected $_configMock; +/** + * Test class for \Magento\PageCache\Observer\FlushCacheByTags + */ +class FlushCacheByTagsTest extends TestCase +{ + /** + * @var FlushCacheByTags + */ + private $model; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\PageCache\Cache */ - protected $_cacheMock; + /** + * @var Config|MockObject + */ + private $configMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\PageCache\Model\Cache\Type */ + /** + * @var Type|MockObject + */ private $fullPageCacheMock; - /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\Cache\Tag\Resolver */ - private $tagResolver; + /** + * @var Resolver|MockObject + */ + private $tagResolverMock; /** - * Set up all mocks and data for test + * @inheritDoc */ protected function setUp() { - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_configMock = $this->createPartialMock(\Magento\PageCache\Model\Config::class, ['getType', 'isEnabled']); - $this->_cacheMock = $this->createPartialMock(\Magento\Framework\App\PageCache\Cache::class, ['clean']); - $this->fullPageCacheMock = $this->createPartialMock(\Magento\PageCache\Model\Cache\Type::class, ['clean']); - - $this->_model = new \Magento\PageCache\Observer\FlushCacheByTags( - $this->_configMock, - $this->_cacheMock + $this->configMock = $this->createPartialMock(Config::class, ['getType', 'isEnabled']); + $this->fullPageCacheMock = $this->createPartialMock(Type::class, ['clean']); + + $this->tagResolverMock = $this->createMock(Resolver::class); + + $objectManager = new ObjectManager($this); + $this->model = $objectManager->getObject( + FlushCacheByTags::class, + [ + 'config' => $this->configMock, + 'fullPageCache' => $this->fullPageCacheMock, + 'tagResolver' => $this->tagResolverMock + ] ); - - $this->tagResolver = $this->createMock(\Magento\Framework\App\Cache\Tag\Resolver::class); - - $helper->setBackwardCompatibleProperty($this->_model, 'tagResolver', $this->tagResolver); - $reflection = new \ReflectionClass(\Magento\PageCache\Observer\FlushCacheByTags::class); - $reflectionProperty = $reflection->getProperty('fullPageCache'); - $reflectionProperty->setAccessible(true); - $reflectionProperty->setValue($this->_model, $this->fullPageCacheMock); } /** @@ -56,28 +71,28 @@ protected function setUp() */ public function testExecute($cacheState) { - $this->_configMock->expects($this->any())->method('isEnabled')->will($this->returnValue($cacheState)); - $observerObject = $this->createMock(\Magento\Framework\Event\Observer::class); - $observedObject = $this->createMock(\Magento\Store\Model\Store::class); + $this->configMock->method('isEnabled')->willReturn($cacheState); + $observerObject = $this->createMock(Observer::class); + $observedObject = $this->createMock(Store::class); if ($cacheState) { $tags = ['cache_1', 'cache_group']; $expectedTags = ['cache_1', 'cache_group']; - $eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); - $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($observedObject)); - $observerObject->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock)); - $this->_configMock->expects($this->once()) + $eventMock = $this->createPartialMock(Event::class, ['getObject']); + $eventMock->expects($this->once())->method('getObject')->willReturn($observedObject); + $observerObject->expects($this->once())->method('getEvent')->willReturn($eventMock); + $this->configMock->expects($this->once()) ->method('getType') - ->willReturn(\Magento\PageCache\Model\Config::BUILT_IN); - $this->tagResolver->expects($this->once())->method('getTags')->will($this->returnValue($tags)); + ->willReturn(Config::BUILT_IN); + $this->tagResolverMock->expects($this->once())->method('getTags')->willReturn($tags); $this->fullPageCacheMock->expects($this->once()) ->method('clean') ->with(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, $this->equalTo($expectedTags)); } - $result = $this->_model->execute($observerObject); + $result = $this->model->execute($observerObject); $this->assertNull($result); } @@ -92,28 +107,31 @@ public function flushCacheByTagsDataProvider() ]; } + /** + * Test case for cache invalidation with empty tags + */ public function testExecuteWithEmptyTags() { - $this->_configMock->expects($this->any())->method('isEnabled')->will($this->returnValue(true)); - $observerObject = $this->createMock(\Magento\Framework\Event\Observer::class); - $observedObject = $this->createMock(\Magento\Store\Model\Store::class); + $this->configMock->method('isEnabled')->willReturn(true); + $observerObject = $this->createMock(Observer::class); + $observedObject = $this->createMock(Store::class); $tags = []; - $eventMock = $this->createPartialMock(\Magento\Framework\Event::class, ['getObject']); - $eventMock->expects($this->once())->method('getObject')->will($this->returnValue($observedObject)); - $observerObject->expects($this->once())->method('getEvent')->will($this->returnValue($eventMock)); - $this->_configMock->expects( + $eventMock = $this->createPartialMock(Event::class, ['getObject']); + $eventMock->expects($this->once())->method('getObject')->willReturn($observedObject); + $observerObject->expects($this->once())->method('getEvent')->willReturn($eventMock); + $this->configMock->expects( $this->once() )->method( 'getType' - )->will( - $this->returnValue(\Magento\PageCache\Model\Config::BUILT_IN) + )->willReturn( + Config::BUILT_IN ); - $this->tagResolver->expects($this->once())->method('getTags')->will($this->returnValue($tags)); + $this->tagResolverMock->expects($this->once())->method('getTags')->willReturn($tags); $this->fullPageCacheMock->expects($this->never())->method('clean'); - $this->_model->execute($observerObject); + $this->model->execute($observerObject); } } diff --git a/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php b/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php index dc9a51b30cfea..72c35a192119f 100644 --- a/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php +++ b/app/code/Magento/Payment/Test/Unit/Gateway/Data/PaymentDataObjectTest.php @@ -5,46 +5,62 @@ */ namespace Magento\Payment\Test\Unit\Gateway\Data; -use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Payment\Gateway\Data\OrderAdapterInterface; +use Magento\Payment\Gateway\Data\PaymentDataObject; use Magento\Payment\Model\InfoInterface; +use PHPUnit\Framework\MockObject\MockObject; /** - * Class PaymentDataObjectTest + * Tests for PaymentDataObject */ class PaymentDataObjectTest extends \PHPUnit\Framework\TestCase { - /** @var PaymentDataObject */ + /** + * @var PaymentDataObject + */ protected $model; /** - * @var OrderAdapterInterface|\PHPUnit_Framework_MockObject_MockObject + * @var OrderAdapterInterface|MockObject */ protected $orderMock; /** - * @var InfoInterface|\PHPUnit_Framework_MockObject_MockObject + * @var InfoInterface|\MockObject */ protected $paymentMock; + /** + * @inheritdoc + */ protected function setUp() { - $this->orderMock = $this->getMockBuilder(\Magento\Payment\Gateway\Data\OrderAdapterInterface::class) + $this->orderMock = $this->getMockBuilder(OrderAdapterInterface::class) ->getMockForAbstractClass(); - $this->paymentMock = $this->getMockBuilder(\Magento\Payment\Model\InfoInterface::class) + $this->paymentMock = $this->getMockBuilder(InfoInterface::class) ->getMockForAbstractClass(); $this->model = new PaymentDataObject($this->orderMock, $this->paymentMock); } - public function testGetOrder() + /** + * Verify can get order + * + * @return void + */ + public function testGetOrder(): void { - $this->assertSame($this->orderMock, $this->model->getOrder()) ; + $this->assertSame($this->orderMock, $this->model->getOrder()); } - public function testGetPayment() + /** + * Verify can get payment + * + * @return void + */ + public function testGetPayment(): void { - $this->assertSame($this->paymentMock, $this->model->getPayment()) ; + $this->assertSame($this->paymentMock, $this->model->getPayment()); } } diff --git a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml index 392014d876e46..b7ebf7dab1c8b 100644 --- a/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml +++ b/app/code/Magento/Paypal/Test/Mftf/ActionGroup/StorefrontPayOrderOnPayPalCheckoutActionGroup.xml @@ -16,6 +16,7 @@ <argument name="productName" type="string"/> </arguments> <click selector="{{PayPalPaymentSection.cartIcon}}" stepKey="openCart"/> + <waitForPageLoad stepKey="waitForCartLoad"/> <seeElement selector="{{PayPalPaymentSection.itemName(productName)}}" stepKey="seeProductName"/> <click selector="{{PayPalPaymentSection.PayPalSubmitBtn}}" stepKey="clickPayPalSubmitBtn"/> <switchToPreviousTab stepKey="switchToPreviousTab"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml index af68a7611cd1d..a1610926b3f36 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Section/PayPalExpressCheckoutConfigSection.xml @@ -57,10 +57,10 @@ <element name="email" type="input" selector="//input[contains(@name, 'email') and not(contains(@style, 'display:none'))]"/> <element name="password" type="input" selector="//input[contains(@name, 'password') and not(contains(@style, 'display:none'))]"/> <element name="loginBtn" type="input" selector="button#btnLogin"/> - <element name="reviewUserInfo" type="text" selector="#reviewUserInfo"/> - <element name="cartIcon" type="text" selector="#transactionCart"/> - <element name="itemName" type="text" selector="//span[@title='{{productName}}']" parameterized="true"/> - <element name="PayPalSubmitBtn" type="text" selector="//input[@type='submit']"/> + <element name="reviewUserInfo" type="text" selector="[data-testid=personalized-banner-content]"/> + <element name="cartIcon" type="text" selector="[data-testid='header-show-cart-dropdown-btn']"/> + <element name="itemName" type="text" selector="//p[contains(@class,'CartDropdown_line') and text()='{{productName}}']" parameterized="true"/> + <element name="PayPalSubmitBtn" type="text" selector="#payment-submit-btn"/> <element name="nextButton" type="button" selector="#btnNext"/> <element name="continueButton" type="button" selector=".continueButton"/> </section> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml index 6adba94e96890..fea1cf3966b99 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/StorefrontPaypalSmartButtonInCheckoutPageTest.xml @@ -16,9 +16,6 @@ <description value="Users are able to place order using Paypal Smart Button"/> <severity value="CRITICAL"/> <testCaseId value="MC-13690"/> - <skip> - <issueId value="DEVOPS-3311"/> - </skip> <group value="paypal"/> </annotations> <before> diff --git a/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php b/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php index 9b5f5c9a126df..86dcd0e4bfc07 100644 --- a/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php +++ b/app/code/Magento/Quote/Model/Quote/Item/CartItemPersister.php @@ -6,14 +6,17 @@ namespace Magento\Quote\Model\Quote\Item; -use Magento\Quote\Api\Data\CartInterface; -use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\LocalizedException; -use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Quote\Api\Data\CartItemInterface; +/** + * Cart item save handler + */ class CartItemPersister { /** @@ -39,6 +42,8 @@ public function __construct( } /** + * Save cart item into cart + * * @param CartInterface $quote * @param CartItemInterface $item * @return CartItemInterface @@ -73,12 +78,13 @@ public function save(CartInterface $quote, CartItemInterface $item) $item = $quote->updateItem($itemId, $buyRequestData); } else { if ($item->getQty() !== $currentItem->getQty()) { + $currentItem->clearMessage(); $currentItem->setQty($qty); /** * Qty validation errors are stored as items message * @see \Magento\CatalogInventory\Model\Quote\Item\QuantityValidator::validate */ - if (!empty($currentItem->getMessage())) { + if (!empty($currentItem->getMessage()) && $currentItem->getHasError()) { throw new LocalizedException(__($currentItem->getMessage())); } } diff --git a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml index 6e31214b0dddf..020b495dc42a2 100644 --- a/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml +++ b/app/code/Magento/Quote/Test/Mftf/Test/StorefrontGuestCheckoutDisabledProductTest.xml @@ -87,6 +87,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductListing"/> <actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Step 1: Add simple product to shopping cart --> <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.name$$)}}" stepKey="amOnSimpleProductPage"/> diff --git a/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php b/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php index cd7a54455a994..279b2c4b4091f 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Product/Plugin/UpdateQuoteItemsTest.php @@ -15,7 +15,7 @@ class UpdateQuoteItemsTest extends \PHPUnit\Framework\TestCase /** * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\ResourceModel\Quote */ - private $quoteResource ; + private $quoteResource; protected function setUp() { diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php index 1b275c4d809cb..dd62411ea3454 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Status/NewStatus/Form.php @@ -100,7 +100,7 @@ protected function _addStoresFieldset($model, $form) 'fieldset_html_class' => 'store' ] ); - return ; + return; } $renderer = $this->getLayout()->createBlock( diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php index 8a760065439d6..598b204a33097 100644 --- a/app/code/Magento/Sales/Model/Order.php +++ b/app/code/Magento/Sales/Model/Order.php @@ -858,7 +858,7 @@ public function canEdit() */ public function canReorder() { - return $this->_canReorder(false); + return $this->_canReorder(); } /** diff --git a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php index 4dbc10308f3be..c6b40800d5160 100644 --- a/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php +++ b/app/code/Magento/Sales/Model/Order/Email/Sender/ShipmentSender.php @@ -17,8 +17,10 @@ use Magento\Framework\DataObject; /** - * Class ShipmentSender + * Class for shipment email notification sender * + * @deprecated since this class works only with the concrete model and no data interface + * @see \Magento\Sales\Model\Order\Shipment\Sender\EmailSender * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ShipmentSender extends Sender diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup.xml new file mode 100644 index 0000000000000..5b08ba30a8fec --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup.xml @@ -0,0 +1,24 @@ +<?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="AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup" extends="AddSimpleProductToOrderActionGroup"> + <annotations> + <description>Add product to order with custom option type file. Start on create order page.</description> + </annotations> + <arguments> + <argument name="file" type="string" defaultValue="{{TestImageNew.file}}" /> + </arguments> + + <remove keyForRemoval="fillProductQty"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad" after="selectProduct"/> + <fillField selector="{{AdminOrderFormCustomOptionsSection.quantity}}" userInput="{{productQty}}" stepKey="fillProductQty" after="waitForAjaxLoad"/> + <attachFile selector="{{AdminOrderFormCustomOptionsSection.file}}" userInput="{{file}}" stepKey="attachImageForOptional" after="fillProductQty"/> + <click selector="{{AdminOrderFormCustomOptionsSection.buttonOk}}" stepKey="clickButtonOK" after="attachImageForOptional"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminChangeCustomerOptionFileActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminChangeCustomerOptionFileActionGroup.xml new file mode 100644 index 0000000000000..fb9f68d861faf --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminChangeCustomerOptionFileActionGroup.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="AdminChangeCustomerOptionFileActionGroup"> + <annotations> + <description>Change custom option file on admin order page.</description> + </annotations> + <arguments> + <argument name="file" type="string" defaultValue="{{TestImageNew.file}}" /> + </arguments> + + <click selector="{{AdminOrderFormItemsSection.configure}}" stepKey="clickConfigure"/> + <waitForAjaxLoad stepKey="waitForAjaxLoad"/> + <click selector="{{AdminOrderFormCustomOptionsSection.linkChange}}" stepKey="clickLinkChange"/> + <waitForPageLoad stepKey="waitForChangeLoad"/> + <attachFile selector="{{AdminOrderFormCustomOptionsSection.file}}" userInput="{{file}}" stepKey="changeAttachImage"/> + <click selector="{{AdminOrderFormCustomOptionsSection.buttonOk}}" stepKey="clickButtonOK"/> + <waitForPageLoad stepKey="waitForCustomOptionApplied"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml index 680d44ebb34fe..a7e2b2ec1f0d9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/AdminOrderCreatePage.xml @@ -20,5 +20,6 @@ <section name="AdminOrderFormStoreSelectorSection"/> <section name="AdminOrderFormDiscountSection"/> <section name="AdminOrderFormMessagesSection"/> + <section name="AdminOrderFormCustomOptionsSection"/> </page> </pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormCustomOptionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormCustomOptionsSection.xml new file mode 100644 index 0000000000000..066aa4181e7ef --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormCustomOptionsSection.xml @@ -0,0 +1,16 @@ +<?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="AdminOrderFormCustomOptionsSection"> + <element name="quantity" type="input" selector="//input[@id='product_composite_configure_input_qty']"/> + <element name="file" type="file" selector="//input[@type='file'][contains(@class, 'product-custom-option')]" /> + <element name="buttonOk" type="button" selector="//button[contains(@class, 'action-primary')][@data-role='action']"/> + <element name="linkChange" type="text" selector="//div[contains(@class, 'entry-edit')]//a[contains(text(),'Change')]"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml index a15e176c943ab..df3a1a2b91219 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AddConfigurableProductToOrderFromShoppingCartTest.xml @@ -72,7 +72,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <magentoCLI command="cron:run --group=index" stepKey="runCron"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Login as customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml index b303364bbf324..5883d044e95a0 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml @@ -108,6 +108,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create new customer order --> <comment userInput="Create new customer order" stepKey="createNewCustomerOrderComment"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml index 2a841b04bd647..766839a50a862 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml @@ -98,6 +98,9 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create Order --> <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderPage"> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.xml new file mode 100644 index 0000000000000..200ac6e62ac15 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateOrderWithSimpleProductCustomOptionFileTest.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="AdminCreateOrderWithSimpleProductCustomOptionFileTest"> + <annotations> + <title value="Create Order with simple product with custom option."/> + <description value="Verify, admin able to change file for custom option during order creation."/> + <features value="Sales"/> + <severity value="MAJOR"/> + <group value="Sales"/> + </annotations> + <before> + <!--Create test data.--> + <createData entity="_defaultCategory" stepKey="category"/> + <createData entity="SimpleProduct" stepKey="simpleProduct"> + <requiredEntity createDataKey="category"/> + </createData> + <createData entity="Simple_US_Customer_CA" stepKey="customer"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!--Clean up created test data.--> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="category" stepKey="deleteCategory"/> + <deleteData createDataKey="customer" stepKey="deleteCustomer" /> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Add option to product.--> + <amOnPage url="{{AdminProductEditPage.url($simpleProduct.id$)}}" stepKey="navigateToProductEditPage"/> + <actionGroup ref="AddProductCustomOptionFileActionGroup" stepKey="addOption"> + <argument name="option" value="ProductOptionFile"/> + </actionGroup> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct"/> + <!--Create order.--> + <actionGroup ref="NavigateToNewOrderPageExistingCustomerActionGroup" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$customer$"/> + </actionGroup> + <actionGroup ref="AdminAddSimpleProductWithCustomOptionFileToOrderActionGroup" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$simpleProduct$"/> + <argument name="productQty" value="$simpleProduct.quantity$"/> + </actionGroup> + <!--Verify, admin able to change file for custom option.--> + <actionGroup ref="AdminChangeCustomerOptionFileActionGroup" stepKey="changeFile"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml index 65bba7512e228..dc269ff7a2f98 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSubmitConfigurableProductOrderTest.xml @@ -131,6 +131,9 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> </test> </tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml index 0fcfa483f1adf..5d017cc0caab8 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml @@ -90,6 +90,9 @@ <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomerIndexPage"/> <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearCustomerGridFilter"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!--Filter and Open the customer edit page --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml index 980d5cc82b93d..27b0952781250 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveConfigurableProductsInComparedOnOrderPageTest.xml @@ -101,6 +101,9 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Login as customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml index b3c3f045f0d59..65c631b95f5c2 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml @@ -66,6 +66,9 @@ <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create order --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml index aed7447050e5f..c7c08914fea4b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml @@ -80,6 +80,9 @@ <!-- Change configuration --> <magentoCLI command="config:set reports/options/enabled 0" stepKey="disableReportModule"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Login as customer --> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml index 31f3449866984..4369fa8a94101 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontPrintOrderGuestTest.xml @@ -226,6 +226,9 @@ <deleteData createDataKey="createCartPriceRule" stepKey="deleteCartPriceRule"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php index dc6fc53e5ec43..dcd80646b168c 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -8,7 +8,10 @@ use Magento\Sales\Model\Order\Email\Sender\ShipmentSender; /** - * Test for Magento\Sales\Model\Order\Email\Sender\ShipmentSender class. + * Test for Magento\Sales\Model\Order\Email\Sender\ShipmentSender class + * + * @deprecated since ShipmentSender is deprecated + * @see \Magento\Sales\Model\Order\Email\Sender\ShipmentSender */ class ShipmentSenderTest extends AbstractSenderTest { diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index ec9246f7c33bd..6db1c4f96796f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -86,7 +86,10 @@ <deleteData createDataKey="createConfigChildProduct2" stepKey="deleteConfigChildProduct2"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteApiCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Create the rule --> diff --git a/app/code/Magento/Search/Block/Term.php b/app/code/Magento/Search/Block/Term.php index b27ef6b01fda2..ed6a2c5215b54 100644 --- a/app/code/Magento/Search/Block/Term.php +++ b/app/code/Magento/Search/Block/Term.php @@ -9,6 +9,8 @@ */ namespace Magento\Search\Block; +use Magento\Framework\DataObject; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\UrlFactory; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\Template; @@ -16,6 +18,8 @@ use Magento\Search\Model\ResourceModel\Query\CollectionFactory; /** + * Terms and conditions block + * * @api * @since 100.0.2 */ @@ -37,15 +41,11 @@ class Term extends Template protected $_maxPopularity; /** - * Url factory - * * @var UrlFactory */ protected $_urlFactory; /** - * Query collection factory - * * @var CollectionFactory */ protected $_queryCollectionFactory; @@ -71,17 +71,17 @@ public function __construct( * Load terms and try to sort it by names * * @return $this - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ protected function _loadTerms() { if (empty($this->_terms)) { $this->_terms = []; - $terms = $this->_queryCollectionFactory->create()->setPopularQueryFilter( - $this->_storeManager->getStore()->getId() - )->setPageSize( - 100 - )->load()->getItems(); + $terms = $this->_queryCollectionFactory->create() + ->setPopularQueryFilter($this->_storeManager->getStore()->getId()) + ->setPageSize(100) + ->load() + ->getItems(); if (count($terms) == 0) { return $this; @@ -91,6 +91,7 @@ protected function _loadTerms() $this->_minPopularity = end($terms)->getPopularity(); $range = $this->_maxPopularity - $this->_minPopularity; $range = $range == 0 ? 1 : $range; + $termKeys = []; foreach ($terms as $term) { if (!$term->getPopularity()) { continue; @@ -99,6 +100,7 @@ protected function _loadTerms() $temp[$term->getQueryText()] = $term; $termKeys[] = $term->getQueryText(); } + natcasesort($termKeys); foreach ($termKeys as $termKey) { @@ -109,8 +111,10 @@ protected function _loadTerms() } /** + * Load and return terms + * * @return array - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @throws NoSuchEntityException */ public function getTerms() { @@ -119,7 +123,9 @@ public function getTerms() } /** - * @param \Magento\Framework\DataObject $obj + * Return search url + * + * @param DataObject $obj * @return string */ public function getSearchUrl($obj) @@ -135,6 +141,8 @@ public function getSearchUrl($obj) } /** + * Return max popularity + * * @return int */ public function getMaxPopularity() @@ -143,6 +151,8 @@ public function getMaxPopularity() } /** + * Return min popularity + * * @return int */ public function getMinPopularity() diff --git a/app/code/Magento/Search/Test/Unit/Block/TermsTest.php b/app/code/Magento/Search/Test/Unit/Block/TermsTest.php new file mode 100644 index 0000000000000..d2e7e94a65f8e --- /dev/null +++ b/app/code/Magento/Search/Test/Unit/Block/TermsTest.php @@ -0,0 +1,173 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Search\Test\Unit\Block; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Framework\Url; +use Magento\Framework\UrlFactory; +use Magento\Framework\View\Element\Template\Context; +use Magento\Search\Block\Term; +use Magento\Search\Model\Query; +use Magento\Search\Model\ResourceModel\Query\Collection; +use Magento\Search\Model\ResourceModel\Query\CollectionFactory; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Tests for Terms block + */ +class TermsTest extends TestCase +{ + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var CollectionFactory|MockObject + */ + private $collectionFactoryMock; + + /** + * @var UrlFactory|MockObject + */ + private $urlFactoryMock; + + /** + * @var Term + */ + private $termsModel; + + /** + * @var StoreManager + */ + private $storeManagerMock; + + /** + * @inheritdoc + */ + public function setUp() + { + $objectManager = new ObjectManager($this); + + $this->contextMock = $this->createMock(Context::class); + $this->collectionFactoryMock = $this->createMock(CollectionFactory::class); + $this->urlFactoryMock = $this->createMock(UrlFactory::class); + $this->storeManagerMock = $this->createMock(StoreManager::class); + + $this->contextMock->expects($this->once()) + ->method('getStoreManager') + ->willReturn($this->storeManagerMock); + $this->termsModel = $objectManager->getObject( + Term::class, + [ + 'context' => $this->contextMock, + '_queryCollectionFactory' => $this->collectionFactoryMock, + '_urlFactory' => $this->urlFactoryMock + ] + ); + } + + /** + * Verify terms + * + * @dataProvider termKeysProvider + * @param string $termKey + * @param bool $popularity + */ + public function testGetTerms(string $termKey, bool $popularity): void + { + $terms = $this->createMock(Collection::class); + $dataObjectMock = $this->getMockBuilder(Query::class) + ->disableOriginalConstructor() + ->setMethods(['getPopularity', 'getQueryText']) + ->getMock(); + $storeMock = $this->createMock(Store::class); + + $this->storeManagerMock->expects($this->once()) + ->method('getStore') + ->willReturn($storeMock); + $storeMock->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $this->collectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($terms); + $terms->expects($this->once()) + ->method('setPopularQueryFilter') + ->willReturnSelf(); + $terms->expects($this->once()) + ->method('setPageSize') + ->willReturnSelf(); + $terms->expects($this->once()) + ->method('load') + ->willReturnSelf(); + $terms->expects($this->once()) + ->method('getItems') + ->willReturn([$dataObjectMock]); + $dataObjectMock->expects($this->exactly(!$popularity ? 3 : 4)) + ->method('getPopularity') + ->willReturn($popularity); + $dataObjectMock->expects($this->exactly(!$popularity ? 0 : 2)) + ->method('getQueryText') + ->willReturn($termKey); + + $this->assertEquals(!$popularity ? [] : [$termKey => $dataObjectMock], $this->termsModel->getTerms()); + } + + /** + * Verify get search Url + * + * @return void + */ + public function testGetSearchResult(): void + { + $urlMock = $this->getMockBuilder(Url::class) + ->disableOriginalConstructor() + ->setMethods(['setQueryParam', 'getUrl']) + ->getMock(); + + $dataObjectMock = $this->getMockBuilder(Query::class) + ->disableOriginalConstructor() + ->setMethods(['getPopularity', 'getQueryText']) + ->getMock(); + $this->urlFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($urlMock); + $dataObjectMock->expects($this->once()) + ->method('getQueryText') + ->willReturn('url'); + $urlMock->expects($this->once())->method('setQueryParam'); + $urlMock->expects($this->once()) + ->method('getUrl') + ->with('catalogsearch/result') + ->willReturn('url'); + + $this->assertEquals('url', $this->termsModel->getSearchUrl($dataObjectMock)); + } + + /** + * Terms data key provider + * + * @return array + */ + public function termKeysProvider(): array + { + return [ + [ + 'search', + true + ], + [ + '', + false + ] + ]; + } +} diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml index 8a2683af83dc1..5cbc7ee49d6cc 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/AdminCreateImageSwatchTest.xml @@ -24,7 +24,14 @@ </before> <after> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> - <actionGroup ref="logout" stepKey="logout"/> + <actionGroup ref="DeleteProductBySkuActionGroup" stepKey="deleteConfigurableProduct"> + <argument name="sku" value="{{BaseConfigurableProduct.sku}}"/> + </actionGroup> + <actionGroup ref="AdminDeleteProductAttributeByLabelActionGroup" stepKey="deleteProductAttribute"/> + <actionGroup ref="NavigateToAndResetProductAttributeGridToDefaultViewActionGroup" stepKey="resetProductAttributeFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- Begin creating a new product attribute of type "Image Swatch" --> @@ -96,15 +103,11 @@ </assertContains> <!-- Create a configurable product to verify the storefront with --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="amOnProductGridPage"/> - <waitForPageLoad time="30" stepKey="waitForProductGrid"/> - <actionGroup ref="GoToCreateProductPageActionGroup" stepKey="goToCreateConfigurableProduct"> - <argument name="product" value="BaseConfigurableProduct"/> - </actionGroup> + <amOnPage url="{{AdminProductCreatePage.url(BaseConfigurableProduct.attribute_set_id, BaseConfigurableProduct.type_id)}}" stepKey="goToCreateConfigurableProduct"/> <actionGroup ref="FillMainProductFormActionGroup" stepKey="fillProductForm"> <argument name="product" value="BaseConfigurableProduct"/> </actionGroup> - <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$$createCategory.name$$]" stepKey="fillCategory"/> + <searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[$createCategory.name$]" stepKey="fillCategory"/> <!-- Create configurations based off the Image Swatch we created earlier --> <click selector="{{AdminProductFormConfigurationsSection.createConfigurations}}" stepKey="clickCreateConfigurations"/> @@ -144,7 +147,7 @@ </assertContains> <!-- Go to the product listing page and see text swatch options --> - <amOnPage url="$$createCategory.name$$.html" stepKey="goToCategoryPageStorefront"/> + <amOnPage url="$createCategory.custom_attributes[url_key]$.html" stepKey="goToCategoryPageStorefront"/> <waitForPageLoad stepKey="waitForProductListingPage"/> <!-- Verify the storefront --> diff --git a/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php b/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php index a3f054d7c8dc7..70ea478004b9d 100644 --- a/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php +++ b/app/code/Magento/Theme/Controller/Result/AsyncCssPlugin.php @@ -58,9 +58,9 @@ function ($matches) use (&$cssMatches) { } $media = $media ?? 'all'; $loadCssAsync = sprintf( - '<link rel="preload" as="style" media="%s" . - onload="this.onload=null;this.rel=\'stylesheet\'"' . - 'href="%s">', + '<link rel="preload" as="style" media="%s"' . + ' onload="this.onload=null;this.rel=\'stylesheet\'"' . + ' href="%s" />', $media, $href ); diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php new file mode 100644 index 0000000000000..b1303e991a680 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/AsyncCssPluginTest.php @@ -0,0 +1,145 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\Test\Unit\Controller\Result; + +use Magento\Theme\Controller\Result\AsyncCssPlugin; +use Magento\Framework\App\Response\Http; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Theme\Test\Unit\Controller\Result\AsyncCssPlugin. + */ +class AsyncCssPluginTest extends TestCase +{ + const STUB_XML_PATH_USE_CSS_CRITICAL_PATH = 'dev/css/use_css_critical_path'; + + /** + * @var AsyncCssPlugin + */ + private $plugin; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Http|MockObject + */ + private $httpMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->httpMock = $this->createMock(Http::class); + + $objectManager = new ObjectManagerHelper($this); + $this->plugin = $objectManager->getObject( + AsyncCssPlugin::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Data Provider for before send response + * + * @return array + */ + public function sendResponseDataProvider(): array + { + return [ + [ + "content" => "<body><h1>Test Title</h1>" . + "<link rel=\"stylesheet\" href=\"css/critical.css\" />" . + "<p>Test Content</p></body>", + "flag" => true, + "result" => "<body><h1>Test Title</h1>" . + "<link rel=\"preload\" as=\"style\" media=\"all\"" . + " onload=\"this.onload=null;this.rel='stylesheet'\" href=\"css/critical.css\" />" . + "<p>Test Content</p>" . + "<link rel=\"stylesheet\" href=\"css/critical.css\" />" . + "\n</body>" + ], + [ + "content" => "<body><p>Test Content</p></body>", + "flag" => false, + "result" => "<body><p>Test Content</p></body>" + ], + [ + "content" => "<body><p>Test Content</p></body>", + "flag" => true, + "result" => "<body><p>Test Content</p></body>" + ] + ]; + } + + /** + * Test beforeSendResponse + * + * @param string $content + * @param bool $isSetFlag + * @param string $result + * @return void + * @dataProvider sendResponseDataProvider + */ + public function testBeforeSendResponse($content, $isSetFlag, $result): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn($content); + + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH, + ScopeInterface::SCOPE_STORE + ) + ->willReturn($isSetFlag); + + $this->httpMock->expects($this->any()) + ->method('setContent') + ->with($result); + + $this->plugin->beforeSendResponse($this->httpMock); + } + + /** + * Test BeforeSendResponse if content is not a string + * + * @return void + */ + public function testIfGetContentIsNotAString(): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn([]); + + $this->scopeConfigMock->expects($this->any()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_USE_CSS_CRITICAL_PATH, + ScopeInterface::SCOPE_STORE + ) + ->willReturn(false); + + $this->plugin->beforeSendResponse($this->httpMock); + } +} diff --git a/app/code/Magento/Theme/Test/Unit/Controller/Result/JsFooterPluginTest.php b/app/code/Magento/Theme/Test/Unit/Controller/Result/JsFooterPluginTest.php new file mode 100644 index 0000000000000..8b696251d4e73 --- /dev/null +++ b/app/code/Magento/Theme/Test/Unit/Controller/Result/JsFooterPluginTest.php @@ -0,0 +1,164 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Theme\Test\Unit\Controller\Result; + +use Magento\Theme\Controller\Result\JsFooterPlugin; +use Magento\Framework\App\Response\Http; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; + +/** + * Unit test for Magento\Theme\Test\Unit\Controller\Result\JsFooterPlugin. + */ +class JsFooterPluginTest extends TestCase +{ + const STUB_XML_PATH_DEV_MOVE_JS_TO_BOTTOM = 'dev/js/move_script_to_bottom'; + + /** + * @var JsFooterPlugin + */ + private $plugin; + + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfigMock; + + /** + * @var Http|MockObject + */ + private $httpMock; + + /** + * @inheritdoc + */ + protected function setUp(): void + { + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterface::class) + ->setMethods(['isSetFlag']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $this->httpMock = $this->createMock(Http::class); + + $objectManager = new ObjectManagerHelper($this); + $this->plugin = $objectManager->getObject( + JsFooterPlugin::class, + [ + 'scopeConfig' => $this->scopeConfigMock + ] + ); + } + + /** + * Data Provider for testBeforeSendResponse() + * + * @return array + */ + public function sendResponseDataProvider(): array + { + return [ + 'content_with_script_tag' => [ + "content" => "<body><h1>Test Title</h1>" . + "<script type=\"text/x-magento-init\">test</script>" . + "<script type=\"text/x-magento-template\">test</script>" . + "<p>Test Content</p></body>", + "flag" => true, + "result" => "<body><h1>Test Title</h1>" . + "<script type=\"text/x-magento-template\">test</script>" . + "<p>Test Content</p>" . + "<script type=\"text/x-magento-init\">test</script>" . + "\n</body>" + ], + 'content_with_config_disable' => [ + "content" => "<body><p>Test Content</p></body>", + "flag" => false, + "result" => "<body><p>Test Content</p></body>" + ], + 'content_without_script_tag' => [ + "content" => "<body><p>Test Content</p></body>", + "flag" => true, + "result" => "<body><p>Test Content</p>\n</body>" + ] + ]; + } + + /** + * Test beforeSendResponse + * + * @param string $content + * @param bool $isSetFlag + * @param string $result + * @return void + * @dataProvider sendResponseDataProvider + */ + public function testBeforeSendResponse($content, $isSetFlag, $result): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn($content); + + $this->scopeConfigMock->expects($this->once()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_DEV_MOVE_JS_TO_BOTTOM, + ScopeInterface::SCOPE_STORE + ) + ->willReturn($isSetFlag); + + $this->httpMock->expects($this->any()) + ->method('setContent') + ->with($result); + + $this->plugin->beforeSendResponse($this->httpMock); + } + + /** + * Data Provider for testBeforeSendResponseIfGetContentIsNotAString() + * + * @return array + */ + public function ifGetContentIsNotAStringDataProvider(): array + { + return [ + 'empty_array' => [ + 'content' => [] + ], + 'null' => [ + 'content' => null + ] + ]; + } + + /** + * Test BeforeSendResponse if content is not a string + * + * @param string $content + * @return void + * @dataProvider ifGetContentIsNotAStringDataProvider + */ + public function testBeforeSendResponseIfGetContentIsNotAString($content): void + { + $this->httpMock->expects($this->once()) + ->method('getContent') + ->willReturn($content); + + $this->scopeConfigMock->expects($this->never()) + ->method('isSetFlag') + ->with( + self::STUB_XML_PATH_DEV_MOVE_JS_TO_BOTTOM, + ScopeInterface::SCOPE_STORE + ) + ->willReturn(false); + + $this->plugin->beforeSendResponse($this->httpMock); + } +} diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js index 8170e4ed44eea..512f3520f2ef0 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/plugins/table/editor_plugin_src.js @@ -1222,7 +1222,7 @@ var last; // Skip empty text nodes form the end - for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ; + for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling); if (last && last.nodeName == 'TABLE') ed.dom.add(ed.getBody(), 'p', null, '<br mce_bogus="1" />'); diff --git a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js index 2549fa93a834f..0464788d155e2 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/columns/image-preview.js @@ -239,7 +239,7 @@ define([ handleKeyDown: function (e) { var key = keyCodes[e.keyCode]; - if (this.visibleRecord() !== null) { + if (this.visibleRecord() !== null && document.activeElement.tagName !== 'INPUT') { if (key === 'pageLeftKey') { this.prev(this.displayedRecord()); } else if (key === 'pageRightKey') { diff --git a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js index 547cdab16cdf1..aa9989251b656 100644 --- a/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js +++ b/app/code/Magento/Ui/view/base/web/js/grid/data-storage.js @@ -188,7 +188,8 @@ define([ result = { items: this.getByIds(request.ids), - totalRecords: request.totalRecords + totalRecords: request.totalRecords, + errorMessage: request.errorMessage }; delay ? @@ -216,7 +217,8 @@ define([ this._requests.push({ ids: this.getIds(data.items), params: params, - totalRecords: data.totalRecords + totalRecords: data.totalRecords, + errorMessage: data.errorMessage }); return this; diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js index 6ff7c1f673213..ab806e89385b6 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/optgroup.js @@ -250,7 +250,7 @@ define([ // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document. // That's why we first added them without selection. Now it's time to set the selection. - if (previousSelectedValues.length) { + if (previousSelectedValues.length && newOptions.value) { isSelected = ko.utils.arrayIndexOf( previousSelectedValues, ko.selectExtensions.readValue(newOptions.value) diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html b/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html index 089ee21bec15c..788cb0c2b5e56 100644 --- a/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html +++ b/app/code/Magento/Ui/view/base/web/templates/grid/masonry.html @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ --> -<div data-role="grid-wrapper" class="masonry-image-grid" attr="'data-id': containerId"> +<div data-role="grid-wrapper" class="masonry-image-grid" attr="'data-id': containerId" tabindex="0"> <div class="masonry-image-column" repeat="foreach: rows, item: '$row'"> <div outerfasteach="data: getVisible(), as: '$col'" template="getBody()"/> </div> diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html index 5e7fd11dbf693..7d7d4b6ae88cb 100644 --- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html +++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-custom.html @@ -27,7 +27,7 @@ <h1 id="modal-title-<%- data.id %>" class="modal-title" <% if(data.subTitle){ %> <span class="modal-subtitle" - data-role="subtitle"> + data-role="subTitle"> <%= data.subTitle %> </span> <% } %> diff --git a/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html b/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html index 53661ed8df87f..08376964eee63 100644 --- a/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html +++ b/app/code/Magento/Ui/view/base/web/templates/modal/modal-popup.html @@ -27,7 +27,7 @@ <h1 id="modal-title-<%- data.id %>" class="modal-title" <% if(data.subTitle){ %> <span class="modal-subtitle" - data-role="subtitle"> + data-role="subTitle"> <%= data.subTitle %> </span> <% } %> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.xml new file mode 100644 index 0000000000000..690b6b8bc2b59 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup.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="AssertStorefrontAddToWishListIconIsClickableForGuestUserActionGroup"> + <annotations> + <description>Assert "Add to Wish List" icon is clickable in category product listing</description> + </annotations> + + <moveMouseOver selector="{{StorefrontCategoryMainSection.ProductItemInfo}}" stepKey="hoverProduct"/> + <click selector="{{StorefrontCategoryMainSection.addToWishListIconProductInfoHover}}" stepKey="clickOnAddToWishListIcon"/> + <waitForElementVisible selector="{{StorefrontCustomerLoginMessagesSection.errorMessage}}" stepKey="waitForErrorMessageIsVisible"/> + <see selector="{{StorefrontCustomerLoginMessagesSection.errorMessage}}" userInput="You must login or register to add items to your wishlist." stepKey="assertErrorMessage"/> + <seeInCurrentUrl url="{{StorefrontCustomerSignInPage.url}}" stepKey="assertCustomerLoginPageUrl"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml index 6a718ebdfcf0f..bb0a3c40c21f1 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontDeleteConfigurableProductFromWishlistTest.xml @@ -118,6 +118,9 @@ <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> <actionGroup ref="logout" stepKey="logout"/> + + <!-- Reindex invalidated indices after product attribute has been created/deleted --> + <actionGroup ref="CliRunReindexUsingCronJobsActionGroup" stepKey="reindexInvalidatedIndices"/> </after> <!-- 1. Login as a customer --> diff --git a/composer.json b/composer.json index 9cbbf1689738f..8d38f1e0a151e 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "lib-libxml": "*", "braintree/braintree_php": "3.35.0", "colinmollenhour/cache-backend-file": "~1.4.1", - "colinmollenhour/cache-backend-redis": "1.10.6", + "colinmollenhour/cache-backend-redis": "1.11.0", "colinmollenhour/credis": "1.10.0", "colinmollenhour/php-redis-session-abstract": "~1.4.0", "composer/composer": "^1.6", @@ -88,7 +88,7 @@ "friendsofphp/php-cs-fixer": "~2.14.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "*", - "magento/magento2-functional-testing-framework": "2.5.4", + "magento/magento2-functional-testing-framework": "2.6.1", "pdepend/pdepend": "2.5.2", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "@stable", diff --git a/composer.lock b/composer.lock index 5b94f60fa80a9..347a50bdf68e0 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": "988eebffd81167973e4a51d7efd5be46", + "content-hash": "3b292997ff7767b89b6e08b0c550db7d", "packages": [ { "name": "braintree/braintree_php", @@ -88,16 +88,16 @@ }, { "name": "colinmollenhour/cache-backend-redis", - "version": "1.10.6", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis.git", - "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf" + "reference": "389fb68de15660e39b055d149d31f3708b5d6cbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/cc941a5f4cc017e11d3eab9061811ba9583ed6bf", - "reference": "cc941a5f4cc017e11d3eab9061811ba9583ed6bf", + "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/389fb68de15660e39b055d149d31f3708b5d6cbc", + "reference": "389fb68de15660e39b055d149d31f3708b5d6cbc", "shasum": "" }, "require": { @@ -120,7 +120,7 @@ ], "description": "Zend_Cache backend using Redis with full support for tags.", "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis", - "time": "2018-09-24T16:02:07+00:00" + "time": "2019-03-03T04:04:49+00:00" }, { "name": "colinmollenhour/credis", @@ -830,6 +830,7 @@ } ], "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", + "abandoned": true, "time": "2018-07-31T13:22:33+00:00" }, { @@ -880,6 +881,7 @@ "Guzzle", "stream" ], + "abandoned": true, "time": "2014-10-12T19:18:40+00:00" }, { @@ -5197,6 +5199,90 @@ ], "time": "2017-11-03T13:08:21+00:00" }, + { + "name": "aws/aws-sdk-php", + "version": "3.133.8", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd", + "reference": "c564fcccd5fc7b5e8514d1cbe35558be1e3a11cd", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^5.3.3|^6.2.1|^7.0", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4.1", + "mtdowling/jmespath.php": "^2.5", + "php": ">=5.5" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-pcntl": "*", + "ext-sockets": "*", + "nette/neon": "^2.3", + "phpunit/phpunit": "^4.8.35|^5.4.3", + "psr/cache": "^1.0", + "psr/simple-cache": "^1.0", + "sebastian/comparator": "^1.2.3" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Aws\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "time": "2020-02-05T19:12:47+00:00" + }, { "name": "behat/gherkin", "version": "v4.6.0", @@ -6589,67 +6675,6 @@ ], "time": "2019-10-30T14:39:59+00:00" }, - { - "name": "facebook/webdriver", - "version": "1.7.1", - "source": { - "type": "git", - "url": "https://github.com/facebook/php-webdriver.git", - "reference": "e43de70f3c7166169d0f14a374505392734160e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/e43de70f3c7166169d0f14a374505392734160e5", - "reference": "e43de70f3c7166169d0f14a374505392734160e5", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "ext-json": "*", - "ext-mbstring": "*", - "ext-zip": "*", - "php": "^5.6 || ~7.0", - "symfony/process": "^2.8 || ^3.1 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.0", - "jakub-onderka/php-parallel-lint": "^0.9.2", - "php-coveralls/php-coveralls": "^2.0", - "php-mock/php-mock-phpunit": "^1.1", - "phpunit/phpunit": "^5.7", - "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", - "squizlabs/php_codesniffer": "^2.6", - "symfony/var-dumper": "^3.3 || ^4.0" - }, - "suggest": { - "ext-SimpleXML": "For Firefox profile creation" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-community": "1.5-dev" - } - }, - "autoload": { - "psr-4": { - "Facebook\\WebDriver\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "description": "A PHP client for Selenium WebDriver", - "homepage": "https://github.com/facebook/php-webdriver", - "keywords": [ - "facebook", - "php", - "selenium", - "webdriver" - ], - "abandoned": "php-webdriver/webdriver", - "time": "2019-06-13T08:02:18+00:00" - }, { "name": "flow/jsonpath", "version": "0.5.0", @@ -7356,34 +7381,41 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.5.4", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "4f482ce22a755a812b76f81020ae71d502f9d043" + "reference": "b00f5e195e1ed7f6335bce3052be9a0291f4d0db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/4f482ce22a755a812b76f81020ae71d502f9d043", - "reference": "4f482ce22a755a812b76f81020ae71d502f9d043", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/b00f5e195e1ed7f6335bce3052be9a0291f4d0db", + "reference": "b00f5e195e1ed7f6335bce3052be9a0291f4d0db", "shasum": "" }, "require": { "allure-framework/allure-codeception": "~1.3.0", + "aws/aws-sdk-php": "^3.132", "codeception/codeception": "~2.4.5", "composer/composer": "^1.4", "consolidation/robo": "^1.0.0", "csharpru/vault-php": "~3.5.3", "csharpru/vault-php-guzzle6-transport": "^2.0", "ext-curl": "*", + "ext-json": "*", + "ext-openssl": "*", "flow/jsonpath": ">0.2", "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", + "php-webdriver/webdriver": "^1.8.0", "symfony/process": "^2.8 || ^3.1 || ^4.0", "vlucas/phpdotenv": "^2.4" }, + "replace": { + "facebook/webdriver": "^1.7.1" + }, "require-dev": { "brainmaestro/composer-git-hooks": "^2.3.1", "codacy/coverage": "^1.4", @@ -7430,7 +7462,7 @@ "magento", "testing" ], - "time": "2019-12-12T20:14:00+00:00" + "time": "2020-02-11T22:23:54+00:00" }, { "name": "mikey179/vfsstream", @@ -7478,6 +7510,63 @@ "homepage": "http://vfs.bovigo.org/", "time": "2019-10-30T15:31:00+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "52168cb9472de06979613d365c7f1ab8798be895" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895", + "reference": "52168cb9472de06979613d365c7f1ab8798be895", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/polyfill-mbstring": "^1.4" + }, + "require-dev": { + "composer/xdebug-handler": "^1.2", + "phpunit/phpunit": "^4.8.36|^7.5.15" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "JmesPath\\": "src/" + }, + "files": [ + "src/JmesPath.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "time": "2019-12-30T18:03:34+00:00" + }, { "name": "mustache/mustache", "version": "v2.13.0", @@ -7765,6 +7854,71 @@ ], "time": "2018-02-15T16:58:55+00:00" }, + { + "name": "php-webdriver/webdriver", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/php-webdriver/php-webdriver.git", + "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/3e33ee3b8a688d719c55acdd7c6788e3006e1d3e", + "reference": "3e33ee3b8a688d719c55acdd7c6788e3006e1d3e", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/polyfill-mbstring": "^1.12", + "symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "jakub-onderka/php-parallel-lint": "^1.0", + "php-coveralls/php-coveralls": "^2.0", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "sminnee/phpunit-mock-objects": "^3.4", + "squizlabs/php_codesniffer": "^3.5", + "symfony/var-dumper": "^3.3 || ^4.0 || ^5.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "files": [ + "lib/Exception/TimeoutException.php" + ], + "psr-4": { + "Facebook\\WebDriver\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.", + "homepage": "https://github.com/php-webdriver/php-webdriver", + "keywords": [ + "Chromedriver", + "geckodriver", + "php", + "selenium", + "webdriver" + ], + "time": "2020-02-10T15:04:25+00:00" + }, { "name": "phpcollection/phpcollection", "version": "0.5.0", diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php index 00c8bb85d9be7..e03a54f9463d7 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartItemRepositoryTest.php @@ -6,6 +6,8 @@ */ namespace Magento\Quote\Api; +use Magento\CatalogInventory\Api\StockRegistryInterface; +use Magento\CatalogInventory\Model\Stock; use Magento\TestFramework\TestCase\WebapiAbstract; class GuestCartItemRepositoryTest extends WebapiAbstract @@ -167,9 +169,13 @@ public function testRemoveItem() /** * @magentoApiDataFixture Magento/Checkout/_files/quote_with_items_saved.php + * @param array $stockData + * @param string|null $errorMessage + * @dataProvider updateItemDataProvider */ - public function testUpdateItem() + public function testUpdateItem(array $stockData, string $errorMessage = null) { + $this->updateStockData('simple_one', $stockData); /** @var \Magento\Quote\Model\Quote $quote */ $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_item_with_items', 'reserved_order_id'); @@ -215,6 +221,9 @@ public function testUpdateItem() ], ]; } + if ($errorMessage) { + $this->expectExceptionMessage($errorMessage); + } $this->_webApiCall($serviceInfo, $requestData); $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); $quote->load('test_order_item_with_items', 'reserved_order_id'); @@ -223,4 +232,66 @@ public function testUpdateItem() $this->assertEquals(5, $item->getQty()); $this->assertEquals($itemId, $item->getItemId()); } + + /** + * @return array + */ + public function updateItemDataProvider(): array + { + return [ + [ + [] + ], + [ + [ + 'qty' => 0, + 'is_in_stock' => 1, + 'use_config_manage_stock' => 0, + 'manage_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_YES_NOTIFY, + ] + ], + [ + [ + 'qty' => 0, + 'is_in_stock' => 1, + 'use_config_manage_stock' => 0, + 'manage_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_NO, + ], + 'This product is out of stock.' + ], + [ + [ + 'qty' => 2, + 'is_in_stock' => 1, + 'use_config_manage_stock' => 0, + 'manage_stock' => 1, + 'use_config_backorders' => 0, + 'backorders' => Stock::BACKORDERS_NO, + ], + 'The requested qty is not available' + ] + ]; + } + + /** + * Update product stock + * + * @param string $sku + * @param array $stockData + * @return void + */ + private function updateStockData(string $sku, array $stockData): void + { + if ($stockData) { + /** @var $stockRegistry StockRegistryInterface */ + $stockRegistry = $this->objectManager->create(StockRegistryInterface::class); + $stockItem = $stockRegistry->getStockItemBySku($sku); + $stockItem->addData($stockData); + $stockRegistry->updateStockItemBySku($sku, $stockItem); + } + } } diff --git a/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php b/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php new file mode 100644 index 0000000000000..6a7d034d5892f --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Bundle/Model/PrepareBundleLinks.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Bundle\Model; + +use Magento\Bundle\Api\Data\LinkInterfaceFactory; +use Magento\Bundle\Api\Data\OptionInterfaceFactory; +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Api\Data\ProductInterface; + +/** + * Prepare bundle product links + */ +class PrepareBundleLinks +{ + /** @var LinkInterfaceFactory */ + private $linkFactory; + + /** @var OptionInterfaceFactory */ + private $optionLinkFactory; + + /** @var ProductExtensionFactory */ + private $extensionAttributesFactory; + + /** + * @param LinkInterfaceFactory $linkFactory + * @param OptionInterfaceFactory $optionLinkFactory + * @param ProductExtensionFactory $extensionAttributesFactory + */ + public function __construct( + LinkInterfaceFactory $linkFactory, + OptionInterfaceFactory $optionLinkFactory, + ProductExtensionFactory $extensionAttributesFactory + ) { + $this->linkFactory = $linkFactory; + $this->optionLinkFactory = $optionLinkFactory; + $this->extensionAttributesFactory = $extensionAttributesFactory; + } + + /** + * Prepare bundle product links + * + * @param ProductInterface $product + * @param array $bundleOptionsData + * @param array $bundleSelectionsData + * @return ProductInterface + */ + public function execute( + ProductInterface $product, + array $bundleOptionsData, + array $bundleSelectionsData + ): ProductInterface { + $product->setBundleOptionsData($bundleOptionsData) + ->setBundleSelectionsData($bundleSelectionsData); + $options = []; + foreach ($product->getBundleOptionsData() as $key => $optionData) { + $option = $this->optionLinkFactory->create(['data' => $optionData]); + $option->setSku($product->getSku()); + $option->setOptionId(null); + $links = []; + $bundleLinks = $product->getBundleSelectionsData(); + foreach ($bundleLinks[$key] as $linkData) { + $link = $this->linkFactory->create(['data' => $linkData]); + $link->setQty($linkData['selection_qty']); + $priceType = $price = null; + if ($product->getPriceType() === Price::PRICE_TYPE_FIXED) { + $priceType = $linkData['selection_price_type'] ?? null; + $price = $linkData['selection_price_value'] ?? null; + } + $link->setPriceType($priceType); + $link->setPrice($price); + $links[] = $link; + } + $option->setProductLinks($links); + $options[] = $option; + } + /** @var ProductExtensionFactory $extensionAttributesFactory */ + $extensionAttributes = $product->getExtensionAttributes() ?? $this->extensionAttributesFactory->create(); + $extensionAttributes->setBundleProductOptions($options); + $product->setExtensionAttributes($extensionAttributes); + + return $product; + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php new file mode 100644 index 0000000000000..f1e98cd4ea0bf --- /dev/null +++ b/dev/tests/integration/framework/Magento/TestFramework/Directory/Model/GetRegionIdByName.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\TestFramework\Directory\Model; + +use Magento\Directory\Model\RegionFactory; + +/** + * Return region ID by region default name and country code. + */ +class GetRegionIdByName +{ + /** + * @var RegionFactory + */ + private $regionFactory; + + /** + * @var array + */ + private $regionIdsCache; + + /** + * @param RegionFactory $regionFactory + */ + public function __construct( + RegionFactory $regionFactory + ) { + $this->regionFactory = $regionFactory; + } + + /** + * Get region ID from cache property if region id exist or load it. + * + * @param string $regionName + * @param string $countryId + * @return int|null + */ + public function execute(string $regionName, string $countryId): ?int + { + $cacheKey = "{$regionName}_{$countryId}"; + + if (!isset($this->regionIdsCache[$cacheKey])) { + $region = $this->regionFactory->create()->loadByName($regionName, $countryId); + $this->regionIdsCache[$cacheKey] = $region->getRegionId() ? (int)$region->getRegionId() : null; + } + + return $this->regionIdsCache[$cacheKey]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php new file mode 100644 index 0000000000000..287a3f07d1964 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/AbstractBundleOptionsViewTest.php @@ -0,0 +1,194 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\ResourceModel\Product as ProductResource; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\View\Result\PageFactory; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class consist of basic logic for bundle options view + */ +abstract class AbstractBundleOptionsViewTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var SerializerInterface */ + private $serializer; + + /** @var Registry */ + private $registry; + + /** @var PageFactory */ + private $pageFactory; + + /** @var ProductResource */ + private $productResource; + + /** @var string */ + private $selectLabelXpath = "//fieldset[contains(@class, 'fieldset-bundle-options')]" + . "//label/span[normalize-space(text()) = '%s']"; + + /** @var string */ + private $backToProductDetailButtonXpath = "//button[contains(@class, 'back customization')]"; + + /** @var string */ + private $titleXpath = "//fieldset[contains(@class, 'bundle-options')]//span[contains(text(), 'Customize %s')]"; + + /** @var string */ + private $singleOptionXpath = "//input[contains(@class, 'bundle-option') and contains(@type, 'hidden')]"; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->serializer = $this->objectManager->get(SerializerInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->pageFactory = $this->objectManager->get(PageFactory::class); + $this->productResource = $this->objectManager->get(ProductResource::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + + parent::tearDown(); + } + + /** + * Process bundle options view with few selections + * + * @param string $sku + * @param string $optionsSelectLabel + * @param array $expectedSelectionsNames + * @param bool $requiredOption + * @return void + */ + protected function processMultiSelectionsView( + string $sku, + string $optionsSelectLabel, + array $expectedSelectionsNames, + bool $requiredOption = false + ): void { + $product = $this->productRepository->get($sku); + $result = $this->renderProductOptionsBlock($product); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->backToProductDetailButtonXpath, $result), + "'Back to product details' button doesn't exist on the page" + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->selectLabelXpath, $optionsSelectLabel), $result), + 'Options select label does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->titleXpath, $product->getName()), $result), + sprintf('Customize %s label does not exist on the page', $product->getName()) + ); + $selectPath = $requiredOption ? $this->getRequiredSelectXpath() : $this->getNotRequiredSelectXpath(); + foreach ($expectedSelectionsNames as $selection) { + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($selectPath, $selection), $result), + sprintf('Option for product named %s does not exist on the page', $selection) + ); + } + } + + /** + * Process bundle options view with single selection + * + * @param string $sku + * @param string $optionsSelectLabel + * @return void + */ + protected function processSingleSelectionView(string $sku, string $optionsSelectLabel): void + { + $product = $this->productRepository->get($sku); + $result = $this->renderProductOptionsBlock($product); + $this->assertEquals(1, Xpath::getElementsCountForXpath($this->backToProductDetailButtonXpath, $result)); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf($this->selectLabelXpath, $optionsSelectLabel), $result), + 'Options select label does not exist on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath($this->singleOptionXpath, $result), + 'Bundle product options select with single option does not display correctly' + ); + } + + /** + * Register product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->unregister('current_product'); + $this->registry->register('product', $product); + $this->registry->register('current_product', $product); + } + + /** + * Render bundle product options block + * + * @param ProductInterface $product + * @return string + */ + private function renderProductOptionsBlock(ProductInterface $product): string + { + $this->registerProduct($product); + $page = $this->pageFactory->create(); + $page->addHandle(['default', 'catalog_product_view', 'catalog_product_view_type_bundle']); + $page->getLayout()->generateXml(); + $block = $page->getLayout()->getBlock('product.info.bundle.options'); + + return $block->toHtml(); + } + + /** + * Get required select Xpath + * + * @return string + */ + abstract protected function getRequiredSelectXpath(): string; + + /** + * Get not required select Xpath + * + * @return string + */ + abstract protected function getNotRequiredSelectXpath(): string; +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php new file mode 100644 index 0000000000000..2a61a252e9313 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleProductPriceTest.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +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; + +/** + * Check bundle product prices. + * + * @magentoDbIsolation enabled + * @magentoAppIsolation disabled + * @magentoAppArea frontend + */ +class BundleProductPriceTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Registry */ + private $registry; + + /** @var ProductRepositoryInterface */ + private $productRepository; + + /** @var SerializerInterface */ + private $json; + + /** @var Bundle */ + private $block; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Bundle::class); + $this->registry = $this->objectManager->get(Registry::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php + * + * @return void + */ + public function testDynamicBundleOptionPrices(): void + { + $expectedData = [ + 'options_prices' => [ + [ + 'oldPrice' => ['amount' => 10], + 'basePrice' => ['amount' => 10], + 'finalPrice' => ['amount' => 10], + ], + [ + 'oldPrice' => ['amount' => 20], + 'basePrice' => ['amount' => 20], + 'finalPrice' => ['amount' => 20], + ], + [ + 'oldPrice' => ['amount' => 30], + 'basePrice' => ['amount' => 30], + 'finalPrice' => ['amount' => 30], + ], + ], + 'bundle_prices' => [ + 'oldPrice' => ['amount' => 0], + 'basePrice' => ['amount' => 0], + 'finalPrice' => ['amount' => 0], + ] + ]; + $this->processBundlePriceView('bundle_product', $expectedData); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/product_with_multiple_options_1.php + * + * @return void + */ + public function testFixedBundleOptionPrices(): void + { + $expectedData = [ + 'options_prices' => [ + [ + 'oldPrice' => ['amount' => 2.75], + 'basePrice' => ['amount' => 2.75], + 'finalPrice' => ['amount' => 2.75], + ], + [ + 'oldPrice' => ['amount' => 6.75], + 'basePrice' => ['amount' => 6.75], + 'finalPrice' => ['amount' => 6.75], + ], + ], + 'bundle_prices' => [ + 'oldPrice' => ['amount' => 12.75], + 'basePrice' => ['amount' => 10], + 'finalPrice' => ['amount' => 10], + ] + ]; + $this->processBundlePriceView('bundle-product', $expectedData); + } + + /** + * @param string $productSku + * @param array $expectedData + * @return void + */ + private function processBundlePriceView(string $productSku, array $expectedData): void + { + $this->registerProduct($productSku); + $jsonConfig = $this->json->unserialize($this->block->getJsonConfig()); + $this->assertEquals($expectedData['bundle_prices'], $jsonConfig['prices']); + $this->assertOptionsConfig($expectedData['options_prices'], $jsonConfig); + } + + /** + * Assert options prices. + * + * @param array $expectedData + * @param array $actualData + * @return void + */ + private function assertOptionsConfig(array $expectedData, array $actualData): void + { + $optionConfig = $actualData['options'] ?? null; + $this->assertNotNull($optionConfig); + $optionConfig = reset($optionConfig); + foreach (array_values($optionConfig['selections']) as $key => $selection) { + $this->assertEquals($expectedData[$key], $selection['prices']); + } + } + + /** + * Register the product. + * + * @param string $productSku + * @return void + */ + private function registerProduct(string $productSku): void + { + $product = $this->productRepository->get($productSku); + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php index ce324ed774dc4..dab699064c4bb 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/BundleTest.php @@ -7,53 +7,66 @@ namespace Magento\Bundle\Block\Catalog\Product\View\Type; +use Magento\Bundle\Model\Product\Price; +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 for Magento\Bundle\Block\Catalog\Product\View\Type\Bundle + * Class checks bundle product view behaviour * * @magentoDataFixture Magento/Bundle/_files/product.php - * @magentoDbIsolation disabled + * @magentoDbIsolation enabled * @magentoAppArea frontend + * @see \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle */ -class BundleTest extends \PHPUnit\Framework\TestCase +class BundleTest extends TestCase { - /** - * @var \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle - */ + /** @var Bundle */ private $block; - /** - * @var \Magento\Catalog\Api\Data\ProductInterface - */ - private $product; - - /** - * @var \Magento\TestFramework\ObjectManager - */ + /** @var ObjectManagerInterface */ private $objectManager; - /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface - */ + /** @var ProductRepositoryInterface */ private $productRepository; + /** @var LayoutInterface */ + private $layout; + + /** @var SerializerInterface */ + private $json; + + /** @var Registry */ + private $registry; + /** * @inheritdoc */ protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - - $this->productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); - $this->product = $this->productRepository->get('bundle-product', false, null, true); - $this->product->setPriceType(\Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC)->save(); - $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); - $this->objectManager->get(\Magento\Framework\Registry::class)->register('product', $this->product); - - $this->block = $this->objectManager->get( - \Magento\Framework\View\LayoutInterface::class - )->createBlock( - \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository->cleanCache(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->block = $this->layout->createBlock(Bundle::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->registry = $this->objectManager->get(Registry::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->registry->unregister('product'); + + parent::tearDown(); } /** @@ -61,12 +74,12 @@ protected function setUp() * * @return void */ - public function testGetJsonConfig() + public function testGetJsonConfig(): void { - $option = $this->productRepository->get('simple'); - $option->setSpecialPrice(5) - ->save(); - $config = json_decode($this->block->getJsonConfig(), true); + $product = $this->updateProduct('bundle-product', ['price_type' => Price::PRICE_TYPE_DYNAMIC]); + $this->registerProduct($product); + $this->updateProduct('simple', ['special_price' => 5]); + $config = $this->json->unserialize($this->block->getJsonConfig()); $options = current($config['options']); $selection = current($options['selections']); $this->assertEquals(10, $selection['prices']['oldPrice']['amount']); @@ -75,10 +88,124 @@ public function testGetJsonConfig() } /** - * Tear Down + * @dataProvider isSalableForStockStatusProvider + * + * @param bool $isSalable + * @param string $expectedValue + * @return void */ - protected function tearDown() + public function testStockStatusView(bool $isSalable, string $expectedValue): void { - $this->objectManager->get(\Magento\Framework\Registry::class)->unregister('product'); + $product = $this->productRepository->get('bundle-product'); + $product->setAllItemsSalable($isSalable); + $this->block->setTemplate('Magento_Bundle::catalog/product/view/type/bundle.phtml'); + $result = $this->renderBlockHtml($product); + $this->assertEquals($expectedValue, trim(strip_tags($result))); + } + + /** + * @return array + */ + public function isSalableForStockStatusProvider(): array + { + return [ + 'is_salable' => [ + 'is_salable' => true, + 'expected_value' => 'In stock', + ], + 'is_not_salable' => [ + 'is_salable' => false, + 'expected_value' => 'Out of stock', + ], + ]; + } + + /** + * @dataProvider isSalableForCustomizeButtonProvider + * + * @param bool $isSalable + * @param string $expectedValue + * @return void + */ + public function testCustomizeButton(bool $isSalable, string $expectedValue): void + { + $product = $this->productRepository->get('bundle-product'); + $product->setSalable($isSalable); + $this->block->setTemplate('Magento_Bundle::catalog/product/view/customize.phtml'); + $result = $this->renderBlockHtml($product); + $this->assertEquals($expectedValue, trim(strip_tags($result))); + } + + /** + * @return array + */ + public function isSalableForCustomizeButtonProvider(): array + { + return [ + 'is_salable' => [ + 'is_salable' => true, + 'expected_value' => 'Customize and Add to Cart', + ], + 'is_not_salable' => [ + 'is_salable' => false, + 'expected_value' => '', + ], + ]; + } + + /** + * @magentoDataFixture Magento/Bundle/_files/empty_bundle_product.php + * + * @param bool $isSalable + * @param string $expectedValue + * @return void + */ + public function testCustomizeButtonProductWithoutOptions(): void + { + $product = $this->productRepository->get('bundle-product'); + $product->setSalable(true); + $this->block->setTemplate('Magento_Bundle::catalog/product/view/customize.phtml'); + $result = $this->renderBlockHtml($product); + $this->assertEmpty(trim(strip_tags($result))); + } + + /** + * Update product + * + * @param ProductInterface|string $productSku + * @param array $data + * @return ProductInterface + */ + private function updateProduct(string $productSku, array $data): ProductInterface + { + $product = $this->productRepository->get($productSku); + $product->addData($data); + + return $this->productRepository->save($product); + } + + /** + * Register product + * + * @param ProductInterface $product + * @return void + */ + private function registerProduct(ProductInterface $product): void + { + $this->registry->unregister('product'); + $this->registry->register('product', $product); + } + + /** + * Render block output + * + * @param ProductInterface $product + * @return string + */ + private function renderBlockHtml(ProductInterface $product): string + { + $this->registerProduct($product); + + return $this->block->toHtml(); } } diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php new file mode 100644 index 0000000000000..cbe150b9fb3f5 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/CheckboxOptionViewTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks checkbox bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class CheckboxOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-checkbox-options', + 'Checkbox Options', + $expectedSelectionsNames + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-checkbox-required-options', + 'Checkbox Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_checkbox_required_option.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product-checkbox-required-option', 'Checkbox Options'); + } + + /** + * @inheritdoc + */ + protected function getRequiredSelectXpath(): string + { + return "//input[@type='checkbox' and contains(@data-validate, 'validate-one-required-by-name')" + . "and contains(@class, 'bundle-option')]/../label//span[normalize-space(text()) = '1 x %s']"; + } + + /** + * @inheritdoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//input[@type='checkbox' and not(contains(@data-validate, 'validate-one-required-by-name'))" + . "and contains(@class, 'bundle-option')]/../label//span[normalize-space(text()) = '1 x %s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php new file mode 100644 index 0000000000000..128fbe56185f3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/DropDownOptionViewTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks dropdown bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class DropDownOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_dropdown_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-dropdown-options', + 'Dropdown Options', + $expectedSelectionsNames + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_dropdown_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-dropdown-required-options', + 'Dropdown Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/product.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product', 'Bundle Product Items'); + } + + /** + * @inheritdoc + */ + protected function getRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and contains(@data-validate, 'required:true')]" + . "/option/span[normalize-space(text()) = '%s']"; + } + + /** + * @inheritdoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and not(contains(@data-validate, 'required:true'))]" + . "/option/span[normalize-space(text()) = '%s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php new file mode 100644 index 0000000000000..234981f36fa94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/MultiselectOptionViewTest.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks multiselect bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class MultiselectOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-multiselect-options', + 'Multiselect Options', + $expectedSelectionsNames + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-multiselect-required-options', + 'Multiselect Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_multiselect_required_option.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product-multiselect-required-option', 'Multiselect Options'); + } + + /** + * @inheridoc + */ + protected function getRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and @multiple='multiple' " + . "and contains(@data-validate, 'required:true')]/option/span[normalize-space(text()) = '1 x %s']"; + } + + /** + * @inheridoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//select[contains(@id, 'bundle-option') and @multiple='multiple'" + . "and not(contains(@data-validate, 'required:true'))]/option/span[normalize-space(text()) = '1 x %s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php new file mode 100644 index 0000000000000..b58f5f7d4189e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/Block/Catalog/Product/View/Type/RadioOptionViewTest.php @@ -0,0 +1,73 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Bundle\Block\Catalog\Product\View\Type; + +/** + * Class checks radio buttons bundle options appearance + * + * @magentoAppArea frontend + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class RadioOptionViewTest extends AbstractBundleOptionsViewTest +{ + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_options.php + * + * @return void + */ + public function testNotRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView('bundle-product-radio-options', 'Radio Options', $expectedSelectionsNames); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_required_options.php + * + * @return void + */ + public function testRequiredSelectMultiSelectionsView(): void + { + $expectedSelectionsNames = ['Simple Product', 'Simple Product2']; + $this->processMultiSelectionsView( + 'bundle-product-radio-required-options', + 'Radio Options', + $expectedSelectionsNames, + true + ); + } + + /** + * @magentoDataFixture Magento/Bundle/_files/bundle_product_radio_required_option.php + * + * @return void + */ + public function testShowSingle(): void + { + $this->processSingleSelectionView('bundle-product-radio-required-option', 'Radio Options'); + } + + /** + * @inheritdoc + */ + protected function getRequiredSelectXpath(): string + { + return "//input[@type='radio' and contains(@data-validate, 'validate-one-required-by-name')" + . "and contains(@class, 'bundle option')]/../label//span[normalize-space(text()) = '%s']"; + } + + /** + * @inheritdoc + */ + protected function getNotRequiredSelectXpath(): string + { + return "//input[@type='radio' and not(contains(@data-validate, 'validate-one-required-by-name'))" + . "and contains(@class, 'bundle option')]/../label//span[normalize-space(text()) = '%s']"; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php new file mode 100644 index 0000000000000..f9636890e61f6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-checkbox-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Checkbox Options', + 'default_title' => 'Checkbox Options', + 'type' => 'checkbox', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); + +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php new file mode 100644 index 0000000000000..3475737790c86 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-checkbox-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php new file mode 100644 index 0000000000000..453b531f75b2d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-checkbox-required-option') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Checkbox Options', + 'default_title' => 'Checkbox Options', + 'type' => 'checkbox', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_rollback.php new file mode 100644 index 0000000000000..f75241fb8b680 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_option_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\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-checkbox-required-option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php new file mode 100644 index 0000000000000..9b84d1236c5c9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-checkbox-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Checkbox Options', + 'default_title' => 'Checkbox Options', + 'type' => 'checkbox', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php new file mode 100644 index 0000000000000..f601d1d6793e6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_checkbox_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-checkbox-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php new file mode 100644 index 0000000000000..06f6473802ee2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-dropdown-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Dropdown Options', + 'default_title' => 'Dropdown Options', + 'type' => 'select', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php new file mode 100644 index 0000000000000..857e44d0298cb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-dropdown-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php new file mode 100644 index 0000000000000..1789f472f968d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-dropdown-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Dropdown Options', + 'default_title' => 'Dropdown Options', + 'type' => 'select', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php new file mode 100644 index 0000000000000..ad4464153dbf1 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_dropdown_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-dropdown-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php new file mode 100644 index 0000000000000..a5667b89f8bf4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-multiselect-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Multiselect Options', + 'default_title' => 'Multiselect Options', + 'type' => 'multi', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php new file mode 100644 index 0000000000000..c02f218131b20 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-multiselect-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php new file mode 100644 index 0000000000000..7789045f6f7ef --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-multiselect-required-option') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Multiselect Options', + 'default_title' => 'Multiselect Options', + 'type' => 'multi', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_rollback.php new file mode 100644 index 0000000000000..8e16ecb5f3890 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_option_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\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-multiselect-required-option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php new file mode 100644 index 0000000000000..65bb49f3b6122 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-multiselect-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Multiselect Options', + 'default_title' => 'Multiselect Options', + 'type' => 'multi', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php new file mode 100644 index 0000000000000..bf78eece56938 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_multiselect_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-multiselect-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php new file mode 100644 index 0000000000000..def31b48b2172 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options.php @@ -0,0 +1,76 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-radio-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Radio Options', + 'default_title' => 'Radio Options', + 'type' => 'radio', + 'required' => 0, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php new file mode 100644 index 0000000000000..5630b2f88ba4d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-radio-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php new file mode 100644 index 0000000000000..c659387e09dcc --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option.php @@ -0,0 +1,68 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-radio-required-option') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Radio Options', + 'default_title' => 'Radio Options', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_rollback.php new file mode 100644 index 0000000000000..9a44ccbca6110 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_option_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\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-radio-required-option', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php new file mode 100644 index 0000000000000..ec28bf556b69c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Bundle\Model\PrepareBundleLinks; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple.php'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$baseWebsiteId = $websiteRepository->get('base')->getId(); +/** @var PrepareBundleLinks $prepareBundleLinks */ +$prepareBundleLinks = $objectManager->get(PrepareBundleLinks::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$productRepository->cleanCache(); +/** @var ProductFactory $productFactory */ +$productFactory = $objectManager->get(ProductFactory::class); +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($product->getDefaultAttributeSetId()) + ->setWebsiteIds([$baseWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle-product-radio-required-options') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1]) + ->setPriceView(0) + ->setSkuType(1) + ->setWeightType(1) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setPrice(10.0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER); + +$bundleOptionsData = [ + [ + 'title' => 'Radio Options', + 'default_title' => 'Radio Options', + 'type' => 'radio', + 'required' => 1, + 'delete' => '', + ], +]; +$bundleSelectionsData = [ + [ + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + ], + [ + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_price_value' => 0, + 'selection_can_change_qty' => 1, + 'delete' => '', + 'option_id' => 1, + ], +]; + +$bundleProduct = $prepareBundleLinks->execute($bundleProduct, $bundleOptionsData, [$bundleSelectionsData]); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php new file mode 100644 index 0000000000000..8536a76979430 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/bundle_product_radio_required_options_rollback.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/../../../Magento/Catalog/_files/product_simple_duplicated_rollback.php'; +require __DIR__ . '/../../../Magento/Catalog/_files/second_product_simple_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +try { + $product = $productRepository->get('bundle-product-radio-required-options', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { + //Product already removed +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php new file mode 100644 index 0000000000000..c9e2bfb74090b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Bundle\Api\Data\LinkInterfaceFactory; +use Magento\Bundle\Api\Data\OptionInterfaceFactory; +use Magento\Bundle\Model\Product\Price; +use Magento\Catalog\Api\Data\ProductExtensionFactory; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Type\AbstractType; +use Magento\Catalog\Model\Product\Visibility; + +require __DIR__ . '/multiple_products.php'; + +/** @var ProductExtensionFactory $extensionAttributesFactory */ +$extensionAttributesFactory = $objectManager->get(ProductExtensionFactory::class); +/** @var OptionInterfaceFactory $optionFactory */ +$optionFactory = $objectManager->get(OptionInterfaceFactory::class); +/** @var LinkInterfaceFactory $linkFactory */ +$linkFactory = $objectManager->get(LinkInterfaceFactory::class); + +$bundleProduct = $productFactory->create(); +$bundleProduct->setTypeId(Type::TYPE_BUNDLE) + ->setAttributeSetId($bundleProduct->getDefaultAttributeSetId()) + ->setWebsiteIds([$defaultWebsiteId]) + ->setName('Bundle Product') + ->setSku('bundle_product') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ) + ->setSkuType(0) + ->setPriceView(0) + ->setPriceType(Price::PRICE_TYPE_DYNAMIC) + ->setWeightType(0) + ->setShipmentType(AbstractType::SHIPMENT_TOGETHER) + ->setBundleOptionsData( + [ + [ + 'title' => 'Option 1', + 'default_title' => 'Option 1', + 'type' => 'multi', + 'required' => 1, + ], + ] + )->setBundleSelectionsData( + [ + [ + [ + 'product_id' => $product->getId(), + 'sku' => $product->getSku(), + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'product_id' => $product2->getId(), + 'sku' => $product2->getSku(), + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + ], + [ + 'product_id' => $product3->getId(), + 'sku' => $product3->getSku(), + 'selection_qty' => 1, + 'selection_can_change_qty' => 1, + ], + ] + ] + ); + +$options = []; +foreach ($bundleProduct->getBundleOptionsData() as $key => $optionData) { + $option = $optionFactory->create(['data' => $optionData]); + $option->setSku($bundleProduct->getSku()); + $option->setOptionId(null); + $links = []; + foreach ($bundleProduct->getBundleSelectionsData()[$key] as $linkData) { + $link = $linkFactory->create(['data' => $linkData]); + $links[] = $link; + } + $option->setProductLinks($links); + $options[] = $option; +} +$extensionAttributes = $bundleProduct->getExtensionAttributes() ?: $extensionAttributesFactory->create(); +$extensionAttributes->setBundleProductOptions($options); +$bundleProduct->setExtensionAttributes($extensionAttributes); +$productRepository->save($bundleProduct); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php new file mode 100644 index 0000000000000..25ebbd14bfa39 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/dynamic_bundle_product_multiselect_option_rollback.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Exception\NoSuchEntityException; + +require __DIR__ . '/multiple_products_rollback.php'; + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $product = $productRepository->get('bundle_product', false, null, true); + $productRepository->delete($product); +} catch (NoSuchEntityException $e) { +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php index 662b69c89bc6d..fa957a0bfd3f8 100644 --- a/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php +++ b/dev/tests/integration/testsuite/Magento/Bundle/_files/multiple_products.php @@ -3,31 +3,44 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Model\Product\Type as ProductType; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Msrp\Model\Product\Attribute\Source\Type; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; -/** @var $product \Magento\Catalog\Model\Product */ -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +/** @var WebsiteRepositoryInterface $websiteRepository */ +$websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); +$defaultWebsiteId = $websiteRepository->get('base')->getId(); +$product = $productFactory->create(); +$product->setTypeId(ProductType::TYPE_SIMPLE) ->setId(10) - ->setAttributeSetId(4) + ->setAttributeSetId($product->getDefaultAttributeSetId()) ->setName('Simple Product') ->setSku('simple1') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART) ->setPrice(10) ->setWeight(1) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -36,29 +49,27 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); - $productRepository->save($product); -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product2 = $productFactory->create(); +$product2->setTypeId(ProductType::TYPE_SIMPLE) ->setId(11) - ->setAttributeSetId(4) + ->setAttributeSetId($product2->getDefaultAttributeSetId()) ->setName('Simple Product2') ->setSku('simple2') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_ON_GESTURE) + ->setMsrpDisplayActualPriceType(Type::TYPE_ON_GESTURE) ->setPrice(20) ->setWeight(1) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_IN_CATALOG) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -67,24 +78,22 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); +$productRepository->save($product2); -$productRepository->save($product); - -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product3 = $productFactory->create(); +$product3->setTypeId(ProductType::TYPE_SIMPLE) ->setId(12) - ->setAttributeSetId(4) + ->setAttributeSetId($product3->getDefaultAttributeSetId()) ->setName('Simple Product 3') ->setSku('simple3') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setPrice(30) ->setWeight(1) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_IN_CATALOG) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_IN_CATALOG) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -93,29 +102,27 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); +$productRepository->save($product3); -$productRepository->save($product); - -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product4 = $productFactory->create(); +$product4->setTypeId(ProductType::TYPE_SIMPLE) ->setId(13) - ->setAttributeSetId(4) + ->setAttributeSetId($product4->getDefaultAttributeSetId()) ->setName('Simple Product 4') ->setSku('simple4') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART) ->setPrice(13) ->setWeight(12) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -124,29 +131,27 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); +$productRepository->save($product4); -$productRepository->save($product); - -$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); -$product->isObjectNew(true); -$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +$product5 = $productFactory->create(); +$product5->setTypeId(ProductType::TYPE_SIMPLE) ->setId(14) - ->setAttributeSetId(4) + ->setAttributeSetId($product5->getDefaultAttributeSetId()) ->setName('Simple Product 5') ->setSku('simple5') - ->setTaxClassId('none') + ->setTaxClassId(0) ->setDescription('description') ->setShortDescription('short description') ->setOptionsContainer('container1') - ->setMsrpDisplayActualPriceType(\Magento\Msrp\Model\Product\Attribute\Source\Type::TYPE_IN_CART) + ->setMsrpDisplayActualPriceType(Type::TYPE_IN_CART) ->setPrice(14) ->setWeight(10) ->setMetaTitle('meta title') ->setMetaKeyword('meta keyword') ->setMetaDescription('meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setWebsiteIds([1]) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([$defaultWebsiteId]) ->setCategoryIds([]) ->setStockData([ 'use_config_manage_stock' => 1, @@ -155,5 +160,4 @@ 'is_in_stock' => 1, 'manage_stock' => 1, ]); - -$productRepository->save($product); +$productRepository->save($product5); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php index f979bad9d0f76..a4631526bd4c5 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php @@ -19,9 +19,15 @@ * * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php */ class CreateCustomOptionsTest extends AbstractBackendController { + /** + * @var string + */ + protected $productSku = 'simple'; + /** * @var ProductRepositoryInterface */ @@ -46,8 +52,6 @@ protected function setUp() /** * Test add to product custom option with type "field". * - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - * * @dataProvider productWithNewOptionsDataProvider * * @param array $productPostData @@ -57,7 +61,7 @@ protected function setUp() public function testSaveCustomOptionWithTypeField(array $productPostData): void { $this->getRequest()->setPostValue($productPostData); - $product = $this->productRepository->get('simple'); + $product = $this->productRepository->get($this->productSku); $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->dispatch('backend/catalog/product/save/id/' . $product->getEntityId()); $this->assertSessionMessages( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php index f1af6e6e41cff..6a4ff066f710d 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php @@ -21,9 +21,15 @@ * * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php */ class DeleteCustomOptionsTest extends AbstractBackendController { + /** + * @var string + */ + protected $productSku = 'simple'; + /** * @var ProductRepositoryInterface */ @@ -54,8 +60,6 @@ protected function setUp() /** * Test delete custom option with type "field". * - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - * * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForCreateOptions * * @param array $optionData @@ -63,7 +67,7 @@ protected function setUp() */ public function testDeleteCustomOptionWithTypeField(array $optionData): void { - $product = $this->productRepository->get('simple'); + $product = $this->productRepository->get($this->productSku); /** @var ProductCustomOptionInterface $option */ $option = $this->optionRepositoryFactory->create(['data' => $optionData]); $option->setProductSku($product->getSku()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php index a45c21444a5d7..1badf6a1a081a 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php @@ -22,9 +22,15 @@ * * @magentoAppArea adminhtml * @magentoDbIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_without_options.php */ class UpdateCustomOptionsTest extends AbstractBackendController { + /** + * @var string + */ + protected $productSku = 'simple'; + /** * @var ProductRepositoryInterface */ @@ -55,8 +61,6 @@ protected function setUp() /** * Test add to product custom option with type "field". * - * @magentoDataFixture Magento/Catalog/_files/product_without_options.php - * * @dataProvider \Magento\TestFramework\Catalog\Model\Product\Option\DataProvider\Type\Field::getDataForUpdateOptions * * @param array $optionData @@ -65,7 +69,7 @@ protected function setUp() */ public function testUpdateCustomOptionWithTypeField(array $optionData, array $updateData): void { - $product = $this->productRepository->get('simple'); + $product = $this->productRepository->get($this->productSku); /** @var ProductCustomOptionInterface|Option $option */ $option = $this->optionRepositoryFactory->create(['data' => $optionData]); $option->setProductSku($product->getSku()); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php index f3989248a8ed1..602af4d0e3c78 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Layer/Filter/_files/attribute_with_option_rollback.php @@ -32,7 +32,7 @@ \Magento\Catalog\Model\Product::class ); $product = $product->loadByAttribute('sku', 'simple_product_' . $option->getId()); - if ($product->getId()) { + if ($product instanceof \Magento\Catalog\Model\Product && $product->getId()) { $product->delete(); } } diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php new file mode 100644 index 0000000000000..20cfbda0ac920 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/CreateCustomOptionsTest.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Controller\Adminhtml\Product\Save\CreateCustomOptionsTest as SimpleProductOptionsTest; + +/** + * Base test cases for configurable product custom options with type "field". + * Option add via dispatch product controller action save with options data in POST data. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ +class CreateCustomOptionsTest extends SimpleProductOptionsTest +{ + /** + * @var string + */ + protected $productSku = 'configurable'; +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php new file mode 100644 index 0000000000000..58b7f0e56fb0c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/DeleteCustomOptionsTest.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Controller\Adminhtml\Product\Save\DeleteCustomOptionsTest as SimpleProductOptionsTest; + +/** + * Base test cases for delete configurable product custom option with type "field". + * Option deleting via product controller action save. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ +class DeleteCustomOptionsTest extends SimpleProductOptionsTest +{ + /** + * @var string + */ + protected $productSku = 'configurable'; +} diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php new file mode 100644 index 0000000000000..ab0b03489cb92 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Save/UpdateCustomOptionsTest.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\ConfigurableProduct\Controller\Adminhtml\Product\Save; + +use Magento\Catalog\Controller\Adminhtml\Product\Save\UpdateCustomOptionsTest as SimpleProductOptionsTest; + +/** + * Base test cases for update configurable product custom options with type "field". + * Option updating via dispatch product controller action save with updated options data in POST data. + * + * @magentoAppArea adminhtml + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php + */ +class UpdateCustomOptionsTest extends SimpleProductOptionsTest +{ + /** + * @var string + */ + protected $productSku = 'configurable'; +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Block/Account/ResetPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Block/Account/ResetPasswordTest.php new file mode 100644 index 0000000000000..cc087e006025d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Block/Account/ResetPasswordTest.php @@ -0,0 +1,86 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Block\Account; + +use Magento\Framework\Math\Random; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use PHPUnit\Framework\TestCase; + +/** + * Class checks password reset block output + * + * @see \Magento\Customer\Block\Account\Resetpassword + * @magentoAppArea frontend + */ +class ResetPasswordTest extends TestCase +{ + private const FORM_XPATH = "//form[contains(@action, '?token=%s')]"; + private const SET_NEW_PASSWORD_BUTTON_XPATH = "//button/span[contains(text(),'Set a New Password')]"; + private const NEW_PASSWORD_LABEL_XPATH = "//label[@for='password']/span[contains(text(), 'New Password')]"; + private const PASSWORD_CONFIRMATION_LABEL_XPATH = "//label[@for='password-confirmation']" + . "/span[contains(text(), 'Confirm New Password')]"; + + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var LayoutInterface */ + private $layout; + + /** @var Resetpassword */ + private $block; + + /** @var Random */ + private $random; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->random = $this->objectManager->get(Random::class); + $this->block = $this->layout->createBlock(Resetpassword::class); + $this->block->setTemplate('Magento_Customer::form/resetforgottenpassword.phtml'); + } + + /** + * @return void + */ + public function testResetPasswordForm(): void + { + $token = $this->random->getUniqueHash(); + $this->block->setResetPasswordLinkToken($token); + $output = $this->block->toHtml(); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(sprintf(self::FORM_XPATH, $token), $output), + 'Form action does not include correct token' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::NEW_PASSWORD_LABEL_XPATH, $output), + 'New password label was not found on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::PASSWORD_CONFIRMATION_LABEL_XPATH, $output), + 'Confirm password label was not found on the page' + ); + $this->assertEquals( + 1, + Xpath::getElementsCountForXpath(self::SET_NEW_PASSWORD_BUTTON_XPATH, $output), + 'Set password button was not found on the page' + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index df4acf3acca91..18f9b2d2cb737 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -132,35 +132,6 @@ public function testLogoutAction() $this->assertRedirect($this->stringContains('customer/account/logoutSuccess')); } - /** - * Test that forgot password email message displays special characters correctly. - * - * @codingStandardsIgnoreStart - * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 - * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoConfigFixture current_store general/store_information/name Test special' characters - * @magentoConfigFixture current_store customer/captcha/enable 0 - * @magentoDataFixture Magento/Customer/_files/customer.php - * @codingStandardsIgnoreEnd - */ - public function testForgotPasswordEmailMessageWithSpecialCharacters() - { - $email = 'customer@example.com'; - - $this->getRequest()->setPostValue(['email' => $email]); - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - - $this->dispatch('customer/account/forgotPasswordPost'); - $this->assertRedirect($this->stringContains('customer/account/')); - - $subject = $this->transportBuilderMock->getSentMessage()->getSubject(); - $this->assertContains( - 'Test special\' characters', - $subject - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ @@ -397,56 +368,6 @@ public function testActiveUserConfirmationAction() ); } - /** - * @codingStandardsIgnoreStart - * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 - * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template - * @magentoConfigFixture current_store customer/password/forgot_email_identity support - * @magentoConfigFixture current_store customer/captcha/enable 0 - * @magentoDataFixture Magento/Customer/_files/customer.php - * @codingStandardsIgnoreEnd - */ - public function testForgotPasswordPostAction() - { - $email = 'customer@example.com'; - - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - $this->getRequest()->setPostValue(['email' => $email]); - - $this->dispatch('customer/account/forgotPasswordPost'); - $this->assertRedirect($this->stringContains('customer/account/')); - - $message = __( - 'If there is an account associated with %1 you will receive an email with a link to reset your password.', - $email - ); - $this->assertSessionMessages( - $this->equalTo([$message]), - MessageInterface::TYPE_SUCCESS - ); - } - - /** - * @magentoConfigFixture current_store customer/captcha/enable 0 - */ - public function testForgotPasswordPostWithBadEmailAction() - { - $this->getRequest()->setMethod(HttpRequest::METHOD_POST); - $this->getRequest() - ->setPostValue( - [ - 'email' => 'bad@email', - ] - ); - - $this->dispatch('customer/account/forgotPasswordPost'); - $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); - $this->assertSessionMessages( - $this->equalTo(['The email address is incorrect. Verify the email address and try again.']), - MessageInterface::TYPE_ERROR - ); - } - /** * @magentoDataFixture Magento/Customer/_files/customer.php */ diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php new file mode 100644 index 0000000000000..bbaf55494294e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/CreatePasswordTest.php @@ -0,0 +1,97 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller; + +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResource; +use Magento\Customer\Model\Session; +use Magento\Framework\Math\Random; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\View\LayoutInterface; +use Magento\Store\Api\WebsiteRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class checks password forgot scenarios + * + * @magentoDbIsolation enabled + */ +class CreatePasswordTest extends AbstractController +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var Session */ + private $session; + + /** @var LayoutInterface */ + private $layout; + + /** @var Random */ + private $random; + + /** @var CustomerResource */ + private $customerResource; + + /** @var CustomerRegistry */ + private $customerRegistry; + + /** @var WebsiteRepositoryInterface */ + private $websiteRepository; + + /** @var int */ + private $customerId; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->session = $this->objectManager->get(Session::class); + $this->layout = $this->objectManager->get(LayoutInterface::class); + $this->random = $this->objectManager->get(Random::class); + $this->customerResource = $this->objectManager->get(CustomerResource::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + $this->customerRegistry->remove($this->customerId); + + parent::tearDown(); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer_with_website.php + * + * @return void + */ + public function testCreatePassword(): void + { + $defaultWebsite = $this->websiteRepository->get('base')->getId(); + $customer = $this->customerRegistry->retrieveByEmail('john.doe@magento.com', $defaultWebsite); + $this->customerId = $customer->getId(); + $token = $this->random->getUniqueHash(); + $customer->changeResetPasswordLinkToken($token); + $customer->setData('confirmation', 'confirmation'); + $this->customerResource->save($customer); + $this->session->setRpToken($token); + $this->session->setRpCustomerId($customer->getId()); + $this->dispatch('customer/account/createPassword'); + $block = $this->layout->getBlock('resetPassword'); + $this->assertEquals($token, $block->getResetPasswordLinkToken()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php new file mode 100644 index 0000000000000..8bfe3b5524487 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/ForgotPasswordPostTest.php @@ -0,0 +1,137 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Controller; + +use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Framework\Message\MessageInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use Magento\TestFramework\TestCase\AbstractController; + +/** + * Class checks password forgot scenarios + * + * @see \Magento\Customer\Controller\Account\ForgotPasswordPost + * @magentoDbIsolation enabled + */ +class ForgotPasswordPostTest extends AbstractController +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var TransportBuilderMock */ + private $transportBuilderMock; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class); + } + + /** + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @return void + */ + public function testWithoutEmail(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => '']); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertSessionMessages( + $this->equalTo([(string)__('Please enter your email.')]), + MessageInterface::TYPE_ERROR + ); + $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); + } + + /** + * Test that forgot password email message displays special characters correctly. + * + * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 + * @codingStandardsIgnoreStart + * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template + * @codingStandardsIgnoreEnd + * @magentoConfigFixture current_store customer/password/forgot_email_identity support + * @magentoConfigFixture current_store general/store_information/name Test special' characters + * @magentoConfigFixture current_store customer/captcha/enable 0 + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testForgotPasswordEmailMessageWithSpecialCharacters(): void + { + $email = 'customer@example.com'; + $this->getRequest()->setPostValue(['email' => $email]); + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSuccessSessionMessage($email); + $subject = $this->transportBuilderMock->getSentMessage()->getSubject(); + $this->assertContains('Test special\' characters', $subject); + } + + /** + * @magentoConfigFixture current_store customer/password/limit_password_reset_requests_method 0 + * @codingStandardsIgnoreStart + * @magentoConfigFixture current_store customer/password/forgot_email_template customer_password_forgot_email_template + * @codingStandardsIgnoreEnd + * @magentoConfigFixture current_store customer/password/forgot_email_identity support + * @magentoConfigFixture current_store customer/captcha/enable 0 + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testForgotPasswordPostAction(): void + { + $email = 'customer@example.com'; + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => $email]); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/')); + $this->assertSuccessSessionMessage($email); + } + + /** + * @magentoConfigFixture current_store customer/captcha/enable 0 + * + * @return void + */ + public function testForgotPasswordPostWithBadEmailAction(): void + { + $this->getRequest()->setMethod(HttpRequest::METHOD_POST); + $this->getRequest()->setPostValue(['email' => 'bad@email']); + $this->dispatch('customer/account/forgotPasswordPost'); + $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); + $this->assertSessionMessages( + $this->equalTo(['The email address is incorrect. Verify the email address and try again.']), + MessageInterface::TYPE_ERROR + ); + } + + /** + * Assert success session message + * + * @param string $email + * @return void + */ + private function assertSuccessSessionMessage(string $email): void + { + $message = __( + 'If there is an account associated with %1 you will receive an email with a link to reset your password.', + $email + ); + $this->assertSessionMessages($this->equalTo([$message]), MessageInterface::TYPE_SUCCESS); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php new file mode 100644 index 0000000000000..03473e9247c51 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/CreateAccountTest.php @@ -0,0 +1,177 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Customer\Api\Data\CustomerInterfaceFactory; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\Exception\InputException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Math\Random; +use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\Validator\Exception; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests for customer creation via customer account management service. + * + * @magentoAppArea frontend + * @magentoDbIsolation enabled + */ +class CreateAccountTest extends TestCase +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var AccountManagementInterface + */ + private $accountManagement; + + /** + * @var CustomerInterfaceFactory + */ + private $customerFactory; + + /** + * @var DataObjectHelper + */ + private $dataObjectHelper; + + /** + * @var array + */ + private $defaultCustomerData = [ + 'email' => 'customer@example.com', + 'firstname' => 'First name', + 'lastname' => 'Last name', + ]; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class); + $this->dataObjectHelper = $this->objectManager->create(DataObjectHelper::class); + parent::setUp(); + } + + /** + * @dataProvider createInvalidAccountDataProvider + * @param array $customerData + * @param string $password + * @param string $errorType + * @param string $errorMessage + * @return void + */ + public function testCreateAccountWithInvalidFields( + array $customerData, + string $password, + string $errorType, + array $errorMessage + ): void { + $data = array_merge($this->defaultCustomerData, $customerData); + $customerEntity = $this->customerFactory->create(); + $this->dataObjectHelper->populateWithArray($customerEntity, $data, CustomerInterface::class); + $this->expectException($errorType); + $this->expectExceptionMessage((string)__(...$errorMessage)); + $this->accountManagement->createAccount($customerEntity, $password); + } + + /** + * @return array + */ + public function createInvalidAccountDataProvider(): array + { + return [ + 'empty_firstname' => [ + 'customer_data' => ['firstname' => ''], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['"%1" is a required value.', 'First Name'], + ], + 'empty_lastname' => [ + 'customer_data' => ['lastname' => ''], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['"%1" is a required value.', 'Last Name'], + ], + 'empty_email' => [ + 'customer_data' => ['email' => ''], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['The customer email is missing. Enter and try again.'], + ], + 'invalid_email' => [ + 'customer_data' => ['email' => 'zxczxczxc'], + 'password' => '_aPassword1', + 'error_type' => Exception::class, + 'error_message' => ['"%1" is not a valid email address.', 'Email'], + ], + 'empty_password' => [ + 'customer_data' => [], + 'password' => '', + 'error_type' => InputException::class, + 'error_message' => ['The password needs at least 8 characters. Create a new password and try again.'], + ], + 'invalid_password_minimum_length' => [ + 'customer_data' => [], + 'password' => 'test', + 'error_type' => InputException::class, + 'error_message' => ['The password needs at least 8 characters. Create a new password and try again.'], + ], + 'invalid_password_maximum_length' => [ + 'customer_data' => [], + 'password' => $this->getRandomNumericString(257), + 'error_type' => InputException::class, + 'error_message' => ['Please enter a password with at most 256 characters.'], + ], + 'invalid_password_without_minimum_characters_classes' => [ + 'customer_data' => [], + 'password' => 'test_password', + 'error_type' => InputException::class, + 'error_message' => [ + 'Minimum of different classes of characters in password is %1.' + . ' Classes of characters: Lower Case, Upper Case, Digits, Special Characters.', + 3, + ], + ], + 'password_same_as_email' => [ + 'customer_data' => ['email' => 'test1@test.com'], + 'password' => 'test1@test.com', + 'error_type' => LocalizedException::class, + 'error_message' => [ + 'The password can\'t be the same as the email address. Create a new password and try again.', + ], + ], + ]; + } + + /** + * Returns random numeric string with given length. + * + * @param int $length + * @return string + */ + private function getRandomNumericString(int $length): string + { + $string = ''; + for ($i = 0; $i <= $length; $i++) { + $string .= Random::getRandomNumber(0, 9); + } + + return $string; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php new file mode 100644 index 0000000000000..7820316d9f41f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagement/ForgotPasswordTest.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\AccountManagement; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\AccountManagement; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\Xpath; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; +use PHPUnit\Framework\TestCase; + +/** + * Class checks password forgot scenarios + * + * @magentoDbIsolation enabled + */ +class ForgotPasswordTest extends TestCase +{ + /** @var ObjectManagerInterface */ + private $objectManager; + + /** @var AccountManagementInterface */ + private $accountManagement; + + /** @var TransportBuilderMock */ + private $transportBuilder; + + /** @var string */ + private $newPasswordLinkPath = "//a[contains(@href, 'customer/account/createPassword') " + . "and contains(text(), 'Set a New Password')]"; + + /** + * @inheritdoc + */ + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class); + $this->transportBuilder = $this->objectManager->get(TransportBuilderMock::class); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * + * @return void + */ + public function testForgotPassword(): void + { + $email = 'customer@example.com'; + $result = $this->accountManagement->initiatePasswordReset($email, AccountManagement::EMAIL_RESET); + $message = $this->transportBuilder->getSentMessage(); + $messageContent = $message->getBody()->getParts()[0]->getRawContent(); + $this->assertTrue($result); + $this->assertEquals(1, Xpath::getElementsCountForXpath($this->newPasswordLinkPath, $messageContent)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php new file mode 100644 index 0000000000000..ae65c32fe3f43 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/CreateAddressTest.php @@ -0,0 +1,368 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Api\Data\AddressInterfaceFactory; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\ResourceModel\Address; +use Magento\Framework\Exception\InputException; +use Magento\TestFramework\Directory\Model\GetRegionIdByName; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that address was created as expected or address create throws expected error. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @magentoDbIsolation enabled + */ +class CreateAddressTest extends TestCase +{ + /** + * Static customer address data. + */ + private const STATIC_CUSTOMER_ADDRESS_DATA = [ + AddressInterface::TELEPHONE => 3468676, + AddressInterface::POSTCODE => 75477, + AddressInterface::COUNTRY_ID => 'US', + 'custom_region_name' => 'Alabama', + AddressInterface::CITY => 'CityM', + AddressInterface::STREET => 'Green str, 67', + AddressInterface::LASTNAME => 'Smith', + AddressInterface::FIRSTNAME => 'John', + ]; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var GetRegionIdByName + */ + private $getRegionIdByName; + + /** + * @var AddressInterfaceFactory + */ + private $addressFactory; + + /** + * @var AddressRegistry + */ + private $addressRegistry; + + /** + * @var Address + */ + private $addressResource; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var int[] + */ + private $createdAddressesIds = []; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->getRegionIdByName = $this->objectManager->get(GetRegionIdByName::class); + $this->addressFactory = $this->objectManager->get(AddressInterfaceFactory::class); + $this->addressRegistry = $this->objectManager->get(AddressRegistry::class); + $this->addressResource = $this->objectManager->get(Address::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + foreach ($this->createdAddressesIds as $createdAddressesId) { + $this->addressRegistry->remove($createdAddressesId); + } + parent::tearDown(); + } + + /** + * Assert that default addresses properly created for customer. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @dataProvider createDefaultAddressesDataProvider + * + * @param array $addressData + * @param bool $isShippingDefault + * @param bool $isBillingDefault + * @return void + */ + public function testCreateDefaultAddress( + array $addressData, + bool $isShippingDefault, + bool $isBillingDefault + ): void { + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertNull($customer->getDefaultShipping(), 'Customer already has default shipping address'); + $this->assertNull($customer->getDefaultBilling(), 'Customer already has default billing address'); + $address = $this->createAddress( + (int)$customer->getId(), + $addressData, + $isShippingDefault, + $isBillingDefault + ); + $expectedShipping = $isShippingDefault ? $address->getId() : null; + $expectedBilling = $isBillingDefault ? $address->getId() : null; + $customer = $this->customerRepository->get('customer5@example.com'); + $this->assertEquals($expectedShipping, $customer->getDefaultShipping()); + $this->assertEquals($expectedBilling, $customer->getDefaultBilling()); + } + + /** + * Data provider for create default or not default address. + * + * @return array + */ + public function createDefaultAddressesDataProvider(): array + { + return [ + 'any_addresses_are_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, false, false], + 'shipping_address_is_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, true, false], + 'billing_address_is_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, false, true], + 'all_addresses_are_default' => [self::STATIC_CUSTOMER_ADDRESS_DATA, true, true], + ]; + } + + /** + * Assert that address created successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @dataProvider createAddressesDataProvider + * + * @param array $addressData + * @param array $expectedData + * @return void + */ + public function testAddressCreatedWithProperData(array $addressData, array $expectedData): void + { + if (isset($expectedData['custom_region_name'])) { + $expectedData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $expectedData['custom_region_name'], + $expectedData[AddressInterface::COUNTRY_ID] + ); + unset($expectedData['custom_region_name']); + } + $customer = $this->customerRepository->get('customer5@example.com'); + $createdAddressData = $this->createAddress((int)$customer->getId(), $addressData)->__toArray(); + foreach ($expectedData as $fieldCode => $expectedValue) { + $this->assertTrue(isset($createdAddressData[$fieldCode]), "Field $fieldCode wasn't found."); + $this->assertEquals($createdAddressData[$fieldCode], $expectedValue); + } + } + + /** + * Data provider for create address with proper data. + * + * @return array + */ + public function createAddressesDataProvider(): array + { + return [ + 'required_fields_valid_data' => [ + self::STATIC_CUSTOMER_ADDRESS_DATA, + [ + AddressInterface::TELEPHONE => 3468676, + AddressInterface::COUNTRY_ID => 'US', + AddressInterface::POSTCODE => 75477, + 'custom_region_name' => 'Alabama', + AddressInterface::FIRSTNAME => 'John', + AddressInterface::LASTNAME => 'Smith', + AddressInterface::STREET => ['Green str, 67'], + AddressInterface::CITY => 'CityM', + ], + ], + 'required_field_empty_postcode_for_uk' => [ + array_replace( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::POSTCODE => '', AddressInterface::COUNTRY_ID => 'GB'] + ), + [ + AddressInterface::COUNTRY_ID => 'GB', + AddressInterface::POSTCODE => null, + ], + ], + 'required_field_empty_region_id_for_ua' => [ + array_replace( + self::STATIC_CUSTOMER_ADDRESS_DATA, + [AddressInterface::REGION_ID => '', AddressInterface::COUNTRY_ID => 'UA'] + ), + [ + AddressInterface::COUNTRY_ID => 'UA', + AddressInterface::REGION => [ + 'region' => null, + 'region_code' => null, + 'region_id' => 0, + ], + ], + ], + 'required_field_street_as_array' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => ['', 'Green str, 67']]), + [AddressInterface::STREET => ['Green str, 67']], + ], + 'field_name_prefix' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::PREFIX => 'My prefix']), + [AddressInterface::PREFIX => 'My prefix'], + ], + 'field_middle_name_initial' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::MIDDLENAME => 'My middle name']), + [AddressInterface::MIDDLENAME => 'My middle name'], + ], + 'field_name_suffix' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::SUFFIX => 'My suffix']), + [AddressInterface::SUFFIX => 'My suffix'], + ], + 'field_company_name' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::COMPANY => 'My company']), + [AddressInterface::COMPANY => 'My company'], + ], + 'field_vat_number' => [ + array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::VAT_ID => 'My VAT number']), + [AddressInterface::VAT_ID => 'My VAT number'], + ], + ]; + } + + /** + * Assert that proper error message has thrown if address creating with wrong data. + * + * @magentoDataFixture Magento/Customer/_files/customer_no_address.php + * + * @dataProvider createWrongAddressesDataProvider + * + * @param array $addressData + * @param \Exception $expectException + * @return void + */ + public function testExceptionThrownDuringCreateAddress(array $addressData, \Exception $expectException): void + { + $customer = $this->customerRepository->get('customer5@example.com'); + $this->expectExceptionObject($expectException); + $this->createAddress((int)$customer->getId(), $addressData); + } + + /** + * Data provider for create address with wrong data. + * + * @return array + */ + public function createWrongAddressesDataProvider(): array + { + return [ + 'required_field_empty_telephone' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::TELEPHONE => '']), + InputException::requiredField('telephone'), + ], + 'required_field_empty_postcode_for_us' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::POSTCODE => '']), + InputException::requiredField('postcode'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'required_field_empty_region_id_for_us' => [ +// array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::REGION_ID => '']), +// InputException::requiredField('regionId'), +// ], + 'required_field_empty_firstname' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::FIRSTNAME => '']), + InputException::requiredField('firstname'), + ], + 'required_field_empty_lastname' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::LASTNAME => '']), + InputException::requiredField('lastname'), + ], + 'required_field_empty_street_as_string' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => '']), + InputException::requiredField('street'), + ], + 'required_field_empty_street_as_array' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::STREET => []]), + InputException::requiredField('street'), + ], + 'required_field_empty_city' => [ + array_replace(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::CITY => '']), + InputException::requiredField('city'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'field_invalid_vat_number' => [ +// array_merge(self::STATIC_CUSTOMER_ADDRESS_DATA, [AddressInterface::VAT_ID => '/>.<*']), +// null// It need to create some error but currently magento doesn't has validation for this field., +// ], + ]; + } + + /** + * Create customer address with provided address data. + * + * @param int $customerId + * @param array $addressData + * @param bool $isDefaultShipping + * @param bool $isDefaultBilling + * @return AddressInterface + */ + private function createAddress( + int $customerId, + array $addressData, + bool $isDefaultShipping = false, + bool $isDefaultBilling = false + ): AddressInterface { + if (isset($addressData['custom_region_name'])) { + $addressData[AddressInterface::REGION_ID] = $this->getRegionIdByName->execute( + $addressData['custom_region_name'], + $addressData[AddressInterface::COUNTRY_ID] + ); + unset($addressData['custom_region_name']); + } + + $addressData['attribute_set_id'] = $this->addressResource->getEntityType()->getDefaultAttributeSetId(); + $address = $this->addressFactory->create(['data' => $addressData]); + $address->setCustomerId($customerId); + $address->setIsDefaultShipping($isDefaultShipping); + $address->setIsDefaultBilling($isDefaultBilling); + $address = $this->addressRepository->save($address); + $this->customerRegistry->remove($customerId); + $this->addressRegistry->remove($address->getId()); + $this->createdAddressesIds[] = (int)$address->getId(); + + return $address; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php new file mode 100644 index 0000000000000..fe5437e294fc6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/DeleteAddressTest.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that address was deleted successfully. + * + * @magentoDbIsolation enabled + */ +class DeleteAddressTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + parent::setUp(); + } + + /** + * Assert that address deleted successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @return void + */ + public function testDeleteDefaultAddress(): void + { + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping()); + $this->assertEquals(1, $customer->getDefaultBilling()); + $customerAddresses = $customer->getAddresses() ?? []; + foreach ($customerAddresses as $address) { + $this->addressRepository->delete($address); + } + $this->customerRegistry->remove($customer->getId()); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertNull($customer->getDefaultShipping()); + $this->assertNull($customer->getDefaultBilling()); + } + + /** + * Assert that deleting non-existent address throws exception. + * + * @expectedException \Magento\Framework\Exception\NoSuchEntityException + * @expectedExceptionMessage No such entity with addressId = 1 + * + * @return void + */ + public function testDeleteMissingAddress(): void + { + $this->addressRepository->deleteById(1); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php new file mode 100644 index 0000000000000..8867f269cdf37 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/Address/UpdateAddressTest.php @@ -0,0 +1,294 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\Address; + +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\AddressRegistry; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\ResourceModel\Address; +use Magento\Framework\Exception\InputException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Assert that address was updated as expected or address update throws expected error. + * + * @magentoDbIsolation enabled + */ +class UpdateAddressTest extends TestCase +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var AddressRegistry + */ + private $addressRegistry; + + /** + * @var Address + */ + private $addressResource; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var AddressRepositoryInterface + */ + private $addressRepository; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var int[] + */ + private $processedAddressesIds = []; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->addressRegistry = $this->objectManager->get(AddressRegistry::class); + $this->addressResource = $this->objectManager->get(Address::class); + $this->customerRegistry = $this->objectManager->get(CustomerRegistry::class); + $this->addressRepository = $this->objectManager->get(AddressRepositoryInterface::class); + $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class); + parent::setUp(); + } + + /** + * @inheritdoc + */ + protected function tearDown() + { + foreach ($this->processedAddressesIds as $createdAddressesId) { + $this->addressRegistry->remove($createdAddressesId); + } + parent::tearDown(); + } + + /** + * Assert that default addresses properly updated for customer. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateAddressIsDefaultDataProvider + * + * @param bool $isShippingDefault + * @param bool $isBillingDefault + * @param int|null $expectedShipping + * @param int|null $expectedBilling + * @return void + */ + public function testUpdateAddressIsDefault( + bool $isShippingDefault, + bool $isBillingDefault, + ?int $expectedShipping, + ?int $expectedBilling + ): void { + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals(1, $customer->getDefaultShipping()); + $this->assertEquals(1, $customer->getDefaultBilling()); + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + $address->setIsDefaultShipping($isShippingDefault); + $address->setIsDefaultBilling($isBillingDefault); + $this->addressRepository->save($address); + $this->customerRegistry->remove(1); + $customer = $this->customerRepository->get('customer@example.com'); + $this->assertEquals($customer->getDefaultShipping(), $expectedShipping); + $this->assertEquals($customer->getDefaultBilling(), $expectedBilling); + } + + /** + * Data provider for update address as default billing or default shipping. + * + * @return array + */ + public function updateAddressIsDefaultDataProvider(): array + { + return [ + 'update_shipping_address_default' => [true, false, 1, null], + 'update_billing_address_default' => [false, true, null, 1], + ]; + } + + /** + * Assert that address updated successfully. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateAddressesDataProvider + * + * @param array $updateData + * @param array $expectedData + * @return void + */ + public function testUpdateAddress(array $updateData, array $expectedData): void + { + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + foreach ($updateData as $setFieldName => $setValue) { + $address->setData($setFieldName, $setValue); + } + $updatedAddressData = $this->addressRepository->save($address)->__toArray(); + foreach ($expectedData as $getFieldName => $getValue) { + $this->assertTrue(isset($updatedAddressData[$getFieldName]), "Field $getFieldName wasn't found."); + $this->assertEquals($getValue, $updatedAddressData[$getFieldName]); + } + } + + /** + * Data provider for update address with proper data. + * + * @return array + */ + public function updateAddressesDataProvider(): array + { + return [ + 'required_field_telephone' => [ + [AddressInterface::TELEPHONE => 251512979595], + [AddressInterface::TELEPHONE => 251512979595], + ], + 'required_field_postcode' => [ + [AddressInterface::POSTCODE => 55425], + [AddressInterface::POSTCODE => 55425], + ], + 'required_field_empty_postcode_for_uk' => [ + [AddressInterface::COUNTRY_ID => 'GB', AddressInterface::POSTCODE => ''], + [AddressInterface::COUNTRY_ID => 'GB', AddressInterface::POSTCODE => null], + ], + 'required_field_empty_region_id_for_ua' => [ + [AddressInterface::COUNTRY_ID => 'UA', AddressInterface::REGION_ID => ''], + [ + AddressInterface::COUNTRY_ID => 'UA', + AddressInterface::REGION_ID => 0, + ], + ], + 'required_field_firstname' => [ + [AddressInterface::FIRSTNAME => 'Test firstname'], + [AddressInterface::FIRSTNAME => 'Test firstname'], + ], + 'required_field_lastname' => [ + [AddressInterface::LASTNAME => 'Test lastname'], + [AddressInterface::LASTNAME => 'Test lastname'], + ], + 'required_field_street_as_array' => [ + [AddressInterface::STREET => ['', 'Test str, 55']], + [AddressInterface::STREET => ['Test str, 55']], + ], + 'required_field_city' => [ + [AddressInterface::CITY => 'Test city'], + [AddressInterface::CITY => 'Test city'], + ], + 'field_name_prefix' => [ + [AddressInterface::PREFIX => 'My prefix'], + [AddressInterface::PREFIX => 'My prefix'], + ], + 'field_middle_name_initial' => [ + [AddressInterface::MIDDLENAME => 'My middle name'], + [AddressInterface::MIDDLENAME => 'My middle name'], + ], + 'field_name_suffix' => [ + [AddressInterface::SUFFIX => 'My suffix'], + [AddressInterface::SUFFIX => 'My suffix'], + ], + 'field_company_name' => [ + [AddressInterface::COMPANY => 'My company'], + [AddressInterface::COMPANY => 'My company'], + ], + 'field_vat_number' => [ + [AddressInterface::VAT_ID => 'My VAT number'], + [AddressInterface::VAT_ID => 'My VAT number'], + ], + ]; + } + + /** + * Assert that error message has thrown during process address update. + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * + * @dataProvider updateWrongAddressesDataProvider + * + * @param array $updateData + * @param \Exception $expectException + * @return void + */ + public function testExceptionThrownDuringUpdateAddress(array $updateData, \Exception $expectException): void + { + $this->processedAddressesIds[] = 1; + $address = $this->addressRepository->getById(1); + foreach ($updateData as $setFieldName => $setValue) { + $address->setData($setFieldName, $setValue); + } + $this->expectExceptionObject($expectException); + $this->addressRepository->save($address); + } + + /** + * Data provider for update address with proper data or with error. + * + * @return array + */ + public function updateWrongAddressesDataProvider(): array + { + return [ + 'required_field_empty_telephone' => [ + [AddressInterface::TELEPHONE => ''], + InputException::requiredField('telephone'), + ], + 'required_field_empty_postcode_for_us' => [ + [AddressInterface::POSTCODE => ''], + InputException::requiredField('postcode'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'required_field_empty_region_id_for_us' => [ +// [AddressInterface::REGION_ID => ''], +// InputException::requiredField('regionId'), +// ], + 'required_field_empty_firstname' => [ + [AddressInterface::FIRSTNAME => ''], + InputException::requiredField('firstname'), + ], + 'required_field_empty_lastname' => [ + [AddressInterface::LASTNAME => ''], + InputException::requiredField('lastname'), + ], + 'required_field_empty_street_as_array' => [ + [AddressInterface::STREET => []], + InputException::requiredField('street'), + ], + 'required_field_empty_city' => [ + [AddressInterface::CITY => ''], + InputException::requiredField('city'), + ], +// TODO: Uncomment this variation after fix issue https://jira.corp.magento.com/browse/MC-31031 +// 'field_invalid_vat_number' => [ +// [AddressInterface::VAT_ID => '/>.<*'], +// null// It need to create some error but currently magento doesn't has validation for this field., +// ], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php new file mode 100644 index 0000000000000..9909856322e13 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_no_address_rollback.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +/** @var CustomerRepositoryInterface $customerRepository */ +$customerRepository = $objectManager->get(CustomerRepositoryInterface::class); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->get(CustomerRegistry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +try { + $customerRegistry->remove(5); + $customerRepository->deleteById(5); +} catch (NoSuchEntityException $e) { + //Customer already deleted. +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php b/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php new file mode 100644 index 0000000000000..8856356227ed9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GroupedProduct/Block/Order/Email/Items/CreditMemo/GroupedTest.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GroupedProduct\Block\Order\Email\Items\CreditMemo; + +use Magento\Sales\Block\Order\Email\Items\DefaultItems; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Verify grouped product block will output correct data. + * + * @magentoAppArea frontend + */ +class GroupedTest extends TestCase +{ + /** + * Test subject. + * + * @var Grouped + */ + private $block; + + /** + * @var CreditMemo + */ + private $creditMemo; + + /** + * @inheritDoc + */ + protected function setUp() + { + $this->block = Bootstrap::getObjectManager()->get(Grouped::class); + $this->creditMemo = Bootstrap::getObjectManager()->get(CreditMemo::class); + } + + /** + * Verify, grouped block will output correct product sku and name. + * + * @magentoDataFixture Magento/Sales/_files/creditmemo_with_grouped_product.php + */ + public function testToHtml() + { + $creditMemo = $this->creditMemo->load('100000002', 'increment_id'); + $creditMemoItem = $creditMemo->getItemsCollection()->getFirstItem(); + $priceBlock = Bootstrap::getObjectManager()->create(DefaultItems::class); + $this->block->setTemplate('Magento_Sales::email/items/creditmemo/default.phtml'); + $this->block->setItem($creditMemoItem); + $this->block->getLayout()->setBlock('item_price', $priceBlock); + $output = $this->block->toHtml(); + self::assertContains('SKU: simple_11', $output); + self::assertContains('"product-name">Simple 11', $output); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php index 6dba48092c400..2ac14fbdc68cc 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/Create/SaveTest.php @@ -68,6 +68,14 @@ protected function setUp() */ public function testExecuteWithPaymentOperation() { + /** @var OrderService|MockObject $orderService */ + $orderService = $this->getMockBuilder(OrderService::class) + ->disableOriginalConstructor() + ->getMock(); + $orderService->method('place') + ->willThrowException(new LocalizedException(__('Transaction has been declined.'))); + $this->_objectManager->addSharedInstance($orderService, OrderService::class); + $quote = $this->getQuote('2000000001'); $session = $this->_objectManager->get(Quote::class); $session->setQuoteId($quote->getId()); @@ -82,14 +90,6 @@ public function testExecuteWithPaymentOperation() $this->getRequest()->setMethod(Http::METHOD_POST); $this->getRequest()->setPostValue(['order' => $data]); - /** @var OrderService|MockObject $orderService */ - $orderService = $this->getMockBuilder(OrderService::class) - ->disableOriginalConstructor() - ->getMock(); - $orderService->method('place') - ->willThrowException(new LocalizedException(__('Transaction has been declined.'))); - $this->_objectManager->addSharedInstance($orderService, OrderService::class); - $this->dispatch('backend/sales/order_create/save'); $this->assertSessionMessages( self::equalTo(['Transaction has been declined.']), diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php index 724865176188a..83bc7e10647b4 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/Email/Sender/ShipmentSenderTest.php @@ -10,6 +10,9 @@ /** * @magentoAppArea frontend + * + * @deprecated since ShipmentSender is deprecated + * @see \Magento\Sales\Model\Order\Email\Sender\ShipmentSender */ class ShipmentSenderTest extends \PHPUnit\Framework\TestCase { diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php new file mode 100644 index 0000000000000..5f5f534ae2cee --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product.php @@ -0,0 +1,36 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Creditmemo\Item; +use Magento\Sales\Model\Order\CreditmemoFactory; +use Magento\TestFramework\Helper\Bootstrap; + +require __DIR__ . '/order_with_grouped_product.php'; + +$objectManager = Bootstrap::getObjectManager(); + +$creditmemoFactory = $objectManager->get(CreditmemoFactory::class); +$creditmemo = $creditmemoFactory->createByOrder($order, $order->getData()); +$creditmemo->setOrder($order); +$creditmemo->setState(Creditmemo::STATE_OPEN); +$creditmemo->setIncrementId('100000002'); +$creditmemo->save(); + +$orderItem = current($order->getAllItems()); +$orderItem->setName('Test item') + ->setQtyRefunded(1) + ->setQtyInvoiced(10) + ->setOriginalPrice(20); + +$creditItem = $objectManager->get(Item::class); +$creditItem->setCreditmemo($creditmemo) + ->setName('Creditmemo item') + ->setOrderItemId($orderItem->getId()) + ->setQty(1) + ->setPrice(20) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php new file mode 100644 index 0000000000000..c8420f8a252ca --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/creditmemo_with_grouped_product_rollback.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection; +use Magento\TestFramework\Helper\Bootstrap; + +require 'default_rollback.php'; + +/** @var $creditmemo Creditmemo */ +$creditmemoCollection = Bootstrap::getObjectManager()->create(Collection::class); +foreach ($creditmemoCollection as $creditmemo) { + $creditmemo->delete(); +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php new file mode 100644 index 0000000000000..0cdc2d10e6f06 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Sales\Model\Order\Item as OrderItem; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +require 'default_rollback.php'; +require __DIR__ . '/../../../Magento/GroupedProduct/_files/product_grouped_with_simple.php'; +$addressData = include __DIR__ . '/address_data.php'; + +$objectManager = Bootstrap::getObjectManager(); +$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); +$billingAddress->setAddressType('billing'); + +$shippingAddress = clone $billingAddress; +$shippingAddress->setId(null)->setAddressType('shipping'); + +$payment = $objectManager->create(Payment::class); +$payment->setMethod('checkmo') + ->setAdditionalInformation('last_trans_id', '11122') + ->setAdditionalInformation( + 'metadata', + [ + 'type' => 'free', + 'fraudulent' => false, + ] + ); +$product = $objectManager->get(ProductRepositoryInterface::class)->get('simple_11'); +$orderItem = $objectManager->create(OrderItem::class); +$orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('grouped') + ->setName($product->getName()) + ->setSku($product->getSku()); + +$order = $objectManager->create(Order::class); +$order->setIncrementId('100000002') + ->setState(Order::STATE_PROCESSING) + ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)) + ->setSubtotal(100) + ->setGrandTotal(100) + ->setBaseSubtotal(100) + ->setBaseGrandTotal(100) + ->setCustomerIsGuest(true) + ->setCustomerEmail('customer@null.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId()) + ->addItem($orderItem) + ->setPayment($payment); +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); +$orderRepository->save($order); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php new file mode 100644 index 0000000000000..1fb4b4636ab29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_grouped_product_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +require 'default_rollback.php'; diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js index b5c6e75248bfa..77ce2fb25f688 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/columns/image-preview.test.js @@ -13,10 +13,26 @@ define([ describe('Ui/js/grid/columns/image-preview', function () { var record = { - _rowIndex: 1, - rowNumber: 1 - }, - imagePreview; + _rowIndex: 1, + rowNumber: 1 + }, + imagePreview, + recordMock = { + _rowIndex: 2 + }, + secondRecordMock = { + _rowIndex: 1, + rowNumber: 1 + }, + elementMock = { + keyCode: 37 + }, + masonryMock = { + shows: jasmine.createSpy().and.returnValue([]), + rows: jasmine.createSpy().and.returnValue({ + 1: secondRecordMock + }) + }; beforeEach(function () { imagePreview = new Preview(); @@ -46,5 +62,36 @@ define([ }); }); + + describe('handleKeyDown method', function () { + + it('verify record changed on key down', function () { + var imageMock = document.createElement('img'), + originMock = $.fn.get; + + spyOn($.fn, 'get').and.returnValue(imageMock); + imagePreview.visibleRecord = jasmine.createSpy().and.returnValue(2); + imagePreview.displayedRecord = ko.observable(); + imagePreview.displayedRecord(recordMock); + imagePreview.masonry = jasmine.createSpy().and.returnValue(masonryMock); + imagePreview.handleKeyDown(elementMock); + expect(imagePreview.displayedRecord()._rowIndex).toBe(secondRecordMock._rowIndex); + + $.fn.get = originMock; + }); + + it('verify record not changed on key down when active element input', function () { + var input = $('<input id=\'input-fixture\'/>'); + + $(document.body).append(input); + input.focus(); + imagePreview.visibleRecord = jasmine.createSpy().and.returnValue(1); + imagePreview.displayedRecord = ko.observable(1); + imagePreview.handleKeyDown(elementMock); + expect(imagePreview.displayedRecord()).toBe(1); + + $('#input-fixture').remove(); + }); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js index 5f8bfc2c98cc2..b53f49bd6103d 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/grid/data-storage.test.js @@ -6,63 +6,190 @@ /*eslint max-nested-callbacks: 0*/ /*jscs:disable requireCamelCaseOrUpperCaseIdentifiers*/ define([ - 'mageUtils', + 'jquery', 'Magento_Ui/js/grid/data-storage' -], function (utils, DataStorage) { +], function ($, DataStorage) { 'use strict'; describe('Magento_Ui/js/grid/data-storage', function () { - describe('costructor', function () { - it('converts dataScope property to array', function () { + + describe('"initConfig" method', function () { + + it('returns self', function () { var model = new DataStorage({ dataScope: 'magento' }); - expect(model.dataScope).toEqual(['magento']); + expect(model.initConfig()).toEqual(model); }); - }); - describe('hasScopeChanged', function () { - it('is function', function () { + it('changes string dataScope property to an array', function () { var model = new DataStorage({ - dataScope: '' + dataScope: 'magento' }); - expect(model.hasScopeChanged).toBeDefined(); - expect(typeof model.hasScopeChanged).toEqual('function'); + expect(model.dataScope).toEqual(['magento']); }); - it('returns false if no requests have been made', function () { + it('changes empty string dataScope property to an empty array', function () { var model = new DataStorage({ dataScope: '' }); - expect(model.hasScopeChanged()).toBeFalsy(); + expect(model.dataScope).toEqual([]); }); - it('tells whether parameters defined in the dataScope property have changed', function () { - var params, newParams, model; + it('doesn\'t change non-string dataScope property', function () { + var testScope = { + testKey: 'test value' + }, + model = new DataStorage({ + dataScope: testScope + }); - params = { - namespace: 'magento', - search: '', - filters: { - store_id: 0 + expect(model.dataScope).toEqual(testScope); + }); + + it('initializes _requests property as an empty array', function () { + var model = new DataStorage(); + + model._requests = null; + model.initConfig(); + expect(model._requests).toEqual([]); + }); + }); + + describe('"getByIds" method', function () { + + it('returns false if data for ids is missing', function () { + var model = new DataStorage(); + + expect(model.getByIds([1,2,3])).toEqual(false); + }); + + it('returns array of items', function () { + var item = { + id_field_name: 'entity_id', + entity_id: '1' }, - sorting: {}, - paging: {} - }; + model = new DataStorage({ + data: { + 1: item + } + }); - newParams = utils.extend({}, params, { - search: 'magento', - filters: { - store_id: 1 - } - }); + expect(model.getByIds([1])).toEqual([item]); + }); - model = new DataStorage({ - dataScope: 'filters.store_id' - }); + }); + + describe('"getIds" method', function () { + + it('returns an array of entity_id\'s from provided data', function () { + var model = new DataStorage(), + ids = [ + { + id_field_name: 'entity_id', + entity_id: '1' + }, + { + id_field_name: 'entity_id', + entity_id: '54' + } + ]; + + expect(model.getIds(ids)).toEqual(['1', '54']); + }); + + it('returns an array of entity_id\'s from stored data if no arguments provided', function () { + var model = new DataStorage({ + data: { + 1: { + id_field_name: 'entity_id', + entity_id: '1' + }, + 2: { + id_field_name: 'entity_id', + entity_id: '42' + } + } + }); + + expect(model.getIds()).toEqual(['1', '42']); + }); + + }); + + describe('"getData" method', function () { + + var model = new DataStorage(); + + it('returns the result of requestData method if scope have been changed', function () { + var requestDataResult = 'requestDataResult'; + + spyOn(model, 'clearRequests'); + spyOn(model, 'hasScopeChanged').and.returnValue(true); + spyOn(model, 'requestData').and.returnValue(requestDataResult); + spyOn(model, 'getRequest'); + expect(model.getData()).toEqual(requestDataResult); + expect(model.clearRequests).toHaveBeenCalled(); + expect(model.getRequest).not.toHaveBeenCalled(); + }); + + it('returns the cached result if scope have not been changed', function () { + var cachedRequestDataResult = 'cachedRequestDataResult'; + + spyOn(model, 'clearRequests'); + spyOn(model, 'requestData'); + spyOn(model, 'hasScopeChanged').and.returnValue(false); + spyOn(model, 'getRequest').and.returnValue(true); + spyOn(model, 'getRequestData').and.returnValue(cachedRequestDataResult); + + expect(model.getData()).toEqual(cachedRequestDataResult); + expect(model.clearRequests).not.toHaveBeenCalled(); + expect(model.requestData).not.toHaveBeenCalled(); + }); + + it('returns the result of requestData method if refresh option is provided', function () { + var requestDataResult = 'requestDataResult', + options = { + refresh: true + }; + + spyOn(model, 'getRequest').and.returnValue(true); + spyOn(model, 'clearRequests'); + spyOn(model, 'hasScopeChanged').and.returnValue(true); + spyOn(model, 'requestData').and.returnValue(requestDataResult); + expect(model.getData({}, options)).toEqual(requestDataResult); + expect(model.clearRequests).toHaveBeenCalled(); + }); + + }); + + describe('"hasScopeChanged" method', function () { + + it('returns false if no requests have been made', function () { + var model = new DataStorage(); + + expect(model.hasScopeChanged()).toBeFalsy(); + }); + + it('returns true for not cached params', function () { + var params = { + search: '1', + filters: { + store_id: 0 + } + }, + newParams = { + search: '2', + filters: { + store_id: 1 + } + }, + model = new DataStorage({ + dataScope: 'filters.store_id' + }); model.cacheRequest({ totalRecords: 0 @@ -72,5 +199,303 @@ define([ expect(model.hasScopeChanged(newParams)).toBeTruthy(); }); }); + + describe('"updateData" method', function () { + var model = new DataStorage({ + dataScope: 'magento', + requestConfig: { + url: 'magento.com', + method: 'GET', + dataType: 'json' + }, + data: { + 1: { + id_field_name: 'entity_id', + entity_id: '1', + field: 'value' + } + } + }); + + it('updates data items', function () { + var data = [{ + id_field_name: 'entity_id', + entity_id: '1', + field: 'updatedValue' + }]; + + expect(model.updateData(data)).toEqual(model); + expect(model.getByIds([1])).toEqual(data); + }); + }); + + describe('"requestData" method', function () { + var model = new DataStorage(); + + it('Check Ajax request', function () { + var result = 'result'; + + spyOn(model, 'onRequestComplete').and.returnValue(result); + spyOn($, 'ajax').and.returnValue({ + /** + * Success result for ajax request + * + * @param {Function} handler + * @returns {*} + */ + done: function (handler) { + return handler(); + } + }); + expect(model.requestData({})).toEqual(result); + }); + }); + + describe('"getRequest" method', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + it('returns cached request', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + request = { + ids: ['1'], + params: params, + totalRecords: 1, + errorMessage: '' + }; + + model._requests.push(request); + expect(model.getRequest(params)).toEqual(request); + }); + }); + + describe('"getRequestData" method', function () { + it('returns request data', function () { + var request = { + ids: [1,2], + totalRecords: 2, + errorMessage: '' + }, + items = [ + { + id_field_name: 'entity_id', + entity_id: '1' + }, + { + id_field_name: 'entity_id', + entity_id: '2' + } + ], + result = { + items: items, + totalRecords: 2, + errorMessage: '' + }, + model = new DataStorage({ + cachedRequestDelay: 0 + }); + + spyOn(model, 'getByIds').and.returnValue(items); + model.getRequestData(request).then(function (promiseResult) { + expect(promiseResult).toEqual(result); + }); + }); + }); + + describe('"cacheRequest" method', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + it('adds the request to the cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + ids = ['1','2','3'], + data = { + items: ids, + totalRecords: 3, + errorMessage: '' + }, + request = { + ids: ids, + params: params, + totalRecords: 3, + errorMessage: '' + }; + + spyOn(model, 'removeRequest'); + spyOn(model, 'getIds').and.returnValue(ids); + model.cacheRequest(data, params); + expect(model.getRequest(params)).toEqual(request); + expect(model.removeRequest).not.toHaveBeenCalled(); + }); + + it('overwrites the previously cached request for the same params', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + ids = ['1','2','3'], + firstData = { + items: ids, + totalRecords: 3, + errorMessage: '' + }, + secondData = { + items: ids, + totalRecords: 3, + errorMessage: 'Error message' + }, + firstRequest = { + ids: ids, + params: params, + totalRecords: 3, + errorMessage: '' + }, + secondRequest = { + ids: ids, + params: params, + totalRecords: 3, + errorMessage: 'Error message' + }; + + spyOn(model, 'getIds').and.returnValue(ids); + model.cacheRequest(firstData, params); + expect(model.getRequest(params)).toEqual(firstRequest); + model.cacheRequest(secondData, params); + expect(model.getRequest(params)).toEqual(secondRequest); + }); + }); + + describe('"clearRequests" method', function () { + + it('removes all cached requests', function () { + var model = new DataStorage(), + params = { + namespace: 'magento', + search: 'magento', + filters: { + store_id: 1 + } + }; + + model._requests.push({ + ids: ['1','2','3','4'], + params: params, + totalRecords: 4, + errorMessage: 'errorMessage' + }); + model.clearRequests(); + expect(model._requests).toEqual([]); + }); + }); + + describe('"removeRequest" method', function () { + + var model = new DataStorage(); + + it('removes the request from the cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + request = { + ids: ['1','2','3'], + params: params, + totalRecords: 3, + errorMessage: '' + }; + + model._requests = [request]; + expect(model.getRequest(params)).toEqual(request); + model.removeRequest(request); + expect(model.getRequest(params)).toBeFalsy(); + }); + }); + + describe('"wasRequested" method', function () { + var model = new DataStorage({ + dataScope: 'magento' + }); + + it('returns false if request is not present in cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }; + + expect(model.wasRequested(params)).toBeFalsy(); + }); + + it('returns true if request is present in cache', function () { + var params = { + namespace: 'magento', + search: '', + sorting: {}, + paging: {} + }, + request = { + ids: ['1','2','3'], + params: params, + totalRecords: 3, + errorMessage: '' + }; + + model._requests = [request]; + + expect(model.wasRequested(params)).toBeTruthy(); + }); + }); + + describe('"onRequestComplete" method', function () { + + it('updates data and does not cache the request if caching is disabled', function () { + var model = new DataStorage({ + cacheRequests: false + }), + data = { + items: [] + }, + params = {}; + + spyOn(model, 'updateData'); + spyOn(model, 'cacheRequest'); + model.onRequestComplete(params, data); + expect(model.updateData).toHaveBeenCalled(); + expect(model.cacheRequest).not.toHaveBeenCalled(); + }); + + it('updates data and adds the request to cache if caching is enabled', function () { + var model = new DataStorage({ + cacheRequests: true + }), + data = { + items: [] + }, + params = {}; + + spyOn(model, 'updateData'); + spyOn(model, 'cacheRequest'); + model.onRequestComplete(params, data); + expect(model.updateData).toHaveBeenCalled(); + expect(model.cacheRequest).toHaveBeenCalled(); + }); + }); }); }); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js index 8b00ecd3a2aed..3625d0898e942 100644 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/modal/modal.test.js @@ -10,18 +10,45 @@ define([ 'use strict'; describe('ui/js/modal/modal', function () { - var element = $('<div>some element</div>'), + + var element, + modal; + + beforeEach(function () { + element = $('<div id="element">Element</div>'); modal = element.modal({}).data('mage-modal'); + $(element).append('<h1 class="modal-title"' + + ' data-role="title">Title</h1>' + + '<span class="modal-subtitle"' + + ' data-role="subTitle"></span>'); + }); + + afterEach(function () { + $('.modal-title').remove(); + $('#element').remove(); + + }); + it('Check for modal definition', function () { expect(modal).toBeDefined(); }); + it('Show/hide function check', function () { expect(element.trigger('openModal')).toBe(element); expect(element.trigger('closeModal')).toBe(element); }); + it('Integration: modal created on page', function () { expect($(modal).length).toEqual(1); }); + + it('Verify set title', function () { + var newTitle = 'New modal title'; + + modal.setTitle(newTitle); + expect($(modal.options.modalTitle).text()).toContain(newTitle); + expect($(modal.options.modalTitle).find(modal.options.modalSubTitle).length).toBe(1); + }); }); }); diff --git a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php index 8ccda77a25191..b7717492fd124 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Php/LiveCodeTest.php @@ -142,7 +142,7 @@ private static function getAddedFilesList($changedFilesBaseDir) function () { // if no list files, probably, this is the dev environment // phpcs:ignore Generic.PHP.NoSilencedErrors,Magento2.Security.InsecureFunction - @exec('git diff --cached --name-only', $addedFiles); + @exec('git diff --cached --name-only --diff-filter=A', $addedFiles); return $addedFiles; } );