diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsTest.xml similarity index 100% rename from app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml rename to app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsTest.xml diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml similarity index 100% rename from app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProducts.xml rename to app/code/Magento/Bundle/Test/Mftf/Test/AdminMassDeleteBundleProductsTest.xml diff --git a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php index 76f5dbd1bea88..523efe08c6a4e 100644 --- a/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php +++ b/app/code/Magento/Catalog/Block/Product/Compare/ListCompare.php @@ -149,7 +149,7 @@ public function getItems() $this->_compareProduct->setAllowUsedFlat(false); $this->_items = $this->_itemCollectionFactory->create(); - $this->_items->useProductItem(true)->setStoreId($this->_storeManager->getStore()->getId()); + $this->_items->useProductItem()->setStoreId($this->_storeManager->getStore()->getId()); if ($this->httpContext->getValue(Context::CONTEXT_AUTH)) { $this->_items->setCustomerId($this->currentCustomer->getCustomerId()); diff --git a/app/code/Magento/Catalog/Helper/Product/Compare.php b/app/code/Magento/Catalog/Helper/Product/Compare.php index 49a90c590a440..4e476fe8d1568 100644 --- a/app/code/Magento/Catalog/Helper/Product/Compare.php +++ b/app/code/Magento/Catalog/Helper/Product/Compare.php @@ -279,7 +279,7 @@ public function getItemCollection() // cannot be placed in constructor because of the cyclic dependency which cannot be fixed with proxy class // collection uses this helper in constructor when calling isEnabledFlat() method $this->_itemCollection = $this->_itemCollectionFactory->create(); - $this->_itemCollection->useProductItem(true)->setStoreId($this->_storeManager->getStore()->getId()); + $this->_itemCollection->useProductItem()->setStoreId($this->_storeManager->getStore()->getId()); if ($this->_customerSession->isLoggedIn()) { $this->_itemCollection->setCustomerId($this->_customerSession->getCustomerId()); @@ -313,7 +313,7 @@ public function calculate($logout = false) { /** @var $collection Collection */ $collection = $this->_itemCollectionFactory->create() - ->useProductItem(true); + ->useProductItem(); if (!$logout && $this->_customerSession->isLoggedIn()) { $collection->setCustomerId($this->_customerSession->getCustomerId()); } elseif ($this->_customerId) { diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCategoryExistInCategoryListActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCategoryExistInCategoryListActionGroup.xml new file mode 100644 index 0000000000000..c9ad309dcadc1 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCategoryExistInCategoryListActionGroup.xml @@ -0,0 +1,26 @@ + + + + + + + Check Category exist in Category list for Assign to Product. + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCategoryNotExistInCategoryListActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCategoryNotExistInCategoryListActionGroup.xml new file mode 100644 index 0000000000000..fb0717fe173af --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductFormCategoryNotExistInCategoryListActionGroup.xml @@ -0,0 +1,26 @@ + + + + + + + Check Category not exist in Category list for Assign to Product. + + + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSubmitCategoriesPopupActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSubmitCategoriesPopupActionGroup.xml new file mode 100644 index 0000000000000..8905643658cd8 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminSubmitCategoriesPopupActionGroup.xml @@ -0,0 +1,18 @@ + + + + + + + Clicks the "Done" button on the Search Categories popup. + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategoryCurrentPageIsNthActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategoryCurrentPageIsNthActionGroup.xml new file mode 100644 index 0000000000000..84e14269d24c2 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AssertStorefrontCategoryCurrentPageIsNthActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + {{expectedPage}} + currentPageText + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryNextPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryNextPageActionGroup.xml new file mode 100644 index 0000000000000..4776c9d32a34d --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontNavigateCategoryNextPageActionGroup.xml @@ -0,0 +1,17 @@ + + + + + + + Navigates storefront category next page from toolbar + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml index 09eb4ad954274..c27a6107e5e35 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml @@ -12,6 +12,6 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml b/app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHintTest.xml similarity index 100% rename from app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHint.xml rename to app/code/Magento/Catalog/Test/Mftf/Test/ConfigurableOptionTextInputLengthValidationHintTest.xml diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 28624c667e42b..360df8f4edc66 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -572,11 +572,11 @@ public function prepareProductIndex($indexData, $productData, $storeId) foreach ($indexData as $entityId => $attributeData) { foreach ($attributeData as $attributeId => $attributeValues) { $value = $this->getAttributeValue($attributeId, $attributeValues, $storeId); - if (!empty($value)) { + if ($value !== null && $value !== false && $value != '') { if (!isset($index[$attributeId])) { $index[$attributeId] = []; } - $index[$attributeId][$entityId] = $value; + $index[$attributeId][$entityId] = $value; } } } diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontCheckUnableAdvancedSearchWithNegativePriceTest.xml b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontCheckUnableAdvancedSearchWithNegativePriceTest.xml index 67e9fdd43f5fe..cceac0475aa78 100644 --- a/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontCheckUnableAdvancedSearchWithNegativePriceTest.xml +++ b/app/code/Magento/CatalogSearch/Test/Mftf/Test/StorefrontCheckUnableAdvancedSearchWithNegativePriceTest.xml @@ -13,6 +13,7 @@ <description value="Check unable negative price use to advanced search by price from and price to"/> + <severity value="MAJOR"/> </annotations> <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="goToStorefront"/> <actionGroup ref="StorefrontOpenAdvancedSearchActionGroup" stepKey="openAdvancedSearch"/> diff --git a/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml b/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml index c33b784fcd20c..192f20653f8c3 100644 --- a/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml +++ b/app/code/Magento/Checkout/view/frontend/layout/checkout_index_index.xml @@ -105,7 +105,7 @@ <item name="trigger" xsi:type="string">opc-new-shipping-address</item> <item name="buttons" xsi:type="array"> <item name="save" xsi:type="array"> - <item name="text" xsi:type="string" translate="true">Ship here</item> + <item name="text" xsi:type="string" translate="true">Ship Here</item> <item name="class" xsi:type="string">action primary action-save-address</item> </item> <item name="cancel" xsi:type="array"> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminDeleteCmsPageTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminDeleteCmsPageTest.xml index c46410dce919e..3687bb4fe5743 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminDeleteCmsPageTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminDeleteCmsPageTest.xml @@ -14,6 +14,7 @@ <stories value="Delete a CMS Page via the Admin"/> <title value="Admin should be able to delete CMS Pages"/> <description value="Admin should be able to delete CMS Pages"/> + <severity value="CRITICAL"/> <group value="Cms"/> <group value="WYSIWYGDisabled"/> </annotations> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml b/app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimpleData.xml similarity index 100% rename from app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimple.xml rename to app/code/Magento/Customer/Test/Mftf/Data/ExtensionAttributeSimpleData.xml diff --git a/app/code/Magento/Customer/view/frontend/email/change_email.html b/app/code/Magento/Customer/view/frontend/email/change_email.html index bd961ad99ec40..5341a2dc67ad5 100644 --- a/app/code/Magento/Customer/view/frontend/email/change_email.html +++ b/app/code/Magento/Customer/view/frontend/email/change_email.html @@ -18,8 +18,5 @@ {{trans "We have received a request to change the following information associated with your account at %store_name: email." store_name=$store.frontend_name}} {{trans 'If you have not authorized this action, please contact us immediately at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. </p> -<br> - -<p>{{trans "Thanks,<br>%store_name" store_name=$store.frontend_name |raw}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html b/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html index 4f5c85b2381f3..ed2af7ada669e 100644 --- a/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html +++ b/app/code/Magento/Customer/view/frontend/email/change_email_and_password.html @@ -18,8 +18,5 @@ {{trans "We have received a request to change the following information associated with your account at %store_name: email, password." store_name=$store.frontend_name}} {{trans 'If you have not authorized this action, please contact us immediately at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. </p> -<br> - -<p>{{trans "Thanks,<br>%store_name" store_name=$store.frontend_name |raw}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Customer/view/frontend/email/password_reset.html b/app/code/Magento/Customer/view/frontend/email/password_reset.html index cab05a89227b6..a6c54842a1573 100644 --- a/app/code/Magento/Customer/view/frontend/email/password_reset.html +++ b/app/code/Magento/Customer/view/frontend/email/password_reset.html @@ -19,8 +19,5 @@ {{trans "We have received a request to change the following information associated with your account at %store_name: password." store_name=$store.frontend_name}} {{trans 'If you have not authorized this action, please contact us immediately at <a href="mailto:%store_email">%store_email</a>' store_email=$store_email |raw}}{{depend store_phone}} {{trans 'or call us at <a href="tel:%store_phone">%store_phone</a>' store_phone=$store_phone |raw}}{{/depend}}. </p> -<br> - -<p>{{trans "Thanks,<br>%store_name" store_name=$store.frontend_name |raw}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductSwitchToSimpleTest.xml similarity index 100% rename from app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateAndSwitchProductType.xml rename to app/code/Magento/Downloadable/Test/Mftf/Test/AdminCreateDownloadableProductSwitchToSimpleTest.xml diff --git a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php index 7f0ecf899e51c..245e4d494afe1 100644 --- a/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php +++ b/app/code/Magento/Elasticsearch/Model/Adapter/BatchDataMapper/ProductDataMapper.php @@ -209,7 +209,7 @@ private function convertAttribute(Attribute $attribute, array $attributeValues, $productAttributes = []; $retrievedValue = $this->retrieveFieldValue($attributeValues); - if ($retrievedValue) { + if ($retrievedValue !== null) { $productAttributes[$attribute->getAttributeCode()] = $retrievedValue; if ($attribute->getIsSearchable()) { @@ -354,7 +354,7 @@ private function getAttributeOptions(Attribute $attribute, int $storeId): array */ private function retrieveFieldValue(array $values) { - $values = \array_filter(\array_unique($values)); + $values = \array_unique($values); return count($values) === 1 ? \array_shift($values) : \array_values($values); } diff --git a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php index 76a2f00f44fe2..ce79f433460d9 100644 --- a/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php +++ b/app/code/Magento/Elasticsearch/SearchAdapter/Filter/Builder/Term.php @@ -68,7 +68,7 @@ public function buildFilter(RequestFilterInterface $filter) $fieldName .= '.' . $suffix; } - if ($filter->getValue()) { + if ($filter->getValue() !== false) { $operator = is_array($filter->getValue()) ? 'terms' : 'term'; $filterQuery []= [ $operator => [ diff --git a/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php new file mode 100644 index 0000000000000..c317221fb6ef7 --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Cart/GiftMessage.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessageGraphQl\Model\Resolver\Cart; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GiftMessage\Api\CartRepositoryInterface; +use Magento\GiftMessage\Helper\Message as GiftMessageHelper; + +/** + * Class provides ability to get GiftMessage for cart + */ +class GiftMessage implements ResolverInterface +{ + /** + * @var CartRepositoryInterface + */ + private $cartRepository; + + /** + * @var GiftMessageHelper + */ + private $giftMessageHelper; + + /** + * @param CartRepositoryInterface $cartRepository + * @param GiftMessageHelper $giftMessageHelper + */ + public function __construct( + CartRepositoryInterface $cartRepository, + GiftMessageHelper $giftMessageHelper + ) { + $this->cartRepository = $cartRepository; + $this->giftMessageHelper = $giftMessageHelper; + } + + /** + * Return information about Gift message of cart + * + * @param Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * + * @return array|Value|mixed + * + * @throws GraphQlInputException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['model'])) { + throw new GraphQlInputException(__('"model" value should be specified')); + } + + $cart = $value['model']; + + if (!$this->giftMessageHelper->isMessagesAllowed('order', $cart)) { + return null; + } + + try { + $giftCartMessage = $this->cartRepository->get($cart->getId()); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__('Can\'t load cart.')); + } + + if (!isset($giftCartMessage)) { + return null; + } + + return [ + 'to' => $giftCartMessage->getRecipient() ?? '', + 'from' => $giftCartMessage->getSender() ?? '', + 'message'=> $giftCartMessage->getMessage() ?? '' + ]; + } +} diff --git a/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Order/GiftMessage.php b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Order/GiftMessage.php new file mode 100644 index 0000000000000..aae0e3709d87f --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/Model/Resolver/Order/GiftMessage.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GiftMessageGraphQl\Model\Resolver\Order; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\Resolver\Value; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\GiftMessage\Api\OrderRepositoryInterface; + +/** + * Class for getting GiftMessage from CustomerOrder + */ +class GiftMessage implements ResolverInterface +{ + /** + * @var OrderRepositoryInterface + */ + private $orderRepository; + + /** + * @param OrderRepositoryInterface $orderRepository + */ + public function __construct( + OrderRepositoryInterface $orderRepository + ) { + $this->orderRepository = $orderRepository; + } + + /** + * Return information about gift message for order + * + * @param Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * + * @return array|Value|mixed + * @throws GraphQlInputException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!isset($value['id'])) { + throw new GraphQlInputException(__('"id" value should be specified')); + } + + try { + $orderGiftMessage = $this->orderRepository->get($value['id']); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__('Can\'t load gift message for order')); + } + + if (!isset($orderGiftMessage)) { + return null; + } + + return [ + 'to' => $orderGiftMessage->getRecipient() ?? '', + 'from' => $orderGiftMessage->getSender() ?? '', + 'message'=> $orderGiftMessage->getMessage() ?? '' + ]; + } +} diff --git a/app/code/Magento/GiftMessageGraphQl/README.md b/app/code/Magento/GiftMessageGraphQl/README.md new file mode 100644 index 0000000000000..fa2e02116b66c --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/README.md @@ -0,0 +1,3 @@ +# GiftMessageGraphQl + +**GiftMessageGraphQl** provides information about gift messages for cart, cart items, order and order items. diff --git a/app/code/Magento/GiftMessageGraphQl/composer.json b/app/code/Magento/GiftMessageGraphQl/composer.json new file mode 100644 index 0000000000000..48088f2a48a32 --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/composer.json @@ -0,0 +1,25 @@ +{ + "name": "magento/module-gift-message-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.3.0||~7.4.0", + "magento/framework": "*", + "magento/module-gift-message": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\GiftMessageGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/GiftMessageGraphQl/etc/module.xml b/app/code/Magento/GiftMessageGraphQl/etc/module.xml new file mode 100644 index 0000000000000..5eaaae0b0b988 --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/etc/module.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_GiftMessageGraphQl"> + <sequence> + <module name="Magento_GiftMessage"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls b/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..f14c812a9a5f3 --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/etc/schema.graphqls @@ -0,0 +1,20 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Cart { + gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Cart\\GiftMessage") @doc(description: "The entered gift message for the cart") +} + +type SalesItemInterface { + gift_message: GiftMessage @doc(description: "The entered gift message for the order item") +} + +type CustomerOrder { + gift_message: GiftMessage @resolver (class: "\\Magento\\GiftMessageGraphQl\\Model\\Resolver\\Order\\GiftMessage") @doc(description: "The entered gift message for the order") +} + +type GiftMessage { + to: String! @doc(description: "Recepient name") + from: String! @doc(description: "Sender name") + message: String! @doc(description: "Gift message text") +} diff --git a/app/code/Magento/GiftMessageGraphQl/registration.php b/app/code/Magento/GiftMessageGraphQl/registration.php new file mode 100644 index 0000000000000..bb260c23b0177 --- /dev/null +++ b/app/code/Magento/GiftMessageGraphQl/registration.php @@ -0,0 +1,13 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register( + ComponentRegistrar::MODULE, + 'Magento_GiftMessageGraphQl', + __DIR__ +); diff --git a/app/code/Magento/GraphQl/etc/graphql/di.xml b/app/code/Magento/GraphQl/etc/graphql/di.xml index 2bcd44e9ae410..77fce336374dd 100644 --- a/app/code/Magento/GraphQl/etc/graphql/di.xml +++ b/app/code/Magento/GraphQl/etc/graphql/di.xml @@ -15,10 +15,6 @@ <item name="type" xsi:type="object">Magento\Webapi\Model\Authorization\TokenUserContext</item> <item name="sortOrder" xsi:type="string">10</item> </item> - <item name="oauthUserContext" xsi:type="array"> - <item name="type" xsi:type="object">Magento\Webapi\Model\Authorization\OauthUserContext</item> - <item name="sortOrder" xsi:type="string">40</item> - </item> <item name="guestUserContext" xsi:type="array"> <item name="type" xsi:type="object">Magento\Webapi\Model\Authorization\GuestUserContext</item> <item name="sortOrder" xsi:type="string">100</item> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest.xml index aaa9cf5b2f925..e966ccf82648f 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Test/StorefrontAdvanceCatalogSearchGroupedProductBySkuWithHyphenTest.xml @@ -21,7 +21,6 @@ <skip> <issueId value="MC-34217"/> </skip> - </annotations> <before> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.xml index 02187658a8781..815d406c68bfa 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckingWithCartPriceRuleMatchingSubtotalForMultiShipmentTest.xml @@ -19,12 +19,6 @@ <group value="Multishipment"/> <group value="SalesRule"/> </annotations> - <before> - <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> - </before> - <after> - <magentoCLI command="config:set multishipping/options/checkout_multiple 0" stepKey="disableShippingToMultipleAddresses"/> - </after> <actionGroup ref="AdminCreateCartPriceRuleActionsWithSubtotalActionGroup" before="goToProduct1" stepKey="createSubtotalCartPriceRuleActionsSection"> <argument name="ruleName" value="CartPriceRuleConditionForSubtotalForMultiShipping"/> </actionGroup> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml index a49a37e475409..8205ab962b9fe 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontCheckoutWithMultipleAddressesTest.xml @@ -22,8 +22,6 @@ <before> <!-- Login as Admin --> <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> - <!-- Set configurations --> - <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> <!-- Create simple products --> <createData entity="SimpleSubCategory" stepKey="createCategory"/> <createData entity="SimpleProduct" stepKey="firstProduct"> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml index 80407a219a841..2e5c0acc32053 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontOrderWithMultishippingTest.xml @@ -27,7 +27,6 @@ <createData entity="SimpleProduct2" stepKey="createProduct2"/> <createData entity="Simple_US_Customer_Two_Addresses" stepKey="createCustomer"/> <!-- Set configurations --> - <magentoCLI command="config:set {{EnableMultiShippingCheckoutMultiple.path}} {{EnableMultiShippingCheckoutMultiple.value}}" stepKey="allowShippingToMultipleAddresses"/> <magentoCLI command="config:set {{EnableFreeShippingMethod.path}} {{EnableFreeShippingMethod.value}}" stepKey="enableFreeShipping"/> <magentoCLI command="config:set {{EnableFlatRateShippingMethod.path}} {{EnableFlatRateShippingMethod.value}}" stepKey="enableFlatRateShipping"/> <magentoCLI command="config:set {{EnableCheckMoneyOrderPaymentMethod.path}} {{EnableCheckMoneyOrderPaymentMethod.value}}" stepKey="enableCheckMoneyOrderPaymentMethod"/> @@ -43,7 +42,6 @@ <!-- Need logout before customer delete. Fatal error appears otherwise --> <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> - <magentoCLI command="config:set {{DisableMultiShippingCheckoutMultiple.path}} {{DisableMultiShippingCheckoutMultiple.value}}" stepKey="withdrawShippingToMultipleAddresses"/> <magentoCLI command="config:set {{DisableFreeShippingMethod.path}} {{DisableFreeShippingMethod.value}}" stepKey="disableFreeShipping"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearAllOrdersGridFilters"/> <actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/> diff --git a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml index caf0ce3a51bae..7bb26525b173f 100644 --- a/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml +++ b/app/code/Magento/Multishipping/Test/Mftf/Test/StorefrontProcessMultishippingCheckoutWhenCartPageIsOpenedInAnotherTabTest.xml @@ -22,8 +22,6 @@ <before> <!-- Login as Admin --> <actionGroup ref="AdminLoginActionGroup" stepKey="login"/> - <!-- Set configurations --> - <magentoCLI command="config:set multishipping/options/checkout_multiple 1" stepKey="allowShippingToMultipleAddresses"/> <!-- Create two simple products --> <createData entity="ApiCategory" stepKey="createCategory"/> <createData entity="_defaultProduct" stepKey="createFirstProduct"> diff --git a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php index 6391219e23c7e..2519dd3a6fea8 100644 --- a/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/ResourceModel/Subscriber.php @@ -116,6 +116,7 @@ public function setMessagesScope($scope) * @param string $email * @param int $websiteId * @return array + * @throws LocalizedException */ public function loadBySubscriberEmail(string $email, int $websiteId): array { diff --git a/app/code/Magento/Newsletter/i18n/en_US.csv b/app/code/Magento/Newsletter/i18n/en_US.csv index f390f6792635d..f8706967117fe 100644 --- a/app/code/Magento/Newsletter/i18n/en_US.csv +++ b/app/code/Magento/Newsletter/i18n/en_US.csv @@ -153,3 +153,6 @@ Store,Store "Newsletter Subscriptions","Newsletter Subscriptions" "We have updated your subscription.","We have updated your subscription." "Are you sure you want to delete the selected subscriber(s)?","Are you sure you want to delete the selected subscriber(s)?" +"Cannot create a newsletter subscription.","Cannot create a newsletter subscription." +"Enter a valid email address.","Enter a valid email address." +"Guests can not subscribe to the newsletter. You must create an account to subscribe.","Guests can not subscribe to the newsletter. You must create an account to subscribe." diff --git a/app/code/Magento/Newsletter/view/frontend/email/subscr_success.html b/app/code/Magento/Newsletter/view/frontend/email/subscr_success.html index d56163c10fdf3..996dff0c973e9 100644 --- a/app/code/Magento/Newsletter/view/frontend/email/subscr_success.html +++ b/app/code/Magento/Newsletter/view/frontend/email/subscr_success.html @@ -13,6 +13,6 @@ {{template config_path="design/email/header_template"}} -{{trans "You have been successfully subscribed to our newsletter."}} +<p>{{trans "You have been successfully subscribed to our newsletter."}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/Newsletter/view/frontend/email/unsub_success.html b/app/code/Magento/Newsletter/view/frontend/email/unsub_success.html index d39b5d8a8b8e9..1f222f85abac7 100644 --- a/app/code/Magento/Newsletter/view/frontend/email/unsub_success.html +++ b/app/code/Magento/Newsletter/view/frontend/email/unsub_success.html @@ -13,6 +13,6 @@ {{template config_path="design/email/header_template"}} -{{trans "You have been unsubscribed from the newsletter."}} +<p>{{trans "You have been unsubscribed from the newsletter."}}</p> {{template config_path="design/email/footer_template"}} diff --git a/app/code/Magento/NewsletterGraphQl/Model/Resolver/SubscribeEmailToNewsletter.php b/app/code/Magento/NewsletterGraphQl/Model/Resolver/SubscribeEmailToNewsletter.php new file mode 100644 index 0000000000000..a4b3bc43d0a8b --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/Model/Resolver/SubscribeEmailToNewsletter.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewsletterGraphQl\Model\Resolver; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\EnumLookup; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Newsletter\Model\SubscriptionManagerInterface; +use Magento\NewsletterGraphQl\Model\SubscribeEmailToNewsletter\Validation; +use Psr\Log\LoggerInterface; + +/** + * Resolver class for the `subscribeEmailToNewsletter` mutation. Adds an email into a newsletter subscription. + */ +class SubscribeEmailToNewsletter implements ResolverInterface +{ + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var EnumLookup + */ + private $enumLookup; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var SubscriptionManagerInterface + */ + private $subscriptionManager; + + /** + * @var Validation + */ + private $validator; + + /** + * SubscribeEmailToNewsletter constructor. + * + * @param CustomerRepositoryInterface $customerRepository + * @param EnumLookup $enumLookup + * @param LoggerInterface $logger + * @param SubscriptionManagerInterface $subscriptionManager + * @param Validation $validator + */ + public function __construct( + CustomerRepositoryInterface $customerRepository, + EnumLookup $enumLookup, + LoggerInterface $logger, + SubscriptionManagerInterface $subscriptionManager, + Validation $validator + ) { + $this->customerRepository = $customerRepository; + $this->enumLookup = $enumLookup; + $this->logger = $logger; + $this->subscriptionManager = $subscriptionManager; + $this->validator = $validator; + } + + /** + * @inheritDoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $email = trim($args['email']); + + if (empty($email)) { + throw new GraphQlInputException( + __('You must specify an email address to subscribe to a newsletter.') + ); + } + + $currentUserId = (int)$context->getUserId(); + $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); + $websiteId = (int)$context->getExtensionAttributes()->getStore()->getWebsiteId(); + + $this->validator->execute($email, $currentUserId, $websiteId); + + try { + $subscriber = $this->isCustomerSubscription($email, $currentUserId) + ? $this->subscriptionManager->subscribeCustomer($currentUserId, $storeId) + : $this->subscriptionManager->subscribe($email, $storeId); + + $status = $this->enumLookup->getEnumValueFromField( + 'SubscriptionStatusesEnum', + (string)$subscriber->getSubscriberStatus() + ); + } catch (LocalizedException $e) { + $this->logger->error($e->getMessage()); + + throw new GraphQlInputException( + __('Cannot create a newsletter subscription.') + ); + } + + return [ + 'status' => $status + ]; + } + + /** + * Returns true if a provided email equals to a current customer one + * + * @param string $email + * @param int $currentUserId + * @return bool + * @throws LocalizedException + * @throws NoSuchEntityException + */ + private function isCustomerSubscription(string $email, int $currentUserId): bool + { + if ($currentUserId > 0) { + $customer = $this->customerRepository->getById($currentUserId); + + if ($customer->getEmail() == $email) { + return true; + } + } + + return false; + } +} diff --git a/app/code/Magento/NewsletterGraphQl/Model/SubscribeEmailToNewsletter/Validation.php b/app/code/Magento/NewsletterGraphQl/Model/SubscribeEmailToNewsletter/Validation.php new file mode 100644 index 0000000000000..8b8cac0c58cf2 --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/Model/SubscribeEmailToNewsletter/Validation.php @@ -0,0 +1,195 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\NewsletterGraphQl\Model\SubscribeEmailToNewsletter; + +use Magento\Customer\Api\AccountManagementInterface as CustomerAccountManagement; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlAlreadyExistsException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Validator\EmailAddress as EmailValidator; +use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberResourceModel; +use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Model\ScopeInterface; +use Psr\Log\LoggerInterface; + +/** + * Validation class for the "subscribeEmailToNewsletter" mutation + */ +class Validation +{ + /** + * @var CustomerAccountManagement + */ + private $customerAccountManagement; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var EmailValidator + */ + private $emailValidator; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @var SubscriberResourceModel + */ + private $subscriberResource; + + /** + * Validation constructor. + * + * @param CustomerAccountManagement $customerAccountManagement + * @param CustomerRepositoryInterface $customerRepository + * @param EmailValidator $emailValidator + * @param LoggerInterface $logger + * @param ScopeConfigInterface $scopeConfig + * @param SubscriberResourceModel $subscriberResource + */ + public function __construct( + CustomerAccountManagement $customerAccountManagement, + CustomerRepositoryInterface $customerRepository, + EmailValidator $emailValidator, + LoggerInterface $logger, + ScopeConfigInterface $scopeConfig, + SubscriberResourceModel $subscriberResource + ) { + $this->customerAccountManagement = $customerAccountManagement; + $this->customerRepository = $customerRepository; + $this->emailValidator = $emailValidator; + $this->logger = $logger; + $this->scopeConfig = $scopeConfig; + $this->subscriberResource = $subscriberResource; + } + + /** + * Validate the next cases: + * - email format + * - email address isn't being used by a different account + * - if a guest user can be subscribed to a newsletter + * - verify if email is already subscribed + * + * @param string $email + * @param int $currentUserId + * @param int $websiteId + * @throws GraphQlAlreadyExistsException + * @throws GraphQlInputException + */ + public function execute(string $email = '', int $currentUserId = 0, int $websiteId = 1): void + { + $this->validateEmailFormat($email); + + if ($currentUserId > 0) { + $this->validateEmailAvailable($email, $currentUserId, $websiteId); + } else { + $this->validateGuestSubscription(); + } + + $this->validateAlreadySubscribed($email, $websiteId); + } + + /** + * Validate the format of the email address + * + * @param string $email + * @throws GraphQlInputException + */ + private function validateEmailFormat(string $email): void + { + if (!$this->emailValidator->isValid($email)) { + throw new GraphQlInputException(__('Enter a valid email address.')); + } + } + + /** + * Validate that the email address isn't being used by a different account. + * + * @param string $email + * @param int $currentUserId + * @param int $websiteId + * @throws GraphQlInputException + */ + private function validateEmailAvailable(string $email, int $currentUserId, int $websiteId): void + { + try { + $customer = $this->customerRepository->getById($currentUserId); + $customerEmail = $customer->getEmail(); + } catch (LocalizedException $e) { + $customerEmail = ''; + } + + try { + $emailAvailable = $this->customerAccountManagement->isEmailAvailable($email, $websiteId); + } catch (LocalizedException $e) { + $emailAvailable = false; + } + + if (!$emailAvailable && $customerEmail != $email) { + $this->logger->error( + __('This email address is already assigned to another user.') + ); + + throw new GraphQlInputException( + __('Cannot create a newsletter subscription.') + ); + } + } + + /** + * Validate if a guest user can be subscribed to a newsletter. + * + * @throws GraphQlInputException + */ + private function validateGuestSubscription(): void + { + if (!$this->scopeConfig->getValue( + Subscriber::XML_PATH_ALLOW_GUEST_SUBSCRIBE_FLAG, + ScopeInterface::SCOPE_STORE + )) { + throw new GraphQlInputException( + __('Guests can not subscribe to the newsletter. You must create an account to subscribe.') + ); + } + } + + /** + * Verify if email is already subscribed + * + * @param string $email + * @param int $websiteId + * @throws GraphQlAlreadyExistsException + */ + private function validateAlreadySubscribed(string $email, int $websiteId): void + { + try { + $subscriberData = $this->subscriberResource->loadBySubscriberEmail($email, $websiteId); + } catch (LocalizedException $e) { + $subscriberData = []; + } + + if (isset($subscriberData['subscriber_status']) + && (int)$subscriberData['subscriber_status'] === Subscriber::STATUS_SUBSCRIBED) { + throw new GraphQlAlreadyExistsException( + __('This email address is already subscribed.') + ); + } + } +} diff --git a/app/code/Magento/NewsletterGraphQl/README.md b/app/code/Magento/NewsletterGraphQl/README.md new file mode 100644 index 0000000000000..c65d44fbcfeba --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/README.md @@ -0,0 +1 @@ +The Magento_NewsletterGraphQl module allows a shopper to subscribe to a newsletter using GraphQL. diff --git a/app/code/Magento/NewsletterGraphQl/composer.json b/app/code/Magento/NewsletterGraphQl/composer.json new file mode 100644 index 0000000000000..92352a8a9adfe --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/composer.json @@ -0,0 +1,30 @@ +{ + "name": "magento/module-newsletter-graph-ql", + "description": "Provides GraphQl functionality for the newsletter subscriptions.", + "config": { + "sort-packages": true + }, + "type": "magento2-module", + "require": { + "php": "~7.3.0||~7.4.0", + "magento/framework": "*", + "magento/module-customer": "*", + "magento/module-newsletter": "*", + "magento/module-store": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\NewsletterGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/NewsletterGraphQl/etc/graphql/di.xml b/app/code/Magento/NewsletterGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..302a562ec4700 --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/etc/graphql/di.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\Framework\GraphQl\Schema\Type\Enum\DefaultDataMapper"> + <arguments> + <argument name="map" xsi:type="array"> + <item name="SubscriptionStatusesEnum" xsi:type="array"> + <item name="subscribed" xsi:type="string">1</item> + <item name="not_active" xsi:type="string">2</item> + <item name="unsubscribed" xsi:type="string">3</item> + <item name="unconfirmed" xsi:type="string">4</item> + </item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/NewsletterGraphQl/etc/module.xml b/app/code/Magento/NewsletterGraphQl/etc/module.xml new file mode 100644 index 0000000000000..8bda85d80c830 --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/etc/module.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_NewsletterGraphQl"> + <sequence> + <module name="Magento_Newsletter"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/NewsletterGraphQl/etc/schema.graphqls b/app/code/Magento/NewsletterGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..d96756e12caea --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/etc/schema.graphqls @@ -0,0 +1,17 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Mutation { + subscribeEmailToNewsletter(email: String!): SubscribeEmailToNewsletterOutput @doc(description:"Subscribes the specified email to a newsletter") @resolver(class: "Magento\\NewsletterGraphQl\\Model\\Resolver\\SubscribeEmailToNewsletter") +} + +type SubscribeEmailToNewsletterOutput { + status: SubscriptionStatusesEnum @doc(description: "Returns the status of the subscription request") +} + +enum SubscriptionStatusesEnum { + NOT_ACTIVE + SUBSCRIBED + UNSUBSCRIBED + UNCONFIRMED +} diff --git a/app/code/Magento/NewsletterGraphQl/registration.php b/app/code/Magento/NewsletterGraphQl/registration.php new file mode 100644 index 0000000000000..82d5512f4afb5 --- /dev/null +++ b/app/code/Magento/NewsletterGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_NewsletterGraphQl', __DIR__); diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Compared.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Compared.php index b4f2e132f6de4..e81d1f9589405 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Compared.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Compared.php @@ -46,23 +46,15 @@ public function getItemCollection() $collection = $this->getData('item_collection'); if ($collection === null) { if ($collection = $this->getCreateOrderModel()->getCustomerCompareList()) { - $collection = $collection->getItemCollection()->useProductItem( - true - )->setStoreId( - $this->getQuote()->getStoreId() - )->addStoreFilter( - $this->getQuote()->getStoreId() - )->setCustomerId( - $this->getCustomerId() - )->addAttributeToSelect( - 'name' - )->addAttributeToSelect( - 'price' - )->addAttributeToSelect( - 'image' - )->addAttributeToSelect( - 'status' - )->load(); + $collection = $collection->getItemCollection() + ->useProductItem() + ->setStoreId($this->getQuote()->getStoreId()) + ->addStoreFilter($this->getQuote()->getStoreId()) + ->setCustomerId($this->getCustomerId()) + ->addAttributeToSelect('name') + ->addAttributeToSelect('price')->addAttributeToSelect('image') + ->addAttributeToSelect('status') + ->load(); } $this->setData('item_collection', $collection); } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Pcompared.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Pcompared.php index 8442d5b36466e..c9f251621f9de 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Pcompared.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Sidebar/Pcompared.php @@ -89,13 +89,11 @@ public function getItemCollection() // get products to skip $skipProducts = []; if ($collection = $this->getCreateOrderModel()->getCustomerCompareList()) { - $collection = $collection->getItemCollection()->useProductItem( - true - )->setStoreId( - $this->getStoreId() - )->setCustomerId( - $this->getCustomerId() - )->load(); + $collection = $collection->getItemCollection() + ->useProductItem() + ->setStoreId($this->getStoreId()) + ->setCustomerId($this->getCustomerId()) + ->load(); foreach ($collection as $_item) { $skipProducts[] = $_item->getProductId(); } diff --git a/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php b/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php index a3242228b28e0..978aec1b79ec4 100644 --- a/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php +++ b/app/code/Magento/Sales/Cron/CleanExpiredQuotes.php @@ -5,12 +5,15 @@ */ namespace Magento\Sales\Cron; -use Magento\Quote\Model\ResourceModel\Quote\Collection; +use Exception; +use Magento\Quote\Model\QuoteRepository; +use Magento\Quote\Model\ResourceModel\Quote\Collection as QuoteCollection; use Magento\Sales\Model\ResourceModel\Collection\ExpiredQuotesCollection; use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; /** - * Class CleanExpiredQuotes + * Cron job for cleaning expired Quotes */ class CleanExpiredQuotes { @@ -24,16 +27,32 @@ class CleanExpiredQuotes */ private $storeManager; + /** + * @var QuoteRepository + */ + private $quoteRepository; + + /** + * @var LoggerInterface + */ + private $logger; + /** * @param StoreManagerInterface $storeManager * @param ExpiredQuotesCollection $expiredQuotesCollection + * @param QuoteRepository $quoteRepository + * @param LoggerInterface $logger */ public function __construct( StoreManagerInterface $storeManager, - ExpiredQuotesCollection $expiredQuotesCollection + ExpiredQuotesCollection $expiredQuotesCollection, + QuoteRepository $quoteRepository, + LoggerInterface $logger ) { $this->storeManager = $storeManager; $this->expiredQuotesCollection = $expiredQuotesCollection; + $this->quoteRepository = $quoteRepository; + $this->logger = $logger; } /** @@ -45,9 +64,41 @@ public function execute() { $stores = $this->storeManager->getStores(true); foreach ($stores as $store) { - /** @var $quotes Collection */ - $quotes = $this->expiredQuotesCollection->getExpiredQuotes($store); - $quotes->walk('delete'); + /** @var $quoteCollection QuoteCollection */ + $quoteCollection = $this->expiredQuotesCollection->getExpiredQuotes($store); + $quoteCollection->setPageSize(50); + + // Last page returns 1 even when we don't have any results + $lastPage = $quoteCollection->getSize() ? $quoteCollection->getLastPageNumber() : 0; + + for ($currentPage = $lastPage; $currentPage >= 1; $currentPage--) { + $quoteCollection->setCurPage($currentPage); + + $this->deleteQuotes($quoteCollection); + } } } + + /** + * Deletes all quotes in collection + * + * @param QuoteCollection $quoteCollection + */ + private function deleteQuotes(QuoteCollection $quoteCollection): void + { + foreach ($quoteCollection as $quote) { + try { + $this->quoteRepository->delete($quote); + } catch (Exception $e) { + $message = sprintf( + 'Unable to delete expired quote (ID: %s): %s', + $quote->getId(), + (string)$e + ); + $this->logger->error($message); + } + } + + $quoteCollection->clear(); + } } diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontCreateOrdersWithMoveJSCodeBottomTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontCreateOrdersWithMoveJSCodeBottomTest.xml index 0888132669177..1c67d778937d1 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontCreateOrdersWithMoveJSCodeBottomTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontCreateOrdersWithMoveJSCodeBottomTest.xml @@ -10,8 +10,10 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="StorefrontCreateOrdersWithMoveJSCodeBottomTest"> <annotations> + <stories value="Create a product and orders with set 'Move Js code to the bottom' to 'Yes'."/> <title value="Create a product and orders with set 'Move Js code to the bottom' to 'Yes'."/> <description value="Create a product and orders with a set 'Move JS code to the bottom of the page' to 'Yes' for registered customers and guests."/> + <severity value="MAJOR"/> </annotations> <before> <magentoCLI command="config:set {{StorefrontEnableMoveJsCodeBottom.path}} {{StorefrontEnableMoveJsCodeBottom.value}}" stepKey="moveJsCodeBottomEnable"/> diff --git a/app/code/Magento/SampleData/Console/Command/SampleDataDeployCommand.php b/app/code/Magento/SampleData/Console/Command/SampleDataDeployCommand.php index 57a61fecae5ca..76159dc8320e1 100644 --- a/app/code/Magento/SampleData/Console/Command/SampleDataDeployCommand.php +++ b/app/code/Magento/SampleData/Console/Command/SampleDataDeployCommand.php @@ -7,63 +7,83 @@ namespace Magento\SampleData\Console\Command; use Composer\Console\Application; +use Composer\Console\ApplicationFactory; +use Exception; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Console\Cli; +use Magento\Framework\Exception\FileSystemException; +use Magento\Framework\Exception\InvalidArgumentException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Filesystem; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\SampleData\Model\Dependency; use Magento\Setup\Model\PackagesAuth; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\ArrayInputFactory; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** * Command for deployment of Sample Data + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class SampleDataDeployCommand extends Command { const OPTION_NO_UPDATE = 'no-update'; /** - * @var \Magento\Framework\Filesystem + * @var Filesystem */ private $filesystem; /** - * @var \Magento\SampleData\Model\Dependency + * @var Dependency */ private $sampleDataDependency; /** - * @var \Symfony\Component\Console\Input\ArrayInputFactory + * @var ArrayInputFactory * @deprecated 100.1.0 */ private $arrayInputFactory; /** - * @var \Composer\Console\ApplicationFactory + * @var ApplicationFactory */ private $applicationFactory; /** - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\SampleData\Model\Dependency $sampleDataDependency - * @param \Symfony\Component\Console\Input\ArrayInputFactory $arrayInputFactory - * @param \Composer\Console\ApplicationFactory $applicationFactory + * @var Json + */ + private $serializer; + + /** + * @param Filesystem $filesystem + * @param Dependency $sampleDataDependency + * @param ArrayInputFactory $arrayInputFactory + * @param ApplicationFactory $applicationFactory + * @param Json $serializer */ public function __construct( - \Magento\Framework\Filesystem $filesystem, - \Magento\SampleData\Model\Dependency $sampleDataDependency, - \Symfony\Component\Console\Input\ArrayInputFactory $arrayInputFactory, - \Composer\Console\ApplicationFactory $applicationFactory + Filesystem $filesystem, + Dependency $sampleDataDependency, + ArrayInputFactory $arrayInputFactory, + ApplicationFactory $applicationFactory, + Json $serializer ) { $this->filesystem = $filesystem; $this->sampleDataDependency = $sampleDataDependency; $this->arrayInputFactory = $arrayInputFactory; $this->applicationFactory = $applicationFactory; + $this->serializer = $serializer; parent::__construct(); } /** - * {@inheritdoc} + * @inheritdoc */ protected function configure() { @@ -79,15 +99,42 @@ protected function configure() } /** - * {@inheritdoc} + * @inheritdoc + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + * @throws FileSystemException + * @throws LocalizedException */ protected function execute(InputInterface $input, OutputInterface $output) { - $rootJson = json_decode($this->filesystem->getDirectoryRead(DirectoryList::ROOT)->readFile("composer.json")); - if (!isset($rootJson->version)) { - // @codingStandardsIgnoreLine - $output->writeln('<info>' . 'Git installations must deploy sample data from GitHub; see https://devdocs.magento.com/guides/v2.3/install-gde/install/sample-data-after-clone.html for more information.' . '</info>'); - return; + $rootJson = $this->serializer->unserialize( + $this->filesystem->getDirectoryRead( + DirectoryList::ROOT + )->readFile("composer.json") + ); + if (!isset($rootJson['version'])) { + $magentoProductPackage = array_filter( + $rootJson['require'], + function ($package) { + return false !== strpos($package, 'magento/product-'); + }, + ARRAY_FILTER_USE_KEY + ); + $version = reset($magentoProductPackage); + $output->writeln( + '<info>' . + // @codingStandardsIgnoreLine + 'We don\'t recommend to remove the "version" field from your composer.json; see https://getcomposer.org/doc/02-libraries.md#library-versioning for more information.' . + '</info>' + ); + $restoreVersion = new ArrayInput([ + 'command' => 'config', + 'setting-key' => 'version', + 'setting-value' => [$version], + '--quiet' => 1 + ]); } $this->updateMemoryLimit(); $this->createAuthFile(); @@ -109,6 +156,12 @@ protected function execute(InputInterface $input, OutputInterface $output) /** @var Application $application */ $application = $this->applicationFactory->create(); $application->setAutoExit(false); + if (!empty($restoreVersion)) { + $result = $application->run($restoreVersion, clone $output); + if ($result === 0) { + $output->writeln('<info>The field "version" has been restored.</info>'); + } + } $result = $application->run($commandInput, $output); if ($result !== 0) { $output->writeln( @@ -116,9 +169,15 @@ protected function execute(InputInterface $input, OutputInterface $output) . '</info>' ); $application->resetComposer(); + + return Cli::RETURN_FAILURE; } + + return Cli::RETURN_SUCCESS; } else { $output->writeln('<info>' . 'There is no sample data for current set of modules.' . '</info>'); + + return Cli::RETURN_FAILURE; } } @@ -128,7 +187,7 @@ protected function execute(InputInterface $input, OutputInterface $output) * We create auth.json with correct permissions instead of relying on Composer. * * @return void - * @throws \Exception + * @throws LocalizedException */ private function createAuthFile() { @@ -137,30 +196,51 @@ private function createAuthFile() if (!$directory->isExist(PackagesAuth::PATH_TO_AUTH_FILE)) { try { $directory->writeFile(PackagesAuth::PATH_TO_AUTH_FILE, '{}'); - } catch (\Exception $e) { - $message = 'Error in writing Auth file ' - . $directory->getAbsolutePath(PackagesAuth::PATH_TO_AUTH_FILE) - . '. Please check permissions for writing.'; - throw new \Exception($message); + } catch (Exception $e) { + throw new LocalizedException(__( + 'Error in writing Auth file %1. Please check permissions for writing.', + $directory->getAbsolutePath(PackagesAuth::PATH_TO_AUTH_FILE) + )); } } } /** + * Updates PHP memory limit + * + * @throws InvalidArgumentException * @return void */ private function updateMemoryLimit() { if (function_exists('ini_set')) { - @ini_set('display_errors', 1); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $result = ini_set('display_errors', 1); + if ($result === false) { + $error = error_get_last(); + throw new InvalidArgumentException(__( + 'Failed to set ini option display_errors to value 1. %1', + $error['message'] + )); + } $memoryLimit = trim(ini_get('memory_limit')); if ($memoryLimit != -1 && $this->getMemoryInBytes($memoryLimit) < 756 * 1024 * 1024) { - @ini_set('memory_limit', '756M'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $result = ini_set('memory_limit', '756M'); + if ($result === false) { + $error = error_get_last(); + throw new InvalidArgumentException(__( + 'Failed to set ini option memory_limit to 756M. %1', + $error['message'] + )); + } } } } /** + * Retrieves the memory size in bytes + * * @param string $value * @return int */ diff --git a/app/code/Magento/SampleData/Test/Unit/Console/Command/AbstractSampleDataCommandTest.php b/app/code/Magento/SampleData/Test/Unit/Console/Command/AbstractSampleDataCommandTest.php index 51235dbffc417..3bf664ea6b0d2 100644 --- a/app/code/Magento/SampleData/Test/Unit/Console/Command/AbstractSampleDataCommandTest.php +++ b/app/code/Magento/SampleData/Test/Unit/Console/Command/AbstractSampleDataCommandTest.php @@ -20,10 +20,22 @@ use Symfony\Component\Console\Input\ArrayInputFactory; /** + * Common class for tests + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractSampleDataCommandTest extends TestCase { + /* + * Expected arguments for `composer config` to set missing field "version" + */ + private const STUB_EXPECTED_COMPOSER_CONFIG = [ + 'command' => 'config', + 'setting-key' => 'version', + 'setting-value' => ['0.0.1'], + '--quiet' => 1 + ]; + /** * @var ReadInterface|MockObject */ @@ -60,8 +72,10 @@ abstract class AbstractSampleDataCommandTest extends TestCase protected $applicationFactoryMock; /** - * @return void + * @var int */ + private $appRunResult; + protected function setUp(): void { $this->directoryReadMock = $this->getMockForAbstractClass(ReadInterface::class); @@ -74,47 +88,84 @@ protected function setUp(): void } /** - * @param array $sampleDataPackages Array in form [package_name => version_constraint] - * @param string $pathToComposerJson Fake path to composer.json - * @param int $appRunResult Composer exit code + * @param array $sampleDataPackages Array in form [package_name => version_constraint] + * @param string $pathToComposerJson Fake path to composer.json + * @param int $appRunResult Composer exit code + * @param array $composerJsonContent Content of the composer.json * @param array $additionalComposerArgs Additional arguments that composer expects */ protected function setupMocks( $sampleDataPackages, $pathToComposerJson, $appRunResult, + $composerJsonContent = [], $additionalComposerArgs = [] ) { - $this->directoryReadMock->expects($this->any())->method('getAbsolutePath')->willReturn($pathToComposerJson); - $this->directoryReadMock->expects($this->any())->method('readFile')->with('composer.json')->willReturn( - '{"version": "0.0.1"}' - ); - $this->filesystemMock->expects($this->any())->method('getDirectoryRead')->with(DirectoryList::ROOT)->willReturn( - $this->directoryReadMock - ); - $this->sampleDataDependencyMock->expects($this->any())->method('getSampleDataPackages')->willReturn( - $sampleDataPackages - ); + $this->appRunResult = $appRunResult; + $this->directoryReadMock->expects($this->any()) + ->method('getAbsolutePath') + ->willReturn($pathToComposerJson); + $this->directoryReadMock->expects($this->any()) + ->method('readFile') + ->with('composer.json') + ->willReturn(json_encode($composerJsonContent)); + $this->filesystemMock->expects($this->any()) + ->method('getDirectoryRead') + ->with(DirectoryList::ROOT) + ->willReturn($this->directoryReadMock); + $this->sampleDataDependencyMock->expects($this->any()) + ->method('getSampleDataPackages') + ->willReturn($sampleDataPackages); $this->arrayInputFactoryMock->expects($this->never())->method('create'); - $this->applicationMock->expects($this->any()) - ->method('run') - ->with( - new ArrayInput( - array_merge( - $this->expectedComposerArguments( - $sampleDataPackages, - $pathToComposerJson + if (!array_key_exists('version', $composerJsonContent)) { + $this->applicationMock->expects($this->any()) + ->method('run') + ->withConsecutive( + [ + 'input' => new ArrayInput( + self::STUB_EXPECTED_COMPOSER_CONFIG ), - $additionalComposerArgs - ) - ), - $this->anything() - ) - ->willReturn($appRunResult); + 'output' => $this->anything() + ], + [ + 'input' => new ArrayInput( + array_merge( + $this->expectedComposerArgumentsSampleDataCommands( + $sampleDataPackages, + $pathToComposerJson + ), + $additionalComposerArgs + ) + ), + 'output' => $this->anything() + ] + )->willReturnOnConsecutiveCalls( + $this->returnValue(0), + $this->returnValue($appRunResult) + ); + } else { + $this->applicationMock->expects($this->any()) + ->method('run') + ->with( + new ArrayInput( + array_merge( + $this->expectedComposerArgumentsSampleDataCommands( + $sampleDataPackages, + $pathToComposerJson + ), + $additionalComposerArgs + ) + ), + $this->anything() + ) + ->willReturn($appRunResult); + } if (($appRunResult !== 0) && !empty($sampleDataPackages)) { - $this->applicationMock->expects($this->once())->method('resetComposer')->willReturnSelf(); + $this->applicationMock->expects($this->any()) + ->method('resetComposer') + ->willReturnSelf(); } $this->applicationFactoryMock->expects($this->any()) @@ -123,14 +174,14 @@ protected function setupMocks( } /** - * Expected arguments for composer based on sample data packages and composer.json path + * Expected arguments for composer based on sample data command * * @param array $sampleDataPackages * @param string $pathToComposerJson * @return array */ - abstract protected function expectedComposerArguments( + abstract protected function expectedComposerArgumentsSampleDataCommands( array $sampleDataPackages, string $pathToComposerJson - ) : array; + ): array; } diff --git a/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataDeployCommandTest.php b/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataDeployCommandTest.php index 45db83403b4f5..a1186d6015871 100644 --- a/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataDeployCommandTest.php +++ b/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataDeployCommandTest.php @@ -7,26 +7,54 @@ namespace Magento\SampleData\Test\Unit\Console\Command; +use Exception; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\Serialize\Serializer\Json; use Magento\SampleData\Console\Command\SampleDataDeployCommand; use Magento\Setup\Model\PackagesAuth; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Console\Tester\CommandTester; class SampleDataDeployCommandTest extends AbstractSampleDataCommandTest { /** + * @var Json|MockObject + */ + private $serializerMock; + + protected function setUp(): void + { + parent::setUp(); + $this->serializerMock = $this->createMock(Json::class); + } + + /** + * Sets mock for unserialization composer content + * @param array $composerJsonContent + * @return void + */ + protected function setupMockForSerializer(array $composerJsonContent): void + { + $this->serializerMock->expects($this->any()) + ->method('unserialize') + ->will($this->returnValue($composerJsonContent)); + } + + /** + * Sets mocks for auth file + * * @param bool $authExist True to test with existing auth.json, false without + * @return void */ - protected function setupMocksForAuthFile($authExist) + protected function setupMocksForAuthFile(bool $authExist): void { $this->directoryWriteMock->expects($this->once()) ->method('isExist') ->with(PackagesAuth::PATH_TO_AUTH_FILE) ->willReturn($authExist); - $this->directoryWriteMock->expects($authExist ? $this->never() : $this->once())->method('writeFile')->with( - PackagesAuth::PATH_TO_AUTH_FILE, - '{}' - ); + $this->directoryWriteMock->expects($authExist ? $this->never() : $this->once()) + ->method('writeFile') + ->with(PackagesAuth::PATH_TO_AUTH_FILE, '{}'); $this->filesystemMock->expects($this->once()) ->method('getDirectoryWrite') ->with(DirectoryList::COMPOSER_HOME) @@ -34,18 +62,30 @@ protected function setupMocksForAuthFile($authExist) } /** - * @param array $sampleDataPackages - * @param int $appRunResult - int 0 if everything went fine, or an error code - * @param string $expectedMsg - * @param bool $authExist - * @return void + * @param array $sampleDataPackages + * @param int $appRunResult - int 0 if everything went fine, or an error code + * @param array $composerJsonContent + * @param string $expectedMsg + * @param bool $authExist + * @return void * * @dataProvider processDataProvider */ - public function testExecute(array $sampleDataPackages, $appRunResult, $expectedMsg, $authExist) - { - $this->setupMocks($sampleDataPackages, '/path/to/composer.json', $appRunResult); + public function testExecute( + array $sampleDataPackages, + int $appRunResult, + array $composerJsonContent, + string $expectedMsg, + bool $authExist + ): void { + $this->setupMocks( + $sampleDataPackages, + '/path/to/composer.json', + $appRunResult, + $composerJsonContent + ); $this->setupMocksForAuthFile($authExist); + $this->setupMockForSerializer($composerJsonContent); $commandTester = $this->createCommandTester(); $commandTester->execute([]); @@ -53,23 +93,31 @@ public function testExecute(array $sampleDataPackages, $appRunResult, $expectedM } /** - * @param array $sampleDataPackages - * @param int $appRunResult - int 0 if everything went fine, or an error code - * @param string $expectedMsg - * @param bool $authExist - * @return void + * @param array $sampleDataPackages + * @param int $appRunResult - int 0 if everything went fine, or an error code + * @param array $composerJsonContent + * @param string $expectedMsg + * @param bool $authExist + * @return void * * @dataProvider processDataProvider */ - public function testExecuteWithNoUpdate(array $sampleDataPackages, $appRunResult, $expectedMsg, $authExist) - { + public function testExecuteWithNoUpdate( + array $sampleDataPackages, + int $appRunResult, + array $composerJsonContent, + string $expectedMsg, + bool $authExist + ): void { $this->setupMocks( $sampleDataPackages, '/path/to/composer.json', $appRunResult, + $composerJsonContent, ['--no-update' => 1] ); $this->setupMocksForAuthFile($authExist); + $this->setupMockForSerializer($composerJsonContent); $commandInput = ['--no-update' => 1]; $commandTester = $this->createCommandTester(); @@ -79,14 +127,20 @@ public function testExecuteWithNoUpdate(array $sampleDataPackages, $appRunResult } /** + * Data provider + * * @return array */ - public function processDataProvider() + public function processDataProvider(): array { return [ 'No sample data found' => [ 'sampleDataPackages' => [], 'appRunResult' => 1, + 'composerJsonContent' => [ + 'require' => ["magento/product-community-edition" => "0.0.1"], + 'version' => '0.0.1' + ], 'expectedMsg' => 'There is no sample data for current set of modules.' . PHP_EOL, 'authExist' => true, ], @@ -95,15 +149,36 @@ public function processDataProvider() 'magento/module-cms-sample-data' => '1.0.0-beta', ], 'appRunResult' => 1, + 'composerJsonContent' => [ + 'require' => ["magento/product-community-edition" => "0.0.1"], + 'version' => '0.0.1' + ], 'expectedMsg' => 'There is an error during sample data deployment. Composer file will be reverted.' . PHP_EOL, 'authExist' => false, ], + 'Successful sample data installation without field "version"' => [ + 'sampleDataPackages' => [ + 'magento/module-cms-sample-data' => '1.0.0-beta', + ], + 'appRunResult' => 0, + 'composerJsonContent' => [ + 'require' => ["magento/product-community-edition" => "0.0.1"] + ], + // @codingStandardsIgnoreLine + 'expectedMsg' => 'We don\'t recommend to remove the "version" field from your composer.json; see https://getcomposer.org/doc/02-libraries.md#library-versioning for more information.' + . PHP_EOL . 'The field "version" has been restored.' . PHP_EOL, + 'authExist' => true, + ], 'Successful sample data installation' => [ 'sampleDataPackages' => [ 'magento/module-cms-sample-data' => '1.0.0-beta', ], 'appRunResult' => 0, + 'composerJsonContent' => [ + 'require' => ["magento/product-community-edition" => "0.0.1"], + 'version' => '0.0.1' + ], 'expectedMsg' => '', 'authExist' => true, ], @@ -113,7 +188,7 @@ public function processDataProvider() /** * @return void */ - public function testExecuteWithException() + public function testExecuteWithException(): void { $this->expectException(\Exception::class); $this->expectExceptionMessage( @@ -122,12 +197,17 @@ public function testExecuteWithException() $this->directoryReadMock->expects($this->once()) ->method('readFile') ->with('composer.json') - ->willReturn('{"version": "0.0.1"}'); + ->willReturn('{"require": {"magento/product-community-edition": "0.0.1"}, "version": "0.0.1"}'); + $this->serializerMock->expects($this->any()) + ->method('unserialize') + ->will($this->returnValue([ + 'require' => ["magento/product-community-edition" => "0.0.1"], + 'version' => '0.0.1' + ])); $this->filesystemMock->expects($this->once()) ->method('getDirectoryRead') ->with(DirectoryList::ROOT) ->willReturn($this->directoryReadMock); - $this->directoryWriteMock->expects($this->once()) ->method('isExist') ->with(PackagesAuth::PATH_TO_AUTH_FILE) @@ -135,7 +215,7 @@ public function testExecuteWithException() $this->directoryWriteMock->expects($this->once()) ->method('writeFile') ->with(PackagesAuth::PATH_TO_AUTH_FILE, '{}') - ->willThrowException(new \Exception('Something went wrong...')); + ->willThrowException(new Exception('Something went wrong...')); $this->directoryWriteMock->expects($this->once()) ->method('getAbsolutePath') ->with(PackagesAuth::PATH_TO_AUTH_FILE) @@ -153,15 +233,15 @@ public function testExecuteWithException() */ private function createCommandTester(): CommandTester { - $commandTester = new CommandTester( + return new CommandTester( new SampleDataDeployCommand( $this->filesystemMock, $this->sampleDataDependencyMock, $this->arrayInputFactoryMock, - $this->applicationFactoryMock + $this->applicationFactoryMock, + $this->serializerMock ) ); - return $commandTester; } /** @@ -169,10 +249,10 @@ private function createCommandTester(): CommandTester * @param $pathToComposerJson * @return array */ - protected function expectedComposerArguments( + protected function expectedComposerArgumentsSampleDataCommands( array $sampleDataPackages, string $pathToComposerJson - ) : array { + ): array { return [ 'command' => 'require', '--working-dir' => $pathToComposerJson, diff --git a/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataRemoveCommandTest.php b/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataRemoveCommandTest.php index cbb562ff10f25..9883100ce5c49 100644 --- a/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataRemoveCommandTest.php +++ b/app/code/Magento/SampleData/Test/Unit/Console/Command/SampleDataRemoveCommandTest.php @@ -10,20 +10,32 @@ use Magento\SampleData\Console\Command\SampleDataRemoveCommand; use Symfony\Component\Console\Tester\CommandTester; +/** + * Tests for command `sampledata:remove` + */ class SampleDataRemoveCommandTest extends AbstractSampleDataCommandTest { - /** - * @param array $sampleDataPackages - * @param int $appRunResult - int 0 if everything went fine, or an error code - * @param string $expectedMsg - * @return void + * @param array $sampleDataPackages + * @param int $appRunResult - int 0 if everything went fine, or an error code + * @param array $composerJsonContent + * @param string $expectedMsg + * @return void * * @dataProvider processDataProvider */ - public function testExecute(array $sampleDataPackages, $appRunResult, $expectedMsg) - { - $this->setupMocks($sampleDataPackages, '/path/to/composer.json', $appRunResult); + public function testExecute( + array $sampleDataPackages, + int $appRunResult, + array $composerJsonContent, + string $expectedMsg + ): void { + $this->setupMocks( + $sampleDataPackages, + '/path/to/composer.json', + $appRunResult, + $composerJsonContent + ); $commandTester = $this->createCommandTester(); $commandTester->execute([]); @@ -31,19 +43,25 @@ public function testExecute(array $sampleDataPackages, $appRunResult, $expectedM } /** - * @param array $sampleDataPackages - * @param int $appRunResult - int 0 if everything went fine, or an error code - * @param string $expectedMsg - * @return void + * @param array $sampleDataPackages + * @param int $appRunResult - int 0 if everything went fine, or an error code + * @param array $composerJsonContent + * @param string $expectedMsg + * @return void * * @dataProvider processDataProvider */ - public function testExecuteWithNoUpdate(array $sampleDataPackages, $appRunResult, $expectedMsg) - { + public function testExecuteWithNoUpdate( + array $sampleDataPackages, + int $appRunResult, + array $composerJsonContent, + string $expectedMsg + ): void { $this->setupMocks( $sampleDataPackages, '/path/to/composer.json', $appRunResult, + $composerJsonContent, ['--no-update' => 1] ); $commandInput = ['--no-update' => 1]; @@ -55,32 +73,51 @@ public function testExecuteWithNoUpdate(array $sampleDataPackages, $appRunResult } /** + * Data provider + * * @return array */ - public function processDataProvider() + public function processDataProvider(): array { return [ - 'No sample data found' => [ - 'sampleDataPackages' => [], + 'No sample data found in require' => [ + 'sampleDataPackages' => [ + 'magento/module-cms-sample-data' => '1.0.0-beta', + ], 'appRunResult' => 1, - 'expectedMsg' => 'There is no sample data for current set of modules.' . PHP_EOL, + 'composerJsonContent' => [ + "require" => [ + "magento/product-community-edition" => "0.0.1", + ], + "version" => "0.0.1" + ], + 'expectedMsg' => 'There is an error during remove sample data.' . PHP_EOL, ], - 'Successful sample data installation' => [ + 'Successful sample data removing' => [ 'sampleDataPackages' => [ 'magento/module-cms-sample-data' => '1.0.0-beta', ], 'appRunResult' => 0, + 'composerJsonContent' => [ + "require" => [ + "magento/product-community-edition" => "0.0.1", + "magento/module-cms-sample-data" => "1.0.0-beta", + ], + "version" => "0.0.1" + ], 'expectedMsg' => '', ], ]; } /** + * Creates command tester + * * @return CommandTester */ private function createCommandTester(): CommandTester { - $commandTester = new CommandTester( + return new CommandTester( new SampleDataRemoveCommand( $this->filesystemMock, $this->sampleDataDependencyMock, @@ -88,15 +125,16 @@ private function createCommandTester(): CommandTester $this->applicationFactoryMock ) ); - return $commandTester; } /** + * Returns expected arguments for command `composer remove` + * * @param $sampleDataPackages * @param $pathToComposerJson * @return array */ - protected function expectedComposerArguments( + protected function expectedComposerArgumentsSampleDataCommands( array $sampleDataPackages, string $pathToComposerJson ) : array { diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml index a75f65dffeca3..83e3479c753e4 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewIntegrationTest.xml @@ -18,8 +18,6 @@ <testCaseId value="MC-14382" /> <group value="security"/> <group value="mtf_migrated"/> - <!-- skip due to MQE-1964 --> - <group value="skip"/> </annotations> <before> <!-- Log in to Admin Panel --> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml index 3d04f3eed4daf..3fffbcd480761 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml @@ -18,8 +18,6 @@ <testCaseId value="MC-14384" /> <group value="security"/> <group value="mtf_migrated"/> - <!-- skip due to MQE-1964 --> - <group value="skip"/> </annotations> <before> <!-- Log in to Admin Panel --> @@ -41,7 +39,7 @@ <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again." /> <argument name="messageType" value="error" /> </actionGroup> - + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldSecondAttempt"> <argument name="role" value="roleAdministrator" /> <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpirationTest.xml similarity index 97% rename from app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml rename to app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpirationTest.xml index 3fb798521fb45..1421b589d5669 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithInvalidExpirationTest.xml @@ -9,7 +9,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminLoginAdminUserWithInvalidExpiration"> + <test name="AdminLoginAdminUserWithInvalidExpirationTest"> <annotations> <features value="Security"/> <stories value="Try to login as a user with an invalid expiration date."/> diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpirationTest.xml similarity index 97% rename from app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml rename to app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpirationTest.xml index 5d12650351bc0..9a9ae8f3872ba 100644 --- a/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpiration.xml +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLoginAdminUserWithValidExpirationTest.xml @@ -9,7 +9,7 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> - <test name="AdminLoginAdminUserWithValidExpiration"> + <test name="AdminLoginAdminUserWithValidExpirationTest"> <annotations> <features value="Security"/> <stories value="Login as a user with a valid expiration date."/> diff --git a/app/code/Magento/Swatches/Block/LayeredNavigation/RenderLayered.php b/app/code/Magento/Swatches/Block/LayeredNavigation/RenderLayered.php index fc13372520945..9ba1083adab74 100644 --- a/app/code/Magento/Swatches/Block/LayeredNavigation/RenderLayered.php +++ b/app/code/Magento/Swatches/Block/LayeredNavigation/RenderLayered.php @@ -5,11 +5,17 @@ */ namespace Magento\Swatches\Block\LayeredNavigation; -use Magento\Eav\Model\Entity\Attribute; +use Magento\Catalog\Model\Layer\Filter\AbstractFilter; +use Magento\Catalog\Model\Layer\Filter\Item as FilterItem; use Magento\Catalog\Model\ResourceModel\Layer\Filter\AttributeFactory; -use Magento\Framework\View\Element\Template; +use Magento\Eav\Model\Entity\Attribute; use Magento\Eav\Model\Entity\Attribute\Option; -use Magento\Catalog\Model\Layer\Filter\Item as FilterItem; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Swatches\Helper\Data; +use Magento\Swatches\Helper\Media; +use Magento\Theme\Block\Html\Pager; /** * Class RenderLayered Render Swatches at Layered Navigation @@ -37,7 +43,7 @@ class RenderLayered extends Template protected $eavAttribute; /** - * @var \Magento\Catalog\Model\Layer\Filter\AbstractFilter + * @var AbstractFilter */ protected $filter; @@ -47,41 +53,52 @@ class RenderLayered extends Template protected $layerAttribute; /** - * @var \Magento\Swatches\Helper\Data + * @var Data */ protected $swatchHelper; /** - * @var \Magento\Swatches\Helper\Media + * @var Media */ protected $mediaHelper; /** - * @param Template\Context $context + * @var Pager + */ + private $htmlPagerBlock; + + /** + * @param Context $context * @param Attribute $eavAttribute * @param AttributeFactory $layerAttribute - * @param \Magento\Swatches\Helper\Data $swatchHelper - * @param \Magento\Swatches\Helper\Media $mediaHelper + * @param Data $swatchHelper + * @param Media $mediaHelper * @param array $data + * @param Pager|null $htmlPagerBlock */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, + Context $context, Attribute $eavAttribute, AttributeFactory $layerAttribute, - \Magento\Swatches\Helper\Data $swatchHelper, - \Magento\Swatches\Helper\Media $mediaHelper, - array $data = [] + Data $swatchHelper, + Media $mediaHelper, + array $data = [], + ?Pager $htmlPagerBlock = null ) { $this->eavAttribute = $eavAttribute; $this->layerAttribute = $layerAttribute; $this->swatchHelper = $swatchHelper; $this->mediaHelper = $mediaHelper; + $this->htmlPagerBlock = $htmlPagerBlock ?? ObjectManager::getInstance()->get(Pager::class); parent::__construct($context, $data); } /** + * Set filter and attribute objects + * * @param \Magento\Catalog\Model\Layer\Filter\AbstractFilter $filter + * * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ @@ -94,6 +111,8 @@ public function setSwatchFilter(\Magento\Catalog\Model\Layer\Filter\AbstractFilt } /** + * Get attribute swatch data + * * @return array */ public function getSwatchData() @@ -114,30 +133,46 @@ public function getSwatchData() $attributeOptionIds = array_keys($attributeOptions); $swatches = $this->swatchHelper->getSwatchesByOptionsId($attributeOptionIds); - $data = [ + return [ 'attribute_id' => $this->eavAttribute->getId(), 'attribute_code' => $this->eavAttribute->getAttributeCode(), 'attribute_label' => $this->eavAttribute->getStoreLabel(), 'options' => $attributeOptions, 'swatches' => $swatches, ]; - - return $data; } /** + * Build filter option url + * * @param string $attributeCode * @param int $optionId + * * @return string */ public function buildUrl($attributeCode, $optionId) { - $query = [$attributeCode => $optionId]; - return $this->_urlBuilder->getUrl('*/*/*', ['_current' => true, '_use_rewrite' => true, '_query' => $query]); + $query = [ + $attributeCode => $optionId, + // exclude current page from urls + $this->htmlPagerBlock->getPageVarName() => null + ]; + + return $this->_urlBuilder->getUrl( + '*/*/*', + [ + '_current' => true, + '_use_rewrite' => true, + '_query' => $query + ] + ); } /** + * Get view data for option with no results + * * @param Option $swatchOption + * * @return array */ protected function getUnusedOption(Option $swatchOption) @@ -150,8 +185,11 @@ protected function getUnusedOption(Option $swatchOption) } /** + * Get option data if visible + * * @param FilterItem[] $filterItems * @param Option $swatchOption + * * @return array */ protected function getFilterOption(array $filterItems, Option $swatchOption) @@ -166,8 +204,11 @@ protected function getFilterOption(array $filterItems, Option $swatchOption) } /** + * Get view data for option + * * @param FilterItem $filterItem * @param Option $swatchOption + * * @return array */ protected function getOptionViewData(FilterItem $filterItem, Option $swatchOption) @@ -187,15 +228,20 @@ protected function getOptionViewData(FilterItem $filterItem, Option $swatchOptio } /** + * Check if option should be visible + * * @param FilterItem $filterItem + * * @return bool */ protected function isOptionVisible(FilterItem $filterItem) { - return $this->isOptionDisabled($filterItem) && $this->isShowEmptyResults() ? false : true; + return !($this->isOptionDisabled($filterItem) && $this->isShowEmptyResults()); } /** + * Check if attribute values should be visible with no results + * * @return bool */ protected function isShowEmptyResults() @@ -204,7 +250,10 @@ protected function isShowEmptyResults() } /** + * Check if option should be disabled + * * @param FilterItem $filterItem + * * @return bool */ protected function isOptionDisabled(FilterItem $filterItem) @@ -213,8 +262,11 @@ protected function isOptionDisabled(FilterItem $filterItem) } /** + * Retrieve filter item by id + * * @param FilterItem[] $filterItems * @param integer $id + * * @return bool|FilterItem */ protected function getFilterItemById(array $filterItems, $id) @@ -228,14 +280,15 @@ protected function getFilterItemById(array $filterItems, $id) } /** + * Get swatch image path + * * @param string $type * @param string $filename + * * @return string */ public function getSwatchPath($type, $filename) { - $imagePath = $this->mediaHelper->getSwatchAttributeImage($type, $filename); - - return $imagePath; + return $this->mediaHelper->getSwatchAttributeImage($type, $filename); } } diff --git a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml index 97a391137d8e3..5f3ec07bd4983 100644 --- a/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml +++ b/app/code/Magento/Swatches/Test/Mftf/ActionGroup/AddTextSwatchToProductActionGroup.xml @@ -19,6 +19,7 @@ <argument name="option2" defaultValue="textSwatchOption2" type="string"/> <argument name="option3" defaultValue="textSwatchOption3" type="string"/> <argument name="usedInProductListing" defaultValue="No" type="string"/> + <argument name="usedInLayeredNavigation" defaultValue="No" type="string"/> </arguments> <amOnPage url="{{ProductAttributePage.url}}" stepKey="goToNewProductAttributePage"/> @@ -41,6 +42,7 @@ <click selector="{{StorefrontPropertiesSection.StoreFrontPropertiesTab}}" stepKey="clickStorefrontPropertiesTab"/> <waitForElementVisible selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" stepKey="waitForTabSwitch"/> <selectOption selector="{{AdvancedAttributePropertiesSection.UseInProductListing}}" userInput="{{usedInProductListing}}" stepKey="useInProductListing"/> + <selectOption selector="{{AttributePropertiesSection.useInLayeredNavigation}}" userInput="{{usedInLayeredNavigation}}" stepKey="useInLayeredNavigation"/> <click selector="{{AttributePropertiesSection.SaveAndEdit}}" stepKey="clickSave"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontRedirectToFirstPageOnFilteringBySwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontRedirectToFirstPageOnFilteringBySwatchTest.xml new file mode 100644 index 0000000000000..c6266e034bffc --- /dev/null +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontRedirectToFirstPageOnFilteringBySwatchTest.xml @@ -0,0 +1,103 @@ +<?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="StorefrontRedirectToFirstPageOnFilteringBySwatchTest"> + <annotations> + <features value="Swatches"/> + <stories value="Filter by swatch attribute on plp layered navigation"/> + <title value="Customers are redirected to first plp page after filtering by swatch"/> + <description value="Customers are redirected to first plp page after filtering by swatch"/> + <severity value="MINOR"/> + <group value="Swatches"/> + </annotations> + + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createSimpleProduct3"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <magentoCLI command="config:set catalog/frontend/grid_per_page 1" stepKey="setOneProductPerPage"/> + <magentoCLI command="config:set catalog/frontend/grid_per_page_values 1" stepKey="setGridPerPage"/> + + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <actionGroup ref="AddTextSwatchToProductActionGroup" stepKey="addSwatchAttribute"> + <argument name="usedInLayeredNavigation" value="1"/> + </actionGroup> + </before> + + <after> + <actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteSwatchAttribute"> + <argument name="ProductAttribute" value="textSwatchAttribute"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="adminLogout"/> + + <magentoCLI command="config:set catalog/frontend/grid_per_page 12" stepKey="setDefaultProductsPerPage"/> + <magentoCLI command="config:set catalog/frontend/grid_per_page_values 12,24,36" stepKey="setDefaultGridPerPage"/> + <magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/> + + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createSimpleProduct2" stepKey="deleteSimpleProduct2"/> + <deleteData createDataKey="createSimpleProduct3" stepKey="deleteSimpleProduct3"/> + </after> + + <amOnPage url="{{AdminProductAttributeSetEditPage.url}}/{{AddToDefaultSet.attributeSetId}}/" stepKey="onAttributeSetEdit"/> + <actionGroup ref="AssignAttributeToGroupActionGroup" stepKey="assignAttributeToGroup"> + <argument name="group" value="Product Details"/> + <argument name="attribute" value="{{textSwatchAttribute.attribute_code}}"/> + </actionGroup> + <actionGroup ref="SaveAttributeSetActionGroup" stepKey="SaveAttributeSet"/> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductIndexPage"/> + <actionGroup ref="ClearFiltersAdminProductGridActionGroup" stepKey="clearFiltersOnProductIndexPage"/> + + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="goToProduct1EditPage"> + <argument name="product" value="$$createSimpleProduct1$$"/> + </actionGroup> + <selectOption selector="{{AdminProductAttributesSection.attributeDropdownByCode(textSwatchAttribute.attribute_code)}}" userInput="textSwatchOption1" stepKey="selectProduct1AttributeOption"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct1"/> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductsGridPage2"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="goToProduct2EditPage"> + <argument name="product" value="$$createSimpleProduct2$$"/> + </actionGroup> + <selectOption selector="{{AdminProductAttributesSection.attributeDropdownByCode(textSwatchAttribute.attribute_code)}}" userInput="textSwatchOption1" stepKey="selectProduct2AttributeOption"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct2"/> + + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="goToProductsGridPage3"/> + <actionGroup ref="OpenEditProductOnBackendActionGroup" stepKey="goToProduct3EditPage"> + <argument name="product" value="$$createSimpleProduct3$$"/> + </actionGroup> + <selectOption selector="{{AdminProductAttributesSection.attributeDropdownByCode(textSwatchAttribute.attribute_code)}}" userInput="textSwatchOption2" stepKey="selectProduct3AttributeOption"/> + <actionGroup ref="SaveProductFormActionGroup" stepKey="saveProduct3"/> + + <magentoCron groups="index" stepKey="runCronIndexer"/> + + <actionGroup ref="StorefrontNavigateCategoryPageActionGroup" stepKey="navigateToCategoryPage"> + <argument name="category" value="$$createCategory$$"/> + </actionGroup> + <actionGroup ref="StorefrontNavigateCategoryNextPageActionGroup" stepKey="navigateToCategoryNextPage"/> + + <click selector="{{StorefrontCategorySidebarSection.filterOptionTitle(textSwatchAttribute.default_label)}}" stepKey="expandAttribute"/> + <click selector="{{StorefrontCategorySidebarSection.attributeNthOption(textSwatchAttribute.attribute_code, '1')}}" stepKey="filterBySwatch1"/> + <waitForPageLoad stepKey="waitForPageToLoad2"/> + + <actionGroup ref="AssertStorefrontCategoryCurrentPageIsNthActionGroup" stepKey="assertCurrentPageIsFirst"> + <argument name="expectedPage" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Swatches/Test/Unit/Block/LayeredNavigation/RenderLayeredTest.php b/app/code/Magento/Swatches/Test/Unit/Block/LayeredNavigation/RenderLayeredTest.php index 4056bf27f571e..06960c409b476 100644 --- a/app/code/Magento/Swatches/Test/Unit/Block/LayeredNavigation/RenderLayeredTest.php +++ b/app/code/Magento/Swatches/Test/Unit/Block/LayeredNavigation/RenderLayeredTest.php @@ -18,6 +18,7 @@ use Magento\Swatches\Block\LayeredNavigation\RenderLayered; use Magento\Swatches\Helper\Data; use Magento\Swatches\Helper\Media; +use Magento\Theme\Block\Html\Pager; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -28,35 +29,60 @@ */ class RenderLayeredTest extends TestCase { - /** @var MockObject */ - protected $contextMock; - - /** @var MockObject */ - protected $requestMock; - - /** @var MockObject */ - protected $urlBuilder; - - /** @var MockObject */ - protected $eavAttributeMock; - - /** @var MockObject */ - protected $layerAttributeFactoryMock; - - /** @var MockObject */ - protected $layerAttributeMock; - - /** @var MockObject */ - protected $swatchHelperMock; - - /** @var MockObject */ - protected $mediaHelperMock; - - /** @var MockObject */ - protected $filterMock; - - /** @var MockObject */ - protected $block; + /** + * @var RenderLayered|MockObject + */ + private $block; + + /** + * @var Context|MockObject + */ + private $contextMock; + + /** + * @var RequestInterface|MockObject + */ + private $requestMock; + + /** + * @var Url|MockObject + */ + private $urlBuilder; + + /** + * @var Attribute|MockObject + */ + private $eavAttributeMock; + + /** + * @var AttributeFactory|MockObject + */ + private $layerAttributeFactoryMock; + + /** + * @var \Magento\Catalog\Model\ResourceModel\Layer\Filter\Attribute|MockObject + */ + private $layerAttributeMock; + + /** + * @var Data|MockObject + */ + private $swatchHelperMock; + + /** + * @var Media|MockObject + */ + private $mediaHelperMock; + + /** + * @var AbstractFilter|MockObject + */ + private $filterMock; + + /** + * @var Pager|MockObject + */ + private $htmlBlockPagerMock; protected function setUp(): void { @@ -66,8 +92,8 @@ protected function setUp(): void Url::class, ['getCurrentUrl', 'getRedirectUrl', 'getUrl'] ); - $this->contextMock->expects($this->any())->method('getRequest')->willReturn($this->requestMock); - $this->contextMock->expects($this->any())->method('getUrlBuilder')->willReturn($this->urlBuilder); + $this->contextMock->method('getRequest')->willReturn($this->requestMock); + $this->contextMock->method('getUrlBuilder')->willReturn($this->urlBuilder); $this->eavAttributeMock = $this->createMock(Attribute::class); $this->layerAttributeFactoryMock = $this->createPartialMock( AttributeFactory::class, @@ -80,6 +106,7 @@ protected function setUp(): void $this->swatchHelperMock = $this->createMock(Data::class); $this->mediaHelperMock = $this->createMock(Media::class); $this->filterMock = $this->createMock(AbstractFilter::class); + $this->htmlBlockPagerMock = $this->createMock(Pager::class); $this->block = $this->getMockBuilder(RenderLayered::class) ->setMethods(['filter', 'eavAttribute']) @@ -91,6 +118,7 @@ protected function setUp(): void $this->swatchHelperMock, $this->mediaHelperMock, [], + $this->htmlBlockPagerMock ] ) ->getMock(); @@ -114,7 +142,7 @@ public function testGetSwatchData() $item3 = $this->createMock(Item::class); $item4 = $this->createMock(Item::class); - $item1->expects($this->any())->method('__call')->withConsecutive( + $item1->method('__call')->withConsecutive( ['getValue'], ['getCount'], ['getValue'], @@ -128,9 +156,9 @@ public function testGetSwatchData() 'Yellow' ); - $item2->expects($this->any())->method('__call')->with('getValue')->willReturn('blue'); + $item2->method('__call')->with('getValue')->willReturn('blue'); - $item3->expects($this->any())->method('__call')->withConsecutive( + $item3->method('__call')->withConsecutive( ['getValue'], ['getCount'] )->willReturnOnConsecutiveCalls( @@ -138,7 +166,7 @@ public function testGetSwatchData() 0 ); - $item4->expects($this->any())->method('__call')->withConsecutive( + $item4->method('__call')->withConsecutive( ['getValue'], ['getCount'], ['getValue'], @@ -162,22 +190,22 @@ public function testGetSwatchData() $this->block->method('filter')->willReturn($this->filterMock); $option1 = $this->createMock(Option::class); - $option1->expects($this->any())->method('getValue')->willReturn('yellow'); + $option1->method('getValue')->willReturn('yellow'); $option2 = $this->createMock(Option::class); - $option2->expects($this->any())->method('getValue')->willReturn(null); + $option2->method('getValue')->willReturn(null); $option3 = $this->createMock(Option::class); - $option3->expects($this->any())->method('getValue')->willReturn('red'); + $option3->method('getValue')->willReturn('red'); $option4 = $this->createMock(Option::class); - $option4->expects($this->any())->method('getValue')->willReturn('green'); + $option4->method('getValue')->willReturn('green'); $eavAttribute = $this->createMock(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class); $eavAttribute->expects($this->once()) ->method('getOptions') ->willReturn([$option1, $option2, $option3, $option4]); - $eavAttribute->expects($this->any())->method('getIsFilterable')->willReturn(0); + $eavAttribute->method('getIsFilterable')->willReturn(0); $this->filterMock->expects($this->once())->method('getAttributeModel')->willReturn($eavAttribute); $this->block->method('eavAttribute')->willReturn($eavAttribute); @@ -200,7 +228,7 @@ public function testGetSwatchDataException() { $this->block->method('filter')->willReturn($this->filterMock); $this->block->setSwatchFilter($this->filterMock); - $this->expectException('\RuntimeException'); + $this->expectException(\RuntimeException::class); $this->block->getSwatchData(); } diff --git a/app/code/Magento/Theme/view/base/requirejs-config.js b/app/code/Magento/Theme/view/base/requirejs-config.js index f5580461f7d9e..423ac707c6572 100644 --- a/app/code/Magento/Theme/view/base/requirejs-config.js +++ b/app/code/Magento/Theme/view/base/requirejs-config.js @@ -4,8 +4,8 @@ */ var config = { - 'waitSeconds': 0, - 'map': { + waitSeconds: 0, + map: { '*': { 'ko': 'knockoutjs/knockout', 'knockout': 'knockoutjs/knockout', @@ -13,7 +13,7 @@ var config = { 'rjsResolver': 'mage/requirejs/resolver' } }, - 'shim': { + shim: { 'jquery/jquery-migrate': ['jquery'], 'jquery/jstree/jquery.hotkeys': ['jquery'], 'jquery/hover-intent': ['jquery'], @@ -28,7 +28,7 @@ var config = { }, 'magnifier/magnifier': ['jquery'] }, - 'paths': { + paths: { 'jquery/validate': 'jquery/jquery.validate', 'jquery/hover-intent': 'jquery/jquery.hoverIntent', 'jquery/file-uploader': 'jquery/fileUploader/jquery.fileupload-fp', @@ -40,11 +40,11 @@ var config = { 'tinycolor': 'jquery/spectrum/tinycolor', 'jquery-ui-modules': 'jquery/ui-modules' }, - 'deps': [ + deps: [ 'jquery/jquery-migrate' ], - 'config': { - 'mixins': { + config: { + mixins: { 'jquery/jstree/jquery.jstree': { 'mage/backend/jstree-mixin': true }, @@ -52,7 +52,7 @@ var config = { 'jquery/patches/jquery': true } }, - 'text': { + text: { 'headers': { 'X-Requested-With': 'XMLHttpRequest' } @@ -60,6 +60,27 @@ var config = { } }; +/* eslint-disable max-depth */ +/** + * Adds polyfills only for browser contexts which prevents bundlers from including them. + */ +if (typeof window !== 'undefined' && window.document) { + /** + * Polyfill localStorage and sessionStorage for browsers that do not support them. + */ + try { + if (!window.localStorage || !window.sessionStorage) { + throw new Error(); + } + + localStorage.setItem('storage_test', 1); + localStorage.removeItem('storage_test'); + } catch (e) { + config.deps.push('mage/polyfill'); + } +} +/* eslint-enable max-depth */ + require(['jquery'], function ($) { 'use strict'; diff --git a/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml b/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml index a4a10ef3f6ee9..96f8fbed4c041 100644 --- a/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml +++ b/app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml @@ -10,7 +10,6 @@ <meta name="viewport" content="width=device-width, initial-scale=1"/> <css src="mage/calendar.css"/> <script src="requirejs/require.js"/> - <script src="mage/polyfill.js"/> </head> <body> <referenceBlock name="head.additional"> diff --git a/app/code/Magento/Ui/view/base/requirejs-config.js b/app/code/Magento/Ui/view/base/requirejs-config.js index 5e76600673254..4ca2c39781343 100644 --- a/app/code/Magento/Ui/view/base/requirejs-config.js +++ b/app/code/Magento/Ui/view/base/requirejs-config.js @@ -4,6 +4,7 @@ */ var config = { + deps: [], shim: { 'chartjs/Chart.min': ['moment'], 'tiny_mce_4/tinymce.min': { @@ -30,3 +31,29 @@ var config = { } } }; + +/** + * Adds polyfills only for browser contexts which prevents bundlers from including them. + */ +if (typeof window !== 'undefined' && window.document) { + /** + * Polyfill Map and WeakMap for older browsers that do not support them. + */ + if (typeof Map === 'undefined' || typeof WeakMap === 'undefined') { + config.deps.push('es6-collections'); + } + + /** + * Polyfill MutationObserver only for the browsers that do not support it. + */ + if (typeof MutationObserver === 'undefined') { + config.deps.push('MutationObserver'); + } + + /** + * Polyfill FormData object for old browsers that don't have full support for it. + */ + if (typeof FormData === 'undefined' || typeof FormData.prototype.get === 'undefined') { + config.deps.push('FormData'); + } +} diff --git a/app/code/Magento/Ui/view/base/web/js/lib/core/events.js b/app/code/Magento/Ui/view/base/web/js/lib/core/events.js index fdb11cd89f361..15965fba1ad2d 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/core/events.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/core/events.js @@ -6,8 +6,7 @@ /* global WeakMap, Map*/ define([ 'ko', - 'underscore', - 'es6-collections' + 'underscore' ], function (ko, _) { 'use strict'; diff --git a/app/code/Magento/Ui/view/base/web/js/lib/knockout/extender/bound-nodes.js b/app/code/Magento/Ui/view/base/web/js/lib/knockout/extender/bound-nodes.js index 6b3c437b90508..0b80a75bf0c18 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/knockout/extender/bound-nodes.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/knockout/extender/bound-nodes.js @@ -8,8 +8,7 @@ define([ 'ko', 'underscore', 'mage/utils/wrapper', - 'uiEvents', - 'es6-collections' + 'uiEvents' ], function (ko, _, wrapper, Events) { 'use strict'; diff --git a/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js b/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js index 826e8ec8c33b4..18e05b8daac68 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/registry/registry.js @@ -9,8 +9,7 @@ /* global WeakMap */ define([ 'jquery', - 'underscore', - 'es6-collections' + 'underscore' ], function ($, _) { 'use strict'; diff --git a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js index f8e752fb77af2..cb9f5b13de494 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/dom-observer.js @@ -5,7 +5,6 @@ define([ 'jquery', 'underscore', - 'MutationObserver', 'domReady!' ], function ($, _) { 'use strict'; diff --git a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js index 3ec0996543c7d..bc8e3095b5cd2 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/view/utils/raf.js @@ -4,9 +4,7 @@ */ /* global WeakMap */ -define([ - 'es6-collections' -], function () { +define([], function () { 'use strict'; var processMap = new WeakMap(), diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchUrlRewriteByRequestPathActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchUrlRewriteByRequestPathActionGroup.xml new file mode 100644 index 0000000000000..dfdc840e0dc9f --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AdminSearchUrlRewriteByRequestPathActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSearchUrlRewriteByRequestPathActionGroup" extends="AdminSearchAndSelectUrlRewriteInGridActionGroup"> + <annotations> + <description>EXTENDS: SearchAndSelectUrlRewrite. Removes 'clickOnRowSelectButton' and 'clickOnEditButton'.</description> + </annotations> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + + <remove keyForRemoval="clickOnRowSelectButton"/> + <remove keyForRemoval="clickOnEditButton"/> + <remove keyForRemoval="waitForEditPageToLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminRequestPathInUrlRewriteGrigActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminRequestPathInUrlRewriteGrigActionGroup.xml new file mode 100644 index 0000000000000..9de6045d70c03 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminRequestPathInUrlRewriteGrigActionGroup.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="AssertAdminRequestPathInUrlRewriteGrigActionGroup"> + <annotations> + <description>Assert the requested path is shown in the URL Rewrite grid.</description> + </annotations> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + + <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', requestPath)}}" + stepKey="seeValueInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup.xml new file mode 100644 index 0000000000000..8aac6ae54582a --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup.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="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup"> + <annotations> + <description>Assert the requested path is not shown in the URL Rewrite grid.</description> + </annotations> + <arguments> + <argument name="requestPath" type="string"/> + </arguments> + + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', requestPath)}}" + stepKey="valueIsNotShownInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminStoreValueIsSetForUrlRewriteActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminStoreValueIsSetForUrlRewriteActionGroup.xml new file mode 100644 index 0000000000000..dea0b8d19b428 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertAdminStoreValueIsSetForUrlRewriteActionGroup.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="AssertAdminStoreValueIsSetForUrlRewriteActionGroup"> + <annotations> + <description>Verifies that the proper Store Value is used for URL Rewrite.</description> + </annotations> + <arguments> + <argument name="storeValue" type="string"/> + </arguments> + + <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" + userInput="{{storeValue}}" stepKey="seeStoreValueForCategoryId"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontUrlRewriteSuccessOutsideRedirectActionGroup.xml b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontUrlRewriteSuccessOutsideRedirectActionGroup.xml new file mode 100644 index 0000000000000..757c15775dd66 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/ActionGroup/AssertStorefrontUrlRewriteSuccessOutsideRedirectActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontUrlRewriteSuccessOutsideRedirectActionGroup"> + <annotations> + <description>Assert redirect to proper URL on the Storefront.</description> + </annotations> + <arguments> + <argument name="target_path" type="string"/> + </arguments> + + <seeInCurrentUrl url="{{target_path}}" stepKey="seePropertUrlRewrite"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml index 036d35d9c3258..d890cde5ecf9d 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml @@ -63,26 +63,25 @@ </actionGroup> <!-- Create simple product with categories created in create data --> - <amOnPage url="{{AdminProductIndexPage.url}}" stepKey="openProductCatalogPage"/> - <waitForPageLoad stepKey="waitForProductCatalogPage"/> + <actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="openProductsGrid"/> <actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct"> <argument name="product" value="$$createProduct$$"/> </actionGroup> - <click selector="{{AdminProductGridFilterSection.nthRow('1')}}" stepKey="clickFirstRowOfCreatedSimpleProduct"/> - <waitForPageLoad stepKey="waitUntilProductIsOpened"/> - <click selector="{{AdminProductFormSection.categoriesDropdown}}" stepKey="clickCategoriesDropDown"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$rootCategory.name$$" stepKey="fillSearchForInitialCategory"/> - <waitForPageLoad stepKey="waitForCategory1"/> - <click selector="{{AdminProductFormSection.selectCategory($$rootCategory.name$$)}}" stepKey="unselectInitialCategory"/> - <fillField selector="{{AdminProductFormSection.searchCategory}}" userInput="$$category.name$$" stepKey="fillSearchCategory"/> - <waitForPageLoad stepKey="waitForCategory2"/> - <click selector="{{AdminProductFormSection.selectCategory($$category.name$$)}}" stepKey="clickOnCategory"/> - <click selector="{{AdminProductFormSection.done}}" stepKey="clickOnDoneAdvancedCategorySelect"/> - <scrollToTopOfPage stepKey="scrollToTopOfAdminProductFormSection"/> - <click selector="{{AdminProductFormSection.save}}" stepKey="clickSaveButton"/> - <waitForPageLoad stepKey="waitForSimpleProductSaved"/> + <actionGroup ref="OpenProductForEditByClickingRowXColumnYInProductGridActionGroup" stepKey="openProduct"/> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="unselectInitialCategory"> + <argument name="categoryName" value="$$rootCategory.name$$"/> + </actionGroup> + <actionGroup ref="AdminSubmitCategoriesPopupActionGroup" stepKey="pressDoneButton"/> + <actionGroup ref="SetCategoryByNameActionGroup" stepKey="setNewCategory"> + <argument name="categoryName" value="$$category.name$$"/> + </actionGroup> + <actionGroup ref="AdminSubmitCategoriesPopupActionGroup" stepKey="clickOnDoneButton"/> + <actionGroup ref="SaveProductFormNoSuccessCheckActionGroup" stepKey="saveProduct"/> + <!-- Verify customer see success message --> - <see selector="{{AdminProductFormSection.successMessage}}" userInput="You saved the product." stepKey="seeAssertSimpleProductSaveSuccessMessage"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="seeAssertSimpleProductSaveSuccessMessage"> + <argument name="message" value="You saved the product."/> + </actionGroup> <!-- Grab category Id --> <actionGroup ref="OpenCategoryFromCategoryTreeActionGroup" stepKey="grabCategoryId"> @@ -95,8 +94,13 @@ <argument name="redirectType" value="No"/> <argument name="targetPath" value="catalog/category/view/id/{$categoryId}"/> </actionGroup> - <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{customStoreGroup.name}}" stepKey="seeStoreValueForCategoryId"/> - <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{customStoreEN.name}}" stepKey="seeStoreViewValueForCategoryId"/> + + <actionGroup ref="AssertAdminStoreValueIsSetForUrlRewriteActionGroup" stepKey="seeStoreValueForCategoryId"> + <argument name="storeValue" value="{{customStoreGroup.name}}"/> + </actionGroup> + <actionGroup ref="AssertAdminStoreValueIsSetForUrlRewriteActionGroup" stepKey="seeStoreViewValueForCategoryId"> + <argument name="storeValue" value="{{customStoreEN.name}}"/> + </actionGroup> <!-- Grab product Id --> <actionGroup ref="FilterAndSelectProductActionGroup" stepKey="grabProductId"> @@ -109,7 +113,12 @@ <argument name="redirectType" value="No"/> <argument name="targetPath" value="catalog/product/view/id/{$productId}"/> </actionGroup> - <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{customStore.name}}" stepKey="seeStoreValueForProductId"/> - <see selector="{{AdminUrlRewriteIndexSection.gridCellByColumnRowNumber('1', 'Store View')}}" userInput="{{storeViewData.name}}" stepKey="seeStoreViewValueForProductId"/> + + <actionGroup ref="AssertAdminStoreValueIsSetForUrlRewriteActionGroup" stepKey="seeStoreValueForProductId"> + <argument name="storeValue" value="{{customStore.name}}"/> + </actionGroup> + <actionGroup ref="AssertAdminStoreValueIsSetForUrlRewriteActionGroup" stepKey="seeStoreViewValueForProductId"> + <argument name="storeValue" value="{{storeViewData.name}}"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml index 9b739b157cddc..16916426167b8 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUpdateCustomURLRewritesPermanentTest.xml @@ -51,8 +51,11 @@ </actionGroup> <!--AssertUrlRewriteSuccessOutsideRedirect--> - <amOnPage url="{{StorefrontHomePage.url}}{{customPermanentUrlRewrite.request_path}}" stepKey="amOnStorefrontPage"/> - <waitForPageLoad stepKey="waitForPageLoad"/> - <seeInCurrentUrl url="{{customPermanentUrlRewrite.target_path}}" stepKey="seeAssertUrlRewrite"/> + <actionGroup ref="NavigateToStorefrontForCreatedPageActionGroup" stepKey="navigateToTheStoreFront"> + <argument name="page" value="{{customPermanentUrlRewrite.request_path}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontUrlRewriteSuccessOutsideRedirectActionGroup" stepKey="seeAssertUrlRewrite"> + <argument name="target_path" value="{{customPermanentUrlRewrite.target_path}}"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index cce0cd11e0199..e98d1b3f526c5 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -39,47 +39,60 @@ <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> </after> <!-- Steps --> <!-- 1. Log in to Admin --> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> - + <actionGroup ref="AdminSearchUrlRewriteByRequestPathActionGroup" stepKey="searchingUrlRewrite"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeValueOne"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeValueTwo"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeValueThree"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeValueFour"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> <!-- 3. Edit Category 1 for DEFAULT Store View: --> <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> <argument name="Store" value="_defaultStore.name"/> <argument name="CatName" value="$$simpleSubCategory1.name$$"/> </actionGroup> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> - <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> - <!-- 4. Save Category 1 --> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> - + <!-- 4. Change URL key for category and save changes --> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeFirstCategoryUrlKey"> + <argument name="value" value="$simpleSubCategory1.custom_attributes[url_key]$new"/> + </actionGroup> <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> + <actionGroup ref="AdminSearchUrlRewriteByRequestPathActionGroup" stepKey="searchingUrlRewriteSecondTime"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValueOne"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValueTwo"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValuethree"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValueFour"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValueFive"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$new/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValue1Six"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeInListValueSeven"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml index 1876b001eb5bc..de44200994873 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewTest.xml @@ -28,9 +28,13 @@ </before> <remove keyForRemoval="switchStoreView"/> <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValueFour"> <argument name="Category" value="$$simpleSubCategory1$$"/> </actionGroup> - <remove keyForRemoval="uncheckRedirect2"/> + <remove keyForRemoval="changeFirstCategoryUrlKey"/> + <!-- 4. Change URL key for category and save changes --> + <actionGroup ref="ChangeSeoUrlKeyActionGroup" stepKey="changeCategoryUrlKey" after="goToCategoryPage"> + <argument name="value" value="$simpleSubCategory1.custom_attributes[url_key]$new"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml index 14f7c9fb7cbe3..bc2005b32bae2 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOffTest.xml @@ -28,9 +28,13 @@ </before> <remove keyForRemoval="switchStoreView"/> <!-- 3. Edit Category 1 for All store view: --> - <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="seeValue4"> + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="goToCategoryPage" after="doNotSeeValueFour"> <argument name="Category" value="$$simpleSubCategory1$$"/> </actionGroup> <remove keyForRemoval="uncheckRedirect2"/> + <!-- 4. Change URL key for category and save changes --> + <actionGroup ref="ChangeSeoUrlKeyActionGroup" stepKey="changeCategoryUrlKey" after="goToCategoryPage"> + <argument name="value" value="$simpleSubCategory1.custom_attributes[url_key]$new"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml index 639cd2c57f7d1..885e09f775c36 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOffTest.xml @@ -47,7 +47,7 @@ <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> - <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache2"/> @@ -55,59 +55,96 @@ <!-- Steps --> <!-- 1. Log in to Admin --> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + <actionGroup ref="AdminSearchUrlRewriteByRequestPathActionGroup" stepKey="searchingUrlRewrite"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeValueOne"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueTwo"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueThree"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueFour"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> <!-- 3. Edit Category 1 for DEFAULT Store View: --> <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="switchStoreView"> <argument name="Store" value="_defaultStore.name"/> <argument name="CatName" value="$$simpleSubCategory1.name$$"/> </actionGroup> - <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> - <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> - <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> - <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> - <!-- 4. Save Category 1 --> - <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> - <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> - <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters1"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName1"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters1"/> - <waitForPageLoad stepKey="waitForPageToLoad1"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> - - <amOnPage url="/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + <!-- 4. Change URL key for category and save changes --> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeCategoryUrlKey"> + <argument name="value" value="$simpleSubCategory1.custom_attributes[url_key]$new"/> + </actionGroup> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage2"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName2"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage3"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName3"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage4"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName4"/> + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <actionGroup ref="AdminSearchUrlRewriteByRequestPathActionGroup" stepKey="searchingUrlRewriteOneMoreTime"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeValueInGrid"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueTwoInGrid"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueThreeInGrid"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueFourInGrid"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueFiveInGrid"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueSixInGrid"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="doNotSeeValueSevenInGrid"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage5"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName5"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage6"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName6"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage7"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName7"/> + <!-- 6. Assert Redirects work and Product is present on StoreFront--> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPageSecondAttempt"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPageThirdAttempt"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPageFourthAttempt"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPageFifthAttempt"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$new/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPageSixthAttempt"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPageSeventhAttempt"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml index 1d460b9b668a0..99be6028a3908 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest/AdminUrlRewritesForProductsWithConfigurationTurnedOffTest.xml @@ -41,17 +41,26 @@ <!--Flush cache--> <magentoCLI command="cache:flush" stepKey="cleanCache2"/> </after> + <!-- 1. Log in to Admin --> <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> - <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> - <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openUrlRewriteGridFilters"/> - <fillField selector="{{AdminDataGridHeaderSection.filterFieldInput('request_path')}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> - <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <seeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeProducturl"/> - <dontSeeElement selector="{{AdminUrlRewriteIndexSection.gridCellByColumnValue('Request Path', $simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeCategoryProducturlKey"/> - <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> - <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + <actionGroup ref="AdminSearchUrlRewriteByRequestPathActionGroup" stepKey="searchingUrlRewrite"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathInUrlRewriteGrigActionGroup" stepKey="seeProductUrlInGrid"> + <argument name="requestPath" value="$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + <actionGroup ref="AssertAdminRequestPathIsNotFoundInUrlRewriteGrigActionGroup" stepKey="categoryProductUrlIsNotShown"> + <argument name="requestPath" value="$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> + + <!-- 3. Assert the Redirect works and Product is present on StoreFront--> + <actionGroup ref="AssertStorefrontProductRedirectActionGroup" stepKey="verifyProductInStoreFrontPage"> + <argument name="productName" value="$$createSimpleProduct.name$$"/> + <argument name="productSku" value="$$createSimpleProduct.sku$$"/> + <argument name="productRequestPath" value="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html"/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml index 501e9520c6367..0943b33e8a711 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminUpdateUserTest.xml @@ -18,8 +18,9 @@ <description value="Change full access role for admin user to custom one with restricted permission (Sales)"/> <group value="user"/> <group value="mtf_migrated"/> - <!-- skip due to MQE-1964 --> - <group value="skip"/> + <skip> + <issueId value="MQE-1964"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Wishlist/Model/Wishlist.php b/app/code/Magento/Wishlist/Model/Wishlist.php index 9b7ff5177afae..cb1a7d956570b 100644 --- a/app/code/Magento/Wishlist/Model/Wishlist.php +++ b/app/code/Magento/Wishlist/Model/Wishlist.php @@ -181,6 +181,7 @@ class Wishlist extends AbstractModel implements IdentityInterface * @param Json|null $serializer * @param StockRegistryInterface|null $stockRegistry * @param ScopeConfigInterface|null $scopeConfig + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -226,6 +227,7 @@ public function __construct( * * @param int $customerId * @param bool $create Create wishlist if don't exists + * * @return $this */ public function loadByCustomerId($customerId, $create = false) @@ -274,6 +276,7 @@ public function generateSharingCode() * Load by sharing code * * @param string $code + * * @return $this */ public function loadByCode($code) @@ -370,6 +373,7 @@ protected function _addCatalogProduct(Product $product, $qty = 1, $forciblySetQt * Retrieve wishlist item collection * * @return \Magento\Wishlist\Model\ResourceModel\Item\Collection + * * @throws NoSuchEntityException */ public function getItemCollection() @@ -389,6 +393,7 @@ public function getItemCollection() * Retrieve wishlist item collection * * @param int $itemId + * * @return false|Item */ public function getItem($itemId) @@ -403,7 +408,9 @@ public function getItem($itemId) * Adding item to wishlist * * @param Item $item + * * @return $this + * * @throws Exception */ public function addItem(Item $item) @@ -424,9 +431,12 @@ public function addItem(Item $item) * @param int|Product $product * @param DataObject|array|string|null $buyRequest * @param bool $forciblySetQty + * * @return Item|string + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * * @throws LocalizedException * @throws InvalidArgumentException */ @@ -529,7 +539,9 @@ public function addNewItem($product, $buyRequest = null, $forciblySetQty = false * Set customer id * * @param int $customerId + * * @return $this + * * @throws LocalizedException */ public function setCustomerId($customerId) @@ -541,6 +553,7 @@ public function setCustomerId($customerId) * Retrieve customer id * * @return int + * * @throws LocalizedException */ public function getCustomerId() @@ -552,6 +565,7 @@ public function getCustomerId() * Retrieve data for save * * @return array + * * @throws LocalizedException */ public function getDataForSave() @@ -567,6 +581,7 @@ public function getDataForSave() * Retrieve shared store ids for current website or all stores if $current is false * * @return array + * * @throws NoSuchEntityException */ public function getSharedStoreIds() @@ -590,6 +605,7 @@ public function getSharedStoreIds() * Set shared store ids * * @param array $storeIds + * * @return $this */ public function setSharedStoreIds($storeIds) @@ -602,6 +618,7 @@ public function setSharedStoreIds($storeIds) * Retrieve wishlist store object * * @return \Magento\Store\Model\Store + * * @throws NoSuchEntityException */ public function getStore() @@ -616,6 +633,7 @@ public function getStore() * Set wishlist store * * @param Store $store + * * @return $this */ public function setStore($store) @@ -653,6 +671,7 @@ public function isSalable() * Retrieve if product has stock or config is set for showing out of stock products * * @param int $productId + * * @return bool */ private function isInStock($productId) @@ -671,7 +690,9 @@ private function isInStock($productId) * Check customer is owner this wishlist * * @param int $customerId + * * @return bool + * * @throws LocalizedException */ public function isOwner($customerId) @@ -696,10 +717,13 @@ public function isOwner($customerId) * @param int|Item $itemId * @param DataObject $buyRequest * @param null|array|DataObject $params + * * @return $this + * * @throws LocalizedException * * @see \Magento\Catalog\Helper\Product::addParamsToBuyRequest() + * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -748,10 +772,11 @@ public function updateItem($itemId, $buyRequest, $params = null) throw new LocalizedException(__($resultItem)); } + if ($resultItem->getDescription() != $item->getDescription()) { + $resultItem->setDescription($item->getDescription())->save(); + } + if ($resultItem->getId() != $itemId) { - if ($resultItem->getDescription() != $item->getDescription()) { - $resultItem->setDescription($item->getDescription())->save(); - } $item->isDeleted(true); $this->setDataChanges(true); } else { diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontWishListInvalidEmailsMessageActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontWishListInvalidEmailsMessageActionGroup.xml new file mode 100644 index 0000000000000..bdb5e702132dc --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/AssertStorefrontWishListInvalidEmailsMessageActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertStorefrontWishListInvalidEmailsMessageActionGroup"> + <arguments> + <argument name="message" type="string"/> + </arguments> + <see userInput="{{message}}" selector="{{StorefrontCustomerWishlistShareSection.errorEmailMessage}}" stepKey="successMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.xml index 1f7ac9fc85f50..57404f54a64b2 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontCustomerShareWishlistActionGroup.xml @@ -8,7 +8,8 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontCustomerShareWishlistActionGroup"> + <actionGroup name="StorefrontCustomerShareWishlistActionGroup" deprecated="Use StorefrontShareCustomerWishlistActionGroup"> + <!-- Deprecated due to Hardcoded WishList Data Using. 18-19 lines --> <annotations> <description>Shares the Wish List from the Storefront Wish List page. PLEASE NOTE: The details for sharing are Hardcoded using 'Wishlist'.</description> </annotations> diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontShareCustomerWishlistActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontShareCustomerWishlistActionGroup.xml new file mode 100644 index 0000000000000..6cabeeac1242f --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontShareCustomerWishlistActionGroup.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="StorefrontShareCustomerWishlistActionGroup"> + <arguments> + <argument name="email" type="string"/> + <argument name="message" type="string"/> + </arguments> + + <click selector="{{StorefrontCustomerWishlistProductSection.productShareWishList}}" stepKey="clickMyWishListButton"/> + <fillField userInput="{{email}}" selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistEmail}}" stepKey="fillEmailsForShare"/> + <fillField userInput="{{message}}" selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistTextMessage}}" stepKey="fillShareMessage"/> + <click selector="{{StorefrontCustomerWishlistShareSection.ProductShareWishlistButton}}" stepKey="sendWishlist"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml index 4a25a8d449dd3..63b864f682455 100755 --- a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml @@ -18,4 +18,15 @@ <data key="min_email_text_length_limit">1</data> <data key="max_email_text_length_limit">10000</data> </entity> + <entity name="notValidEmails" type="wishlist"> + <data key="id">null</data> + <var key="product" entityType="product" entityKey="id"/> + <var key="customer_email" entityType="customer" entityKey="email"/> + <var key="customer_password" entityType="customer" entityKey="password"/> + <data key="shareInfo_emails">JohnDoe123456789@,JohnDoe987654321example.com,JohnDoe123456abc@@example.com</data> + <data key="shareInfo_message">Sharing message.</data> + <data key="default_email_text_length_limit">255</data> + <data key="min_email_text_length_limit">1</data> + <data key="max_email_text_length_limit">10000</data> + </entity> </entities> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml index 76b99ba56a327..3f16133be96a9 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml @@ -12,5 +12,6 @@ <element name="ProductShareWishlistEmail" type="input" selector="#email_address"/> <element name="ProductShareWishlistTextMessage" type="input" selector="#message"/> <element name="ProductShareWishlistButton" type="button" selector=".action.submit.primary" timeout="30"/> + <element name="errorEmailMessage" type="input" selector="#email_address-error"/> </section> </sections> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml index 329978462c107..c6b6dc6886f96 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistEntityTest.xml @@ -48,6 +48,12 @@ <argument name="productVar" value="$createProduct$"/> </actionGroup> - <actionGroup ref="StorefrontCustomerShareWishlistActionGroup" stepKey="shareWishlist"/> + <actionGroup ref="StorefrontShareCustomerWishlistActionGroup" stepKey="shareWishlist"> + <argument name="email" value="{{Wishlist.shareInfo_emails}}"/> + <argument name="message" value="{{Wishlist.shareInfo_message}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCustomerMessagesActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="Your wish list has been shared."/> + </actionGroup> </test> </tests> diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistWithNotValidEmailAddressTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistWithNotValidEmailAddressTest.xml new file mode 100644 index 0000000000000..20881fa64f8f8 --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistWithNotValidEmailAddressTest.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="StorefrontShareWishlistWithNotValidEmailAddressTest"> + <annotations> + <features value="Wishlist"/> + <stories value="Customer Wishlist"/> + <title value="Customer is not able to share wishlist with invalid email addresses"/> + <description value="Customer is not able to share wishlist with invalid email addresses"/> + <group value="wishlist"/> + </annotations> + <before> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$createCustomer$"/> + </actionGroup> + <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openProductFromCategory"> + <argument name="category" value="$createCategory$"/> + <argument name="product" value="$createProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addToWishlistProduct"> + <argument name="productVar" value="$createProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontShareCustomerWishlistActionGroup" stepKey="shareWishList"> + <argument name="email" value="{{notValidEmails.shareInfo_emails}}"/> + <argument name="message" value="{{notValidEmails.shareInfo_message}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontWishListInvalidEmailsMessageActionGroup" stepKey="assertErrorMessage"> + <argument name="message" value="Please enter valid email addresses, separated by commas. For example, johndoe@domain.com, johnsmith@domain.com."/> + </actionGroup> + + </test> +</tests> diff --git a/composer.json b/composer.json index d487ad5975040..25e6c6c5435bf 100644 --- a/composer.json +++ b/composer.json @@ -88,7 +88,7 @@ "friendsofphp/php-cs-fixer": "~2.16.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "*", - "magento/magento2-functional-testing-framework": "dev-3.0.0-RC3", + "magento/magento2-functional-testing-framework": "3.0.0-RC4", "pdepend/pdepend": "~2.7.1", "phpcompatibility/php-compatibility": "^9.3", "phpmd/phpmd": "^2.8.0", @@ -164,6 +164,7 @@ "magento/module-encryption-key": "*", "magento/module-fedex": "*", "magento/module-gift-message": "*", + "magento/module-gift-message-graph-ql": "*", "magento/module-google-adwords": "*", "magento/module-google-analytics": "*", "magento/module-google-optimizer": "*", @@ -214,6 +215,7 @@ "magento/module-mysql-mq": "*", "magento/module-new-relic-reporting": "*", "magento/module-newsletter": "*", + "magento/module-newsletter-graph-ql": "*", "magento/module-offline-payments": "*", "magento/module-offline-shipping": "*", "magento/module-page-cache": "*", diff --git a/composer.lock b/composer.lock index 6a47e7e44ab69..39282cb149dc6 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": "e86af25d9a4a1942c437cca58f9f1efb", + "content-hash": "4abc523fda743ab847f07f9905bb2731", "packages": [ { "name": "colinmollenhour/cache-backend-file", @@ -206,16 +206,6 @@ "ssl", "tls" ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], "time": "2020-04-08T08:27:21+00:00" }, { @@ -297,16 +287,6 @@ "dependency", "package" ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], "time": "2020-05-06T08:28:10+00:00" }, { @@ -472,12 +452,6 @@ "Xdebug", "performance" ], - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - } - ], "time": "2020-03-01T12:26:26+00:00" }, { @@ -1331,12 +1305,6 @@ "BSD-3-Clause" ], "description": "Replace zendframework and zfcampus packages with their Laminas Project equivalents.", - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], "time": "2020-05-20T13:45:39+00:00" }, { @@ -2294,12 +2262,6 @@ "laminas", "mail" ], - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], "time": "2020-04-21T16:42:19+00:00" }, { @@ -3292,12 +3254,6 @@ "laminas", "zf" ], - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], "time": "2020-05-20T16:45:56+00:00" }, { @@ -3537,16 +3493,6 @@ "logging", "psr-3" ], - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" - } - ], "time": "2020-05-22T07:31:27+00:00" }, { @@ -3962,20 +3908,6 @@ "x.509", "x509" ], - "funding": [ - { - "url": "https://github.com/terrafrost", - "type": "github" - }, - { - "url": "https://www.patreon.com/phpseclib", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", - "type": "tidelift" - } - ], "time": "2020-04-04T23:17:33+00:00" }, { @@ -4339,16 +4271,6 @@ "parser", "validator" ], - "funding": [ - { - "url": "https://github.com/Seldaek", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", - "type": "tidelift" - } - ], "time": "2020-04-30T19:05:18+00:00" }, { @@ -4469,43 +4391,29 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-03-30T11:41:10+00:00" }, { "name": "symfony/css-selector", - "version": "v5.0.8", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "5f8d5271303dad260692ba73dfa21777d38e124e" + "reference": "e544e24472d4c97b2d11ade7caacd446727c6bf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/5f8d5271303dad260692ba73dfa21777d38e124e", - "reference": "5f8d5271303dad260692ba73dfa21777d38e124e", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/e544e24472d4c97b2d11ade7caacd446727c6bf9", + "reference": "e544e24472d4c97b2d11ade7caacd446727c6bf9", "shasum": "" }, "require": { - "php": "^7.2.5" + "php": ">=7.2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -4536,21 +4444,7 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2020-05-20T17:43:50+00:00" }, { "name": "symfony/event-dispatcher", @@ -4620,20 +4514,6 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-03-27T16:54:36+00:00" }, { @@ -4696,26 +4576,26 @@ }, { "name": "symfony/filesystem", - "version": "v5.0.8", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7cd0dafc4353a0f62e307df90b48466379c8cc91" + "reference": "6e4320f06d5f2cce0d96530162491f4465179157" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7cd0dafc4353a0f62e307df90b48466379c8cc91", - "reference": "7cd0dafc4353a0f62e307df90b48466379c8cc91", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/6e4320f06d5f2cce0d96530162491f4465179157", + "reference": "6e4320f06d5f2cce0d96530162491f4465179157", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -4742,43 +4622,29 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-04-12T14:40:17+00:00" + "time": "2020-05-30T20:35:19+00:00" }, { "name": "symfony/finder", - "version": "v5.0.8", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d" + "reference": "4298870062bfc667cb78d2b379be4bf5dec5f187" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/600a52c29afc0d1caa74acbec8d3095ca7e9910d", - "reference": "600a52c29afc0d1caa74acbec8d3095ca7e9910d", + "url": "https://api.github.com/repos/symfony/finder/zipball/4298870062bfc667cb78d2b379be4bf5dec5f187", + "reference": "4298870062bfc667cb78d2b379be4bf5dec5f187", "shasum": "" }, "require": { - "php": "^7.2.5" + "php": ">=7.2.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -4805,21 +4671,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-03-27T16:56:45+00:00" + "time": "2020-05-20T17:43:50+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4877,20 +4729,6 @@ "polyfill", "portable" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-05-12T16:14:59+00:00" }, { @@ -4953,20 +4791,6 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-05-12T16:47:27+00:00" }, { @@ -5026,20 +4850,6 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-05-12T16:47:27+00:00" }, { @@ -5095,20 +4905,6 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-05-12T16:47:27+00:00" }, { @@ -5167,20 +4963,6 @@ "portable", "shim" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-05-12T16:47:27+00:00" }, { @@ -5230,20 +5012,6 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-04-15T15:56:18+00:00" }, { @@ -5720,16 +5488,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.138.7", + "version": "3.140.2", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "6b9f3fcea4dfa6092c628c790ca6d369a75453b7" + "reference": "7e37960c1103ee211932be51b2282b41c948a5f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6b9f3fcea4dfa6092c628c790ca6d369a75453b7", - "reference": "6b9f3fcea4dfa6092c628c790ca6d369a75453b7", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/7e37960c1103ee211932be51b2282b41c948a5f0", + "reference": "7e37960c1103ee211932be51b2282b41c948a5f0", "shasum": "" }, "require": { @@ -5800,7 +5568,7 @@ "s3", "sdk" ], - "time": "2020-05-22T18:11:09+00:00" + "time": "2020-06-05T18:12:25+00:00" }, { "name": "beberlei/assert", @@ -6018,16 +5786,16 @@ }, { "name": "codeception/codeception", - "version": "4.1.4", + "version": "4.1.5", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "55d8d1d882fa0777e47de17b04c29b3c50fe29e7" + "reference": "24f2345329b1059f1208f65581fc632a4a6e5a55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/55d8d1d882fa0777e47de17b04c29b3c50fe29e7", - "reference": "55d8d1d882fa0777e47de17b04c29b3c50fe29e7", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/24f2345329b1059f1208f65581fc632a4a6e5a55", + "reference": "24f2345329b1059f1208f65581fc632a4a6e5a55", "shasum": "" }, "require": { @@ -6099,13 +5867,7 @@ "functional testing", "unit testing" ], - "funding": [ - { - "url": "https://opencollective.com/codeception", - "type": "open_collective" - } - ], - "time": "2020-03-23T17:07:20+00:00" + "time": "2020-05-24T13:58:47+00:00" }, { "name": "codeception/lib-asserts", @@ -6249,16 +6011,16 @@ }, { "name": "codeception/module-webdriver", - "version": "1.0.8", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/Codeception/module-webdriver.git", - "reference": "da55466876d9e73c09917f495b923395b1cdf92a" + "reference": "09c167817393090ce3dbce96027d94656b1963ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/module-webdriver/zipball/da55466876d9e73c09917f495b923395b1cdf92a", - "reference": "da55466876d9e73c09917f495b923395b1cdf92a", + "url": "https://api.github.com/repos/Codeception/module-webdriver/zipball/09c167817393090ce3dbce96027d94656b1963ce", + "reference": "09c167817393090ce3dbce96027d94656b1963ce", "shasum": "" }, "require": { @@ -6300,7 +6062,7 @@ "browser-testing", "codeception" ], - "time": "2020-04-29T13:45:52+00:00" + "time": "2020-05-31T08:47:24+00:00" }, { "name": "codeception/phpunit-wrapper", @@ -6530,22 +6292,22 @@ }, { "name": "doctrine/annotations", - "version": "1.10.2", + "version": "1.10.3", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "b9d758e831c70751155c698c2f7df4665314a1cb" + "reference": "5db60a4969eba0e0c197a19c077780aadbc43c5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/b9d758e831c70751155c698c2f7df4665314a1cb", - "reference": "b9d758e831c70751155c698c2f7df4665314a1cb", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5db60a4969eba0e0c197a19c077780aadbc43c5d", + "reference": "5db60a4969eba0e0c197a19c077780aadbc43c5d", "shasum": "" }, "require": { "doctrine/lexer": "1.*", "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/cache": "1.*", @@ -6595,24 +6357,24 @@ "docblock", "parser" ], - "time": "2020-04-20T09:18:32+00:00" + "time": "2020-05-25T17:24:27+00:00" }, { "name": "doctrine/cache", - "version": "1.10.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62" + "reference": "35a4a70cd94e09e2259dfae7488afc6b474ecbd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/382e7f4db9a12dc6c19431743a2b096041bcdd62", - "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62", + "url": "https://api.github.com/repos/doctrine/cache/zipball/35a4a70cd94e09e2259dfae7488afc6b474ecbd3", + "reference": "35a4a70cd94e09e2259dfae7488afc6b474ecbd3", "shasum": "" }, "require": { - "php": "~7.1" + "php": "~7.1 || ^8.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" @@ -6677,7 +6439,7 @@ "redis", "xcache" ], - "time": "2019-11-29T15:36:20+00:00" + "time": "2020-05-27T16:24:54+00:00" }, { "name": "doctrine/inflector", @@ -6748,20 +6510,20 @@ }, { "name": "doctrine/instantiator", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1" + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1", - "reference": "ae466f726242e637cebdd526a7d991b9433bacf1", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea", + "reference": "f350df0268e904597e3bd9c4685c53e0e333feea", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^6.0", @@ -6800,24 +6562,24 @@ "constructor", "instantiate" ], - "time": "2019-10-21T16:45:58+00:00" + "time": "2020-05-29T17:27:14+00:00" }, { "name": "doctrine/lexer", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6" + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", - "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042", + "reference": "e864bbf5904cb8f5bb334f99209b48018522f042", "shasum": "" }, "require": { - "php": "^7.2" + "php": "^7.2 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^6.0", @@ -6862,7 +6624,7 @@ "parser", "php" ], - "time": "2019-10-30T14:39:59+00:00" + "time": "2020-05-25T17:44:05+00:00" }, { "name": "friendsofphp/php-cs-fixer", @@ -6953,12 +6715,6 @@ } ], "description": "A tool to automatically fix PHP code style", - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], "time": "2020-04-15T18:51:10+00:00" }, { @@ -7217,12 +6973,6 @@ "sftp", "storage" ], - "funding": [ - { - "url": "https://offset.earth/frankdejonge", - "type": "other" - } - ], "time": "2020-05-18T15:13:39+00:00" }, { @@ -7333,16 +7083,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "dev-3.0.0-RC3", + "version": "3.0.0-RC4", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "aea30ae1df2fe6618478ba8813864c204561fde3" + "reference": "34781ccc7385993b1e5bc9182e6ddddde7f2769f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/aea30ae1df2fe6618478ba8813864c204561fde3", - "reference": "aea30ae1df2fe6618478ba8813864c204561fde3", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/34781ccc7385993b1e5bc9182e6ddddde7f2769f", + "reference": "34781ccc7385993b1e5bc9182e6ddddde7f2769f", "shasum": "" }, "require": { @@ -7369,7 +7119,8 @@ "symfony/finder": "^5.0", "symfony/mime": "^5.0", "symfony/process": "^4.4", - "vlucas/phpdotenv": "^2.4" + "vlucas/phpdotenv": "^2.4", + "weew/helpers-array": "^1.3" }, "replace": { "facebook/webdriver": "^1.7.1" @@ -7417,7 +7168,7 @@ "magento", "testing" ], - "time": "2020-05-22T19:17:05+00:00" + "time": "2020-06-08T18:17:54+00:00" }, { "name": "mikey179/vfsstream", @@ -8425,20 +8176,6 @@ "MIT" ], "description": "PHPStan - PHP Static Analysis Tool", - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://www.patreon.com/phpstan", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" - } - ], "time": "2020-05-05T12:55:44+00:00" }, { @@ -8553,12 +8290,6 @@ "filesystem", "iterator" ], - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-04-18T05:02:12+00:00" }, { @@ -8707,12 +8438,6 @@ "keywords": [ "timer" ], - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-04-20T06:00:37+00:00" }, { @@ -8762,12 +8487,6 @@ "keywords": [ "tokenizer" ], - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-05-06T09:56:31+00:00" }, { @@ -8856,16 +8575,6 @@ "testing", "xunit" ], - "funding": [ - { - "url": "https://phpunit.de/donate.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-05-22T13:54:05+00:00" }, { @@ -9006,12 +8715,6 @@ ], "description": "Collection of value objects that represent the PHP code units", "homepage": "https://github.com/sebastianbergmann/code-unit", - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-04-30T05:58:10+00:00" }, { @@ -9177,12 +8880,6 @@ "unidiff", "unified diff" ], - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-05-08T05:01:12+00:00" }, { @@ -9236,12 +8933,6 @@ "environment", "hhvm" ], - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2020-04-14T13:36:52+00:00" }, { @@ -9925,20 +9616,6 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-04-15T15:59:10+00:00" }, { @@ -10012,49 +9689,87 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, + "time": "2020-04-28T17:58:55+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "dd99cb3a0aff6cadd2a8d7d7ed72c2161e218337" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/dd99cb3a0aff6cadd2a8d7d7ed72c2161e218337", + "reference": "dd99cb3a0aff6cadd2a8d7d7ed72c2161e218337", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://github.com/fabpot", - "type": "github" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "time": "2020-04-28T17:58:55+00:00" + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "time": "2020-05-27T08:34:37+00:00" }, { "name": "symfony/http-foundation", - "version": "v5.0.8", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e47fdf8b24edc12022ba52923150ec6484d7f57d" + "reference": "e0d853bddc2b2cfb0d67b0b4496c03fffe1d37fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e47fdf8b24edc12022ba52923150ec6484d7f57d", - "reference": "e47fdf8b24edc12022ba52923150ec6484d7f57d", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e0d853bddc2b2cfb0d67b0b4496c03fffe1d37fa", + "reference": "e0d853bddc2b2cfb0d67b0b4496c03fffe1d37fa", "shasum": "" }, "require": { - "php": "^7.2.5", - "symfony/mime": "^4.4|^5.0", - "symfony/polyfill-mbstring": "~1.1" + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.15" }, "require-dev": { "predis/predis": "~1.0", - "symfony/expression-language": "^4.4|^5.0" + "symfony/cache": "^4.4|^5.0", + "symfony/expression-language": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -10081,40 +9796,27 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-04-18T20:50:06+00:00" + "time": "2020-05-24T12:18:07+00:00" }, { "name": "symfony/mime", - "version": "v5.0.8", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "5d6c81c39225a750f3f43bee15f03093fb9aaa0b" + "reference": "56261f89385f9d13cf843a5101ac72131190bc91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/5d6c81c39225a750f3f43bee15f03093fb9aaa0b", - "reference": "5d6c81c39225a750f3f43bee15f03093fb9aaa0b", + "url": "https://api.github.com/repos/symfony/mime/zipball/56261f89385f9d13cf843a5101ac72131190bc91", + "reference": "56261f89385f9d13cf843a5101ac72131190bc91", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.15" }, "conflict": { "symfony/mailer": "<4.4" @@ -10126,7 +9828,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -10157,21 +9859,7 @@ "mime", "mime-type" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-04-17T03:29:44+00:00" + "time": "2020-05-25T12:33:44+00:00" }, { "name": "symfony/options-resolver", @@ -10225,20 +9913,6 @@ "configuration", "options" ], - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-04-06T10:40:56+00:00" }, { @@ -10298,20 +9972,68 @@ "portable", "shim" ], - "funding": [ + "time": "2020-05-12T16:47:27+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/5e30b2799bc1ad68f7feb62b60a73743589438dd", + "reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd", + "shasum": "" + }, + "require": { + "php": ">=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.17-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" }, { - "url": "https://github.com/fabpot", - "type": "github" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], "time": "2020-05-12T16:47:27+00:00" }, { @@ -10362,38 +10084,25 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-03-27T16:56:45+00:00" }, { "name": "symfony/yaml", - "version": "v5.0.8", + "version": "v5.1.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "482fb4e710e5af3e0e78015f19aa716ad953392f" + "reference": "ea342353a3ef4f453809acc4ebc55382231d4d23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/482fb4e710e5af3e0e78015f19aa716ad953392f", - "reference": "482fb4e710e5af3e0e78015f19aa716ad953392f", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ea342353a3ef4f453809acc4ebc55382231d4d23", + "reference": "ea342353a3ef4f453809acc4ebc55382231d4d23", "shasum": "" }, "require": { - "php": "^7.2.5", + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", "symfony/polyfill-ctype": "~1.8" }, "conflict": { @@ -10405,10 +10114,13 @@ "suggest": { "symfony/console": "For validating YAML files using the lint command" }, + "bin": [ + "Resources/bin/yaml-lint" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -10435,21 +10147,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-04-28T17:58:55+00:00" + "time": "2020-05-20T17:43:50+00:00" }, { "name": "thecodingmachine/safe", @@ -10724,16 +10422,6 @@ "env", "environment" ], - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "type": "tidelift" - } - ], "time": "2020-05-02T13:38:00+00:00" }, { @@ -10826,7 +10514,7 @@ "minimum-stability": "stable", "stability-flags": { "magento/composer": 20, - "magento/magento2-functional-testing-framework": 20 + "magento/magento2-functional-testing-framework": 5 }, "prefer-stable": true, "prefer-lowest": false, @@ -10849,6 +10537,5 @@ "ext-zip": "*", "lib-libxml": "*" }, - "platform-dev": [], - "plugin-api-version": "1.1.0" + "platform-dev": [] } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php index 6e90e85782bb2..c42450d86fd58 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/UpdateCustomerTest.php @@ -7,8 +7,9 @@ namespace Magento\GraphQl\Customer; +use Exception; use Magento\Customer\Model\CustomerAuthUpdate; -use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\AuthenticationException; use Magento\Integration\Api\CustomerTokenServiceInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; @@ -113,7 +114,7 @@ public function testUpdateCustomer() */ public function testUpdateCustomerIfInputDataIsEmpty() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('"input" value should be specified'); $currentEmail = 'customer@example.com'; @@ -139,7 +140,7 @@ public function testUpdateCustomerIfInputDataIsEmpty() */ public function testUpdateCustomerIfUserIsNotAuthorized() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('The current customer isn\'t authorized.'); $newFirstname = 'Richard'; @@ -165,7 +166,7 @@ public function testUpdateCustomerIfUserIsNotAuthorized() */ public function testUpdateCustomerIfAccountIsLocked() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('The account is locked.'); $this->lockCustomer->execute(1); @@ -195,7 +196,7 @@ public function testUpdateCustomerIfAccountIsLocked() */ public function testUpdateEmailIfPasswordIsMissed() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Provide the current "password" to change "email".'); $currentEmail = 'customer@example.com'; @@ -223,7 +224,7 @@ public function testUpdateEmailIfPasswordIsMissed() */ public function testUpdateEmailIfPasswordIsInvalid() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Invalid login or password.'); $currentEmail = 'customer@example.com'; @@ -253,8 +254,10 @@ public function testUpdateEmailIfPasswordIsInvalid() */ public function testUpdateEmailIfEmailAlreadyExists() { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('A customer with the same email address already exists in an associated website.'); + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'A customer with the same email address already exists in an associated website.' + ); $currentEmail = 'customer@example.com'; $currentPassword = 'password'; @@ -281,12 +284,42 @@ public function testUpdateEmailIfEmailAlreadyExists() $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testUpdateEmailIfEmailIsInvalid() + { + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $invalidEmail = 'customer.example.com'; + + $query = <<<QUERY +mutation { + updateCustomer( + input: { + email: "{$invalidEmail}" + password: "{$currentPassword}" + } + ) { + customer { + email + } + } +} +QUERY; + + $this->expectException(Exception::class); + $this->expectExceptionMessage('"' . $invalidEmail . '" is not a valid email address.'); + + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php */ public function testEmptyCustomerName() { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); $this->expectExceptionMessage('Required parameters are missing: First Name'); $currentEmail = 'customer@example.com'; @@ -310,10 +343,63 @@ public function testEmptyCustomerName() $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testEmptyCustomerLastName() + { + $query = <<<QUERY +mutation { + updateCustomer( + input: { + lastname: "" + } + ) { + customer { + lastname + } + } +} +QUERY; + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Required parameters are missing: Last Name'); + + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders('customer@example.com', 'password')); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testUpdateCustomerIfDobIsInvalid() + { + $invalidDob = 'bla-bla-bla'; + + $query = <<<QUERY +mutation { + updateCustomer( + input: { + date_of_birth: "{$invalidDob}" + } + ) { + customer { + date_of_birth + } + } +} +QUERY; + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Invalid date'); + + $this->graphQlMutation($query, [], '', $this->getCustomerAuthHeaders('customer@example.com', 'password')); + } + /** * @param string $email * @param string $password * @return array + * @throws AuthenticationException */ private function getCustomerAuthHeaders(string $email, string $password): array { diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/GiftMessage/Cart/GiftMessageTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/GiftMessage/Cart/GiftMessageTest.php new file mode 100644 index 0000000000000..8eaac6d46aa02 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/GiftMessage/Cart/GiftMessageTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\GiftMessage\Cart; + +use Exception; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +class GiftMessageTest extends GraphQlAbstract +{ + /** + * @var GetMaskedQuoteIdByReservedOrderId + */ + private $getMaskedQuoteIdByReservedOrderId; + + protected function setUp(): void + { + $objectManager = Bootstrap::getObjectManager(); + $this->getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoConfigFixture default_store sales/gift_options/allow_order 1 + * @magentoApiDataFixture Magento/GiftMessage/_files/quote_with_message.php + * @throws NoSuchEntityException + * @throws Exception + */ + public function testGiftMessageForCart() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('message_order_21'); + $response = $this->requestCartAndAssertResult($maskedQuoteId); + self::assertArrayHasKey('gift_message', $response['cart']); + self::assertSame('Mercutio', $response['cart']['gift_message']['to']); + self::assertSame('Romeo', $response['cart']['gift_message']['from']); + self::assertSame('I thought all for the best.', $response['cart']['gift_message']['message']); + } + + /** + * @magentoConfigFixture default_store sales/gift_options/allow_order 0 + * @magentoApiDataFixture Magento/GiftMessage/_files/quote_with_message.php + * @throws NoSuchEntityException + * @throws Exception + */ + public function testGiftMessageForCartWithNotAllow() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('message_order_21'); + $response = $this->requestCartAndAssertResult($maskedQuoteId); + self::assertArrayHasKey('gift_message', $response['cart']); + self::assertNull($response['cart']['gift_message']); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @throws NoSuchEntityException + * @throws Exception + */ + public function testGiftMessageForCartWithoutMessage() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $response = $this->requestCartAndAssertResult($maskedQuoteId); + self::assertArrayHasKey('gift_message', $response['cart']); + self::assertNull($response['cart']['gift_message']); + } + + /** + * Get Gift Message Assertion + * + * @param string $quoteId + * + * @return array + * @throws Exception + */ + private function requestCartAndAssertResult(string $quoteId) + { + $query = <<<QUERY +{ + cart(cart_id: "$quoteId") { + gift_message { + to + from + message + } + } +} +QUERY; + return $this->graphQlQuery($query); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/GiftMessage/Order/GiftMessageTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/GiftMessage/Order/GiftMessageTest.php new file mode 100644 index 0000000000000..538456884df58 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/GiftMessage/Order/GiftMessageTest.php @@ -0,0 +1,101 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\GiftMessage\Order; + +use Exception; +use Magento\Framework\Exception\AuthenticationException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +class GiftMessageTest extends GraphQlAbstract +{ + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + protected function setUp(): void + { + parent::setUp(); + $this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoConfigFixture default_store sales/gift_options/allow_order 1 + * @magentoConfigFixture default_store sales/gift_options/allow_items 1 + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GiftMessage/_files/customer/order_with_message.php + * @throws AuthenticationException + * @throws Exception + */ + public function testGiftMessageForOrder() + { + $query = $this->getCustomerOrdersQuery(); + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $response = $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + foreach ($response['customerOrders']['items'] as $order) { + self::assertArrayHasKey('gift_message', $order); + self::assertArrayHasKey('to', $order['gift_message']); + self::assertArrayHasKey('from', $order['gift_message']); + self::assertArrayHasKey('message', $order['gift_message']); + } + } + + /** + * @magentoConfigFixture default_store sales/gift_options/allow_order 0 + * @magentoConfigFixture default_store sales/gift_options/allow_items 0 + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GiftMessage/_files/customer/order_with_message.php + */ + public function testGiftMessageNotAllowForOrder() + { + $query = $this->getCustomerOrdersQuery(); + $currentEmail = 'customer@example.com'; + $currentPassword = 'password'; + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Can\'t load gift message for order'); + $this->graphQlQuery($query, [], '', $this->getCustomerAuthHeaders($currentEmail, $currentPassword)); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } + + /** + * Get Customer Orders query + * + * @return string + */ + private function getCustomerOrdersQuery() + { + return <<<QUERY +query { + customerOrders { + items { + order_number + gift_message { + to + from + message + } + } + } +} +QUERY; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Newsletter/Customer/SubscribeEmailToNewsletterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Newsletter/Customer/SubscribeEmailToNewsletterTest.php new file mode 100644 index 0000000000000..ec0e49cc55153 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Newsletter/Customer/SubscribeEmailToNewsletterTest.php @@ -0,0 +1,204 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Newsletter\Customer; + +use Exception; +use Magento\Customer\Model\CustomerAuthUpdate; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\AuthenticationException; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberResourceModel; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test newsletter email subscription for customer + */ +class SubscribeEmailToNewsletterTest extends GraphQlAbstract +{ + /** + * @var CustomerAuthUpdate + */ + private $customerAuthUpdate; + + /** + * @var CustomerRegistry + */ + private $customerRegistry; + + /** + * @var CustomerTokenServiceInterface + */ + private $customerTokenService; + + /** + * @var SubscriberResourceModel + */ + private $subscriberResource; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $objectManager = Bootstrap::getObjectManager(); + $this->customerAuthUpdate = Bootstrap::getObjectManager()->get(CustomerAuthUpdate::class); + $this->customerRegistry = Bootstrap::getObjectManager()->get(CustomerRegistry::class); + $this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class); + $this->subscriberResource = $objectManager->get(SubscriberResourceModel::class); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testAddRegisteredCustomerEmailIntoNewsletterSubscription() + { + $query = $this->getQuery('customer@example.com'); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('subscribeEmailToNewsletter', $response); + self::assertNotEmpty($response['subscribeEmailToNewsletter']); + self::assertEquals('SUBSCRIBED', $response['subscribeEmailToNewsletter']['status']); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testAddLockedCustomerEmailIntoNewsletterSubscription() + { + /* lock customer */ + $customerSecure = $this->customerRegistry->retrieveSecureData(1); + $customerSecure->setLockExpires('2030-12-31 00:00:00'); + $this->customerAuthUpdate->saveAuth(1); + + $query = $this->getQuery('customer@example.com'); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('subscribeEmailToNewsletter', $response); + self::assertNotEmpty($response['subscribeEmailToNewsletter']); + self::assertEquals('SUBSCRIBED', $response['subscribeEmailToNewsletter']['status']); + } + + /** + * @magentoConfigFixture default_store newsletter/subscription/confirm 1 + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testSubscribeRegisteredCustomerEmailWithEnabledConfirmation() + { + $query = $this->getQuery('customer@example.com'); + $response = $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('subscribeEmailToNewsletter', $response); + self::assertNotEmpty($response['subscribeEmailToNewsletter']); + self::assertEquals('NOT_ACTIVE', $response['subscribeEmailToNewsletter']['status']); + } + + /** + * @magentoConfigFixture default_store customer/create_account/confirm 1 + * @magentoApiDataFixture Magento/Customer/_files/unconfirmed_customer.php + * @expectedException Exception + * @expectedExceptionMessage The account sign-in was incorrect or your account is disabled temporarily. + * Please wait and try again later + */ + public function testNewsletterSubscriptionWithUnconfirmedCustomer() + { + $headers = $this->getHeaderMap('unconfirmedcustomer@example.com', 'Qwert12345'); + $query = $this->getQuery('unconfirmedcustomer@example.com'); + + $this->graphQlMutation($query, [], '', $headers); + } + + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + */ + public function testNewsletterSubscriptionWithIncorrectEmailFormat() + { + $query = $this->getQuery('customer.example.com'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Enter a valid email address.' . "\n"); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Newsletter/_files/subscribers.php + */ + public function testNewsletterSubscriptionWithAlreadySubscribedEmail() + { + $query = $this->getQuery('customer@example.com'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('This email address is already subscribed.' . "\n"); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap()); + } + + /** + * @magentoApiDataFixture Magento/Newsletter/_files/three_subscribers.php + */ + public function testNewsletterSubscriptionWithAnotherCustomerEmail() + { + $query = $this->getQuery('customer2@search.example.com'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Cannot create a newsletter subscription.' . "\n"); + + $this->graphQlMutation($query, [], '', $this->getHeaderMap('customer@search.example.com')); + } + + /** + * Returns a mutation query + * + * @param string $email + * @return string + */ + private function getQuery(string $email = ''): string + { + return <<<QUERY +mutation { + subscribeEmailToNewsletter( + email: "$email" + ) { + status + } +} +QUERY; + } + + /** + * Retrieve customer authorization headers + * + * @param string $username + * @param string $password + * @return array + * @throws AuthenticationException + */ + private function getHeaderMap(string $username = 'customer@example.com', string $password = 'password'): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($username, $password); + + return [ + 'Authorization' => 'Bearer ' . $customerToken + ]; + } + + /** + * @inheritDoc + */ + public function tearDown(): void + { + $this->subscriberResource + ->getConnection() + ->delete( + $this->subscriberResource->getMainTable() + ); + + parent::tearDown(); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Newsletter/Guest/SubscribeEmailToNewsletterTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Newsletter/Guest/SubscribeEmailToNewsletterTest.php new file mode 100644 index 0000000000000..f0a933609c762 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Newsletter/Guest/SubscribeEmailToNewsletterTest.php @@ -0,0 +1,114 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Newsletter\Guest; + +use Exception; +use Magento\Newsletter\Model\ResourceModel\Subscriber as SubscriberResourceModel; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test newsletter email subscription for guest + */ +class SubscribeEmailToNewsletterTest extends GraphQlAbstract +{ + /** + * @var SubscriberResourceModel + */ + private $subscriberResource; + + /** + * @inheritDoc + */ + protected function setUp(): void + { + $objectManager = Bootstrap::getObjectManager(); + $this->subscriberResource = $objectManager->get(SubscriberResourceModel::class); + } + + public function testAddEmailIntoNewsletterSubscription() + { + $query = $this->getQuery('guest@example.com'); + $response = $this->graphQlMutation($query); + + self::assertArrayHasKey('subscribeEmailToNewsletter', $response); + self::assertNotEmpty($response['subscribeEmailToNewsletter']); + self::assertEquals('SUBSCRIBED', $response['subscribeEmailToNewsletter']['status']); + } + + public function testNewsletterSubscriptionWithIncorrectEmailFormat() + { + $query = $this->getQuery('guest.example.com'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Enter a valid email address.' . "\n"); + + $this->graphQlMutation($query); + } + + /** + * @magentoConfigFixture default_store newsletter/subscription/allow_guest_subscribe 0 + */ + public function testNewsletterSubscriptionWithDisallowedGuestSubscription() + { + $query = $this->getQuery('guest@example.com'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage( + 'Guests can not subscribe to the newsletter. You must create an account to subscribe.' . "\n" + ); + + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/Newsletter/_files/guest_subscriber.php + */ + public function testNewsletterSubscriptionWithAlreadySubscribedEmail() + { + $query = $this->getQuery('guest@example.com'); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('This email address is already subscribed.' . "\n"); + + $this->graphQlMutation($query); + } + + /** + * Returns a mutation query + * + * @param string $email + * @return string + */ + private function getQuery(string $email = ''): string + { + return <<<QUERY +mutation { + subscribeEmailToNewsletter( + email: "$email" + ) { + status + } +} +QUERY; + } + + /** + * @inheritDoc + */ + public function tearDown(): void + { + $this->subscriberResource + ->getConnection() + ->delete( + $this->subscriberResource->getMainTable() + ); + + parent::tearDown(); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php index 337068710c31b..040215a241c47 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php @@ -122,7 +122,9 @@ public function testSendFriendDisableAsCustomer() public function testSendWithoutExistProduct() { $this->expectException(\Exception::class); - $this->expectExceptionMessage('The product that was requested doesn\'t exist. Verify the product and try again.'); + $this->expectExceptionMessage( + 'The product that was requested doesn\'t exist. Verify the product and try again.' + ); $productId = 2018; $recipients = '{ @@ -290,81 +292,124 @@ public function testSendProductWithoutVisibility() /** * @return array */ - public function sendFriendsErrorsDataProvider() + public function sendFriendsErrorsDataProvider(): array + { + return array_merge( + $this->getRecipientErrors(), + $this->getSenderErrors() + ); + } + + /** + * @return array + */ + private function getRecipientErrors(): array { return [ [ - 'product_id: 1 - sender: { - name: "Name" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { - name: "" - email:"recipient1@mail.com" - }, - { - name: "" - email:"recipient2@mail.com" - } - ]', 'Please provide Name for all of recipients.' + 'product_id: 1 + sender: { + name: "Name" + email: "e@mail.com" + message: "Lorem Ipsum" + } + recipients: [ + { + name: "" + email:"recipient1@mail.com" + }, + { + name: "" + email:"recipient2@mail.com" + } + ]', + 'Please provide Name for all of recipients.' ], [ 'product_id: 1 - sender: { - name: "Name" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { - name: "Recipient Name 1" - email:"" - }, - { - name: "Recipient Name 2" - email:"" - } - ]', 'Please provide Email for all of recipients.' + sender: { + name: "Name" + email: "e@mail.com" + message: "Lorem Ipsum" + } + recipients: [ + { + name: "Recipient Name 1" + email:"" + }, + { + name: "Recipient Name 2" + email:"" + } + ]', + 'Please provide Email for all of recipients.' + ], + ]; + } + + /** + * @return array + */ + private function getSenderErrors(): array + { + return [ + [ + 'product_id: 1 + sender: { + name: "" + email: "e@mail.com" + message: "Lorem Ipsum" + } + recipients: [ + { + name: "Recipient Name 1" + email:"recipient1@mail.com" + }, + { + name: "Recipient Name 2" + email:"recipient2@mail.com" + } + ]', + 'Please provide Name of sender.' ], [ 'product_id: 1 - sender: { - name: "" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { - name: "Recipient Name 1" - email:"recipient1@mail.com" - }, - { - name: "Recipient Name 2" - email:"recipient2@mail.com" - } - ]', 'Please provide Name of sender.' + sender: { + name: "Name" + email: "" + message: "Lorem Ipsum" + } + recipients: [ + { + name: "Recipient Name 1" + email:"recipient1@mail.com" + }, + { + name: "Recipient Name 2" + email:"recipient2@mail.com" + } + ]', + 'Please provide Email of sender.' ], [ 'product_id: 1 - sender: { - name: "Name" - email: "e@mail.com" - message: "" - } - recipients: [ - { - name: "Recipient Name 1" - email:"recipient1@mail.com" - }, - { - name: "Recipient Name 2" - email:"recipient2@mail.com" - } - ]', 'Please provide Message.' - ] + sender: { + name: "Name" + email: "e@mail.com" + message: "" + } + recipients: [ + { + name: "Recipient Name 1" + email:"recipient1@mail.com" + }, + { + name: "Recipient Name 2" + email:"recipient2@mail.com" + } + ]', + 'Please provide Message.' + ], ]; } diff --git a/dev/tests/integration/testsuite/Magento/AdminNotification/_files/notifications.php b/dev/tests/integration/testsuite/Magento/AdminNotification/_files/notifications.php index 6615c24320b21..0a8f2670b5740 100644 --- a/dev/tests/integration/testsuite/Magento/AdminNotification/_files/notifications.php +++ b/dev/tests/integration/testsuite/Magento/AdminNotification/_files/notifications.php @@ -3,52 +3,48 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -$om = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL -)->setTitle( - 'Unread Critical 1' -)->save(); - -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity(\Magento\Framework\Notification\MessageInterface::SEVERITY_MAJOR) - ->setTitle('Unread Major 1') - ->save(); - -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL -)->setTitle( - 'Unread Critical 2' -)->save(); - -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL -)->setTitle( - 'Unread Critical 3' -)->save(); - -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL -)->setTitle( - 'Read Critical 1' -)->setIsRead( - 1 -)->save(); - -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity(\Magento\Framework\Notification\MessageInterface::SEVERITY_MAJOR) - ->setTitle('Unread Major 2') - ->save(); - -$message = $om->create(\Magento\AdminNotification\Model\Inbox::class); -$message->setSeverity( - \Magento\Framework\Notification\MessageInterface::SEVERITY_CRITICAL -)->setTitle( - 'Removed Critical 1' -)->setIsRemove( - 1 -)->save(); + +declare(strict_types=1); + +use Magento\AdminNotification\Model\Inbox; +use Magento\AdminNotification\Model\ResourceModel\Inbox as InboxResource; +use Magento\Framework\Notification\MessageInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** + * @var Inbox $message + * @var InboxResource $messageResource + */ +$message = $objectManager->create(Inbox::class); +$messageResource = $objectManager->create(InboxResource::class); + +$message->setSeverity(MessageInterface::SEVERITY_CRITICAL)->setTitle('Unread Critical 1'); +$messageResource->save($message); + +$message = $objectManager->create(Inbox::class); +$message->setSeverity(MessageInterface::SEVERITY_MAJOR)->setTitle('Unread Major 1'); +$messageResource->save($message); + +$message = $objectManager->create(Inbox::class); +$message->setSeverity(MessageInterface::SEVERITY_CRITICAL)->setTitle('Unread Critical 2'); +$messageResource->save($message); + +$message = $objectManager->create(Inbox::class); +$message->setSeverity(MessageInterface::SEVERITY_CRITICAL)->setTitle('Unread Critical 3'); +$messageResource->save($message); + +$message = $objectManager->create(Inbox::class); +$message->setSeverity(MessageInterface::SEVERITY_CRITICAL)->setTitle('Read Critical 1')->setIsRead(1); +$messageResource->save($message); + +$message = $objectManager->create(Inbox::class); +$message->setSeverity(MessageInterface::SEVERITY_MAJOR)->setTitle('Unread Major 2'); +$messageResource->save($message); + +$message = $objectManager->create(Inbox::class); +$message->setSeverity(MessageInterface::SEVERITY_CRITICAL)->setTitle('Removed Critical 1')->setIsRemove(1); +$messageResource->save($message); diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php index ef5877612a3b9..2ae807f0a401b 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/create_products.php @@ -4,26 +4,50 @@ * See COPYING.txt for license details. */ -$productModel = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() - ->create(\Magento\Catalog\Model\Product::class); +declare(strict_types=1); -$productModel->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE) +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** + * @var Product $productModel + * @var ProductRepositoryInterface $productRepository + */ +$productModel = $objectManager->create(Product::class); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +$productModel->setTypeId(Type::TYPE_SIMPLE) ->setAttributeSetId(4) ->setName('AdvancedPricingSimple 1') ->setSku('AdvancedPricingSimple 1') ->setPrice(321) - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) ->setWebsiteIds([1]) ->setCategoryIds([]) ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) - ->setIsObjectNew(true) - ->save(); + ->setIsObjectNew(true); -$productModel->setName('AdvancedPricingSimple 2') - ->setId(null) - ->setUrlKey(null) +$productRepository->save($productModel); + +$productModel = $objectManager->create(Product::class); +$productModel->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setName('AdvancedPricingSimple 2') ->setSku('AdvancedPricingSimple 2') ->setPrice(654) - ->setIsObjectNew(true) - ->save(); + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setWebsiteIds([1]) + ->setCategoryIds([]) + ->setStockData(['qty' => 100, 'is_in_stock' => 1, 'manage_stock' => 1]) + ->setIsObjectNew(true); +$productRepository->save($productModel); diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website.php index 47456de5ab07e..17b6a700e0c07 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/_files/product_with_second_website.php @@ -4,7 +4,12 @@ * See COPYING.txt for license details. */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Group; +use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Workaround\Override\Fixture\Resolver; use Magento\Store\Api\WebsiteRepositoryInterface; @@ -13,15 +18,16 @@ Resolver::getInstance()->requireDataFixture('Magento/AdvancedPricingImportExport/_files/create_products.php'); $objectManager = Bootstrap::getObjectManager(); -/** @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository */ -$attributeRepository = $objectManager - ->get(Magento\Catalog\Api\ProductAttributeRepositoryInterface::class); -$groupPriceAttribute = $attributeRepository->get('tier_price') - ->setScope(Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_WEBSITE); + +/** @var ProductAttributeRepositoryInterface $attributeRepository */ +$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class); +$groupPriceAttribute = $attributeRepository->get('tier_price')->setScope(ScopedAttributeInterface::SCOPE_WEBSITE); $attributeRepository->save($groupPriceAttribute); + /** @var WebsiteRepositoryInterface $websiteRepository */ $websiteRepository = $objectManager->get(WebsiteRepositoryInterface::class); $website = $websiteRepository->get('test'); + /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->create(ProductRepositoryInterface::class); $productModel = $productRepository->get('AdvancedPricingSimple 2'); @@ -30,10 +36,10 @@ [ [ 'website_id' => $website->getId(), - 'cust_group' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'cust_group' => Group::CUST_GROUP_ALL, 'price_qty' => 3, 'price' => 5 ] ] ); -$productModel->save(); +$productRepository->save($productModel); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php index 460488fdfae76..4f046eccbe59f 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Product/CompareTest.php @@ -412,7 +412,7 @@ protected function _assertCompareListEquals(array $expectedProductIds) $compareItems = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( \Magento\Catalog\Model\ResourceModel\Product\Compare\Item\Collection::class ); - $compareItems->useProductItem(true); + $compareItems->useProductItem(); // important $compareItems->setVisitorId( \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php index 34dccc2284445..57b918fb5e663 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_boolean_attribute.php @@ -9,6 +9,7 @@ use Magento\Eav\Api\AttributeRepositoryInterface; use Magento\Catalog\Model\ResourceModel\Eav\Attribute; use Magento\Eav\Model\Entity\Attribute\Source\Boolean; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); @@ -19,32 +20,36 @@ /** @var $installer CategorySetup */ $installer = $objectManager->create(CategorySetup::class); -$attribute->setData( - [ - 'attribute_code' => 'boolean_attribute', - 'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID, - 'is_global' => 0, - 'is_user_defined' => 1, - 'frontend_input' => 'boolean', - 'is_unique' => 0, - 'is_required' => 0, - 'is_searchable' => 1, - 'is_visible_in_advanced_search' => 1, - 'is_comparable' => 0, - 'is_filterable' => 1, - 'is_filterable_in_search' => 1, - 'is_used_for_promo_rules' => 0, - 'is_html_allowed_on_front' => 1, - 'is_visible_on_front' => 1, - 'used_in_product_listing' => 1, - 'used_for_sort_by' => 0, - 'frontend_label' => ['Boolean Attribute'], - 'backend_type' => 'int', - 'source_model' => Boolean::class - ] -); +try { + $attributeRepository->get(CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID, 'boolean_attribute'); +} catch (NoSuchEntityException $e) { + $attribute->setData( + [ + 'attribute_code' => 'boolean_attribute', + 'entity_type_id' => CategorySetup::CATALOG_PRODUCT_ENTITY_TYPE_ID, + 'is_global' => 0, + 'is_user_defined' => 1, + 'frontend_input' => 'boolean', + 'is_unique' => 0, + 'is_required' => 0, + 'is_searchable' => 1, + 'is_visible_in_advanced_search' => 1, + 'is_comparable' => 0, + 'is_filterable' => 1, + 'is_filterable_in_search' => 1, + 'is_used_for_promo_rules' => 0, + 'is_html_allowed_on_front' => 1, + 'is_visible_on_front' => 1, + 'used_in_product_listing' => 1, + 'used_for_sort_by' => 0, + 'frontend_label' => ['Boolean Attribute'], + 'backend_type' => 'int', + 'source_model' => Boolean::class + ] + ); -$attributeRepository->save($attribute); + $attributeRepository->save($attribute); -/* Assign attribute to attribute set */ -$installer->addAttributeToGroup('catalog_product', 'Default', 'Attributes', $attribute->getId()); + /* Assign attribute to attribute set */ + $installer->addAttributeToGroup('catalog_product', 'Default', 'Attributes', $attribute->getId()); +} diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/block.php b/dev/tests/integration/testsuite/Magento/Cms/_files/block.php index 070fd9ae2a0b3..4625c1fe3313b 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/block.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/block.php @@ -3,9 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + +use Magento\Cms\Api\BlockRepositoryInterface; +use Magento\Cms\Model\Block; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** + * @var $block Block + * @var $blockRepository BlockRepositoryInterface + */ +$block = $objectManager->create(Block::class); +$blockRepository = $objectManager->create(BlockRepositoryInterface::class); -/** @var $block \Magento\Cms\Model\Block */ -$block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Cms\Model\Block::class); $block->setTitle( 'CMS Block Title' )->setIdentifier( @@ -20,8 +33,10 @@ 1 )->setStores( [ - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( - \Magento\Store\Model\StoreManagerInterface::class + Bootstrap::getObjectManager()->get( + StoreManagerInterface::class )->getStore()->getId() ] -)->save(); +); + +$blockRepository->save($block); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/block_default_store.php b/dev/tests/integration/testsuite/Magento/Cms/_files/block_default_store.php index de4e852f807bc..825103d76ecff 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/block_default_store.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/block_default_store.php @@ -5,12 +5,20 @@ */ declare(strict_types=1); +use Magento\Cms\Api\BlockRepositoryInterface; use Magento\Cms\Model\Block; use Magento\Store\Model\Store; use Magento\TestFramework\Helper\Bootstrap; -/** @var $block Block */ -$block = Bootstrap::getObjectManager()->create(Block::class); +$objectManager = Bootstrap::getObjectManager(); + +/** + * @var $block Block + * @var $blockRepository BlockRepositoryInterface + */ +$block = $objectManager->create(Block::class); +$blockRepository = $objectManager->create(BlockRepositoryInterface::class); + $block->setTitle( 'CMS Block Title' )->setIdentifier( @@ -24,4 +32,6 @@ 1 )->setStores( [Store::DEFAULT_STORE_ID] -)->save(); +); + +$blockRepository->save($block); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php b/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php index 2556e0318222d..a4dd0c5fd4e56 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/home_with_custom_handle.php @@ -5,6 +5,7 @@ */ declare(strict_types=1); +use Magento\Cms\Model\ResourceModel\Page as PageResource; use Magento\Cms\Model\Page as PageModel; use Magento\Cms\Model\PageFactory as PageModelFactory; use Magento\TestFramework\Cms\Model\CustomLayoutManager; @@ -20,11 +21,16 @@ $customLayoutName = 'page_custom_layout'; -/** @var PageModel $page */ +/** + * @var PageModel $page + * @var PageResource $pageResource + */ $page = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); -$page->load('home'); +$pageResource = $objectManager->create(PageResource::class); + +$pageResource->load($page, 'home'); $cmsPageId = (int)$page->getId(); $fakeManager->fakeAvailableFiles($cmsPageId, [$customLayoutName]); $page->setData('layout_update_selected', $customLayoutName); -$page->save(); +$pageResource->save($page); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/noroute.php b/dev/tests/integration/testsuite/Magento/Cms/_files/noroute.php index 4c56132a12c01..6fb93a266036c 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/noroute.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/noroute.php @@ -3,6 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -$block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Cms\Model\Page::class); -$block->load('no-route', 'identifier'); -$block->setIsActive(0)->save(); + +declare(strict_types=1); + +use Magento\Cms\Model\Page; +use Magento\Cms\Model\ResourceModel\Page as PageResource; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** + * @var Page $page + * @var PageResource $pageResource + */ +$page = $objectManager->create(Page::class); +$pageResource = $objectManager->create(PageResource::class); + +$pageResource->load($page, 'no-route', 'identifier'); +$page->setIsActive(0); +$pageResource->save($page); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages.php index b2742ecd380f3..3581fdc34f8e5 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages.php @@ -4,8 +4,21 @@ * See COPYING.txt for license details. */ -/** @var $page \Magento\Cms\Model\Page */ -$page = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Cms\Model\Page::class); +declare(strict_types=1); + +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** + * @var $page Page + * @var $pageRepository PageRepositoryInterface + */ +$page = $objectManager->create(Page::class); +$pageRepository = $objectManager->create(PageRepositoryInterface::class); + $page->setTitle('Cms Page 100') ->setIdentifier('page100') ->setStores([0]) @@ -15,10 +28,10 @@ ->setMetaTitle('Cms Meta title for page100') ->setMetaKeywords('Cms Meta Keywords for page100') ->setMetaDescription('Cms Meta Description for page100') - ->setPageLayout('1column') - ->save(); + ->setPageLayout('1column'); +$pageRepository->save($page); -$page = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Cms\Model\Page::class); +$page = $objectManager->create(Page::class); $page->setTitle('Cms Page Design Blank') ->setIdentifier('page_design_blank') ->setStores([0]) @@ -29,5 +42,5 @@ ->setMetaKeywords('Cms Meta Keywords for Blank page') ->setMetaDescription('Cms Meta Description for Blank page') ->setPageLayout('1column') - ->setCustomTheme('Magento/blank') - ->save(); + ->setCustomTheme('Magento/blank'); +$pageRepository->save($page); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php index 9734ed3abaeed..c7ea5f6380b32 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml.php @@ -7,16 +7,21 @@ declare(strict_types=1); use Magento\Cms\Model\Page as PageModel; +use Magento\Cms\Model\ResourceModel\Page as PageResource; use Magento\Cms\Model\PageFactory as PageModelFactory; use Magento\TestFramework\Cms\Model\CustomLayoutManager; use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); $pageFactory = $objectManager->get(PageModelFactory::class); + /** @var CustomLayoutManager $fakeManager */ $fakeManager = $objectManager->get(CustomLayoutManager::class); $layoutRepo = $objectManager->create(PageModel\CustomLayoutRepositoryInterface::class, ['manager' => $fakeManager]); +/** @var PageResource $pageRepository */ +$pageResource = $objectManager->create(PageResource::class); + /** @var PageModel $page */ $page = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); $page->setIdentifier('test_custom_layout_page_1'); @@ -25,14 +30,16 @@ $page->setLayoutUpdateXml('<container />'); $page->setIsActive(true); $page->setStoreId(0); -$page->save(); +$pageResource->save($page); + /** @var PageModel $page2 */ $page2 = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); $page2->setIdentifier('test_custom_layout_page_2'); $page2->setTitle('Test Page 2'); $page->setIsActive(true); $page->setStoreId(0); -$page2->save(); +$pageResource->save($page2); + /** @var PageModel $page3 */ $page3 = $pageFactory->create(['customLayoutRepository' => $layoutRepo]); $page3->setIdentifier('test_custom_layout_page_3'); @@ -41,7 +48,7 @@ $page3->setIsActive(1); $page3->setContent('<h1>Test Page</h1>'); $page3->setPageLayout('1column'); -$page3->save(); +$pageResource->save($page3); $fakeManager->fakeAvailableFiles((int)$page3->getId(), ['test_selected']); $page3->setData('layout_update_selected', 'test_selected'); -$page3->save(); +$pageResource->save($page3); diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php index 3217b94d7392b..684b1d4356d20 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/pages_with_layout_xml_rollback.php @@ -8,25 +8,33 @@ use Magento\Cms\Model\Page as PageModel; use Magento\Cms\Model\PageFactory as PageModelFactory; +use Magento\Cms\Model\ResourceModel\Page as PageResource; use Magento\TestFramework\Helper\Bootstrap; $objectManager = Bootstrap::getObjectManager(); $pageFactory = $objectManager->get(PageModelFactory::class); -/** @var PageModel $page */ + +/** + * @var PageModel $page + * @var PageResource $pageResource + */ $page = $pageFactory->create(); -$page->load('test_custom_layout_page_1', PageModel::IDENTIFIER); +$pageResource = $objectManager->create(PageResource::class); +$pageResource->load($page, 'test_custom_layout_page_1', PageModel::IDENTIFIER); if ($page->getId()) { - $page->delete(); + $pageResource->delete($page); } + /** @var PageModel $page2 */ $page2 = $pageFactory->create(); -$page2->load('test_custom_layout_page_2', PageModel::IDENTIFIER); +$pageResource->load($page2, 'test_custom_layout_page_2', PageModel::IDENTIFIER); if ($page2->getId()) { - $page2->delete(); + $pageResource->delete($page2); } + /** @var PageModel $page3 */ $page3 = $pageFactory->create(); -$page3->load('test_custom_layout_page_3', PageModel::IDENTIFIER); +$pageResource->load($page3, 'test_custom_layout_page_3', PageModel::IDENTIFIER); if ($page3->getId()) { - $page3->delete(); + $pageResource->delete($page3); } diff --git a/dev/tests/integration/testsuite/Magento/Cms/_files/two_cms_page_with_same_url_for_different_stores.php b/dev/tests/integration/testsuite/Magento/Cms/_files/two_cms_page_with_same_url_for_different_stores.php index 16e4a4e521fa3..fdb042fbb18fa 100644 --- a/dev/tests/integration/testsuite/Magento/Cms/_files/two_cms_page_with_same_url_for_different_stores.php +++ b/dev/tests/integration/testsuite/Magento/Cms/_files/two_cms_page_with_same_url_for_different_stores.php @@ -5,29 +5,37 @@ */ declare(strict_types=1); +use Magento\Cms\Api\PageRepositoryInterface; +use Magento\Cms\Model\Page; use Magento\Store\Api\StoreRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Workaround\Override\Fixture\Resolver; Resolver::getInstance()->requireDataFixture('Magento/Store/_files/second_store.php'); -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +$objectManager = Bootstrap::getObjectManager(); + /** @var StoreRepositoryInterface $storeRepository */ $storeRepository = $objectManager->get(StoreRepositoryInterface::class); $store = $storeRepository->get('fixture_second_store'); -/** @var $page \Magento\Cms\Model\Page */ -$page = $objectManager->create(\Magento\Cms\Model\Page::class); + +/** @var PageRepositoryInterface $pageRepository */ +$pageRepository = $objectManager->create(PageRepositoryInterface::class); + +/** @var $page Page */ +$page = $objectManager->create(Page::class); $page->setTitle('First test page') ->setIdentifier('page1') ->setStores([1]) ->setIsActive(1) - ->setPageLayout('1column') - ->save(); + ->setPageLayout('1column'); +$pageRepository->save($page); -/** @var $page \Magento\Cms\Model\Page */ -$page = $objectManager->create(\Magento\Cms\Model\Page::class); +/** @var $page Page */ +$page = $objectManager->create(Page::class); $page->setTitle('Second test page') ->setIdentifier('page1') ->setStores([$store->getId()]) ->setIsActive(1) - ->setPageLayout('1column') - ->save(); + ->setPageLayout('1column'); +$pageRepository->save($page); diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php index 1eb2550dc484c..0173a643dd7bd 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/Model/Indexer/IndexHandlerTest.php @@ -19,6 +19,7 @@ use Magento\Indexer\Model\Indexer; use Magento\Framework\Search\EngineResolverInterface; use Magento\TestModuleCatalogSearch\Model\ElasticsearchVersionChecker; +use PHPUnit\Framework\TestCase; /** * Important: Please make sure that each integration test file works with unique elastic search index. In order to @@ -29,7 +30,7 @@ * @magentoDataFixture Magento/Elasticsearch/_files/indexer.php * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class IndexHandlerTest extends \PHPUnit\Framework\TestCase +class IndexHandlerTest extends TestCase { /** * @var string @@ -116,6 +117,9 @@ public function testReindexAll(): void $products = $this->searchByName('Simple Product', $storeId); $this->assertCount(5, $products); + + $this->assertCount(2, $this->searchByBoolAttribute(0, $storeId)); + $this->assertCount(3, $this->searchByBoolAttribute(1, $storeId)); } } @@ -266,6 +270,32 @@ private function searchByName(string $text, int $storeId): array return $products; } + /** + * Search docs in Elasticsearch by boolean attribute. + * + * @param int $value + * @param int $storeId + * @return array + */ + private function searchByBoolAttribute(int $value, int $storeId): array + { + $index = $this->searchIndexNameResolver->getIndexName($storeId, $this->indexer->getId()); + $searchQuery = [ + 'index' => $index, + 'type' => $this->entityType, + 'body' => [ + 'query' => [ + 'query_string' => [ + 'query' => $value, + 'default_field' => 'boolean_attribute', + ], + ], + ], + ]; + $queryResult = $this->client->query($searchQuery); + return isset($queryResult['hits']['hits']) ? $queryResult['hits']['hits'] : []; + } + /** * Returns installed on server search service * diff --git a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php index cf87be7e8d710..c6989c7805b4a 100644 --- a/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php +++ b/dev/tests/integration/testsuite/Magento/Elasticsearch/_files/indexer.php @@ -4,14 +4,27 @@ * See COPYING.txt for license details. */ -/** @var $objectManager \Magento\Framework\ObjectManagerInterface */ -$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\App\MutableScopeConfig; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_boolean_attribute.php'); -/** @var \Magento\Store\Model\StoreManagerInterface $storeManager */ -$storeManager = $objectManager->get(\Magento\Store\Model\StoreManagerInterface::class); +/** @var $objectManager \Magento\Framework\ObjectManagerInterface */ +$objectManager = Bootstrap::getObjectManager(); -/** @var \Magento\Store\Model\Store $store */ -$store = $objectManager->create(\Magento\Store\Model\Store::class); +/** @var StoreManagerInterface $storeManager */ +$storeManager = $objectManager->get(StoreManagerInterface::class); +/** @var Store $store */ +$store = $objectManager->create(Store::class); $storeCode = 'secondary'; if (!$store->load($storeCode)->getId()) { @@ -23,92 +36,118 @@ ->setIsActive(1); $store->save(); - /** @var \Magento\Framework\App\MutableScopeConfig $scopeConfig */ - $scopeConfig = $objectManager->get(\Magento\Framework\App\MutableScopeConfig::class); + /** @var MutableScopeConfig $scopeConfig */ + $scopeConfig = $objectManager->get(MutableScopeConfig::class); $scopeConfig->setValue( 'general/locale/code', 'de_DE', - \Magento\Store\Model\ScopeInterface::SCOPE_STORES, + ScopeInterface::SCOPE_STORES, $store->getId() ); } -/** @var $productFirst \Magento\Catalog\Model\Product */ -$productFirst = $objectManager->create(\Magento\Catalog\Model\Product::class); -$productFirst->setTypeId('simple') - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product Apple') - ->setSku('fulltext-1') - ->setPrice(10) - ->setMetaTitle('first meta title') - ->setMetaKeyword('first meta keyword') - ->setMetaDescription('first meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 0]) - ->save(); +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +try { + $productRepository->get('fulltext-1'); +} catch (NoSuchEntityException $e) { + /** @var $productFirst Product */ + $productFirst = $objectManager->create(Product::class); + $productFirst->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product Apple') + ->setSku('fulltext-1') + ->setPrice(10) + ->setMetaTitle('first meta title') + ->setMetaKeyword('first meta keyword') + ->setMetaDescription('first meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->setBooleanAttribute(1) + ->save(); +} -/** @var $productSecond \Magento\Catalog\Model\Product */ -$productSecond = $objectManager->create(\Magento\Catalog\Model\Product::class); -$productSecond->setTypeId('simple') - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product Banana') - ->setSku('fulltext-2') - ->setPrice(20) - ->setMetaTitle('second meta title') - ->setMetaKeyword('second meta keyword') - ->setMetaDescription('second meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 0]) - ->save(); +try { + $productRepository->get('fulltext-2'); +} catch (NoSuchEntityException $e) { + /** @var $productSecond Product */ + $productSecond = $objectManager->create(Product::class); + $productSecond->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product Banana') + ->setSku('fulltext-2') + ->setPrice(20) + ->setMetaTitle('second meta title') + ->setMetaKeyword('second meta keyword') + ->setMetaDescription('second meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->setBooleanAttribute(1) + ->save(); +} -/** @var $productThird \Magento\Catalog\Model\Product */ -$productThird = $objectManager->create(\Magento\Catalog\Model\Product::class); -$productThird->setTypeId('simple') - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product Orange') - ->setSku('fulltext-3') - ->setPrice(20) - ->setMetaTitle('third meta title') - ->setMetaKeyword('third meta keyword') - ->setMetaDescription('third meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 0]) - ->save(); +try { + $productRepository->get('fulltext-3'); +} catch (NoSuchEntityException $e) { + /** @var $productThird Product */ + $productThird = $objectManager->create(Product::class); + $productThird->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product Orange') + ->setSku('fulltext-3') + ->setPrice(20) + ->setMetaTitle('third meta title') + ->setMetaKeyword('third meta keyword') + ->setMetaDescription('third meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->setBooleanAttribute(1) + ->save(); +} -/** @var $productFourth \Magento\Catalog\Model\Product */ -$productFourth = $objectManager->create(\Magento\Catalog\Model\Product::class); -$productFourth->setTypeId('simple') - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product Papaya') - ->setSku('fulltext-4') - ->setPrice(20) - ->setMetaTitle('fourth meta title') - ->setMetaKeyword('fourth meta keyword') - ->setMetaDescription('fourth meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 0]) - ->save(); +try { + $productRepository->get('fulltext-4'); +} catch (NoSuchEntityException $e) { + /** @var $productFourth Product */ + $productFourth = $objectManager->create(Product::class); + $productFourth->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product Papaya') + ->setSku('fulltext-4') + ->setPrice(20) + ->setMetaTitle('fourth meta title') + ->setMetaKeyword('fourth meta keyword') + ->setMetaDescription('fourth meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->setBooleanAttribute(0) + ->save(); +} -/** @var $productFifth \Magento\Catalog\Model\Product */ -$productFifth = $objectManager->create(\Magento\Catalog\Model\Product::class); -$productFifth->setTypeId('simple') - ->setAttributeSetId(4) - ->setWebsiteIds([1]) - ->setName('Simple Product Cherry') - ->setSku('fulltext-5') - ->setPrice(20) - ->setMetaTitle('fifth meta title') - ->setMetaKeyword('fifth meta keyword') - ->setMetaDescription('fifth meta description') - ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) - ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) - ->setStockData(['use_config_manage_stock' => 0]) - ->save(); +try { + $productRepository->get('fulltext-5'); +} catch (NoSuchEntityException $e) { + /** @var $productFifth Product */ + $productFifth = $objectManager->create(Product::class); + $productFifth->setTypeId('simple') + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product Cherry') + ->setSku('fulltext-5') + ->setPrice(20) + ->setMetaTitle('fifth meta title') + ->setMetaKeyword('fifth meta keyword') + ->setMetaDescription('fifth meta description') + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData(['use_config_manage_stock' => 0]) + ->setBooleanAttribute(0) + ->save(); +} diff --git a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample index 74c1522fa41f0..930c439899f03 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample +++ b/dev/tests/integration/testsuite/Magento/Framework/Code/_expected/SourceClassWithNamespaceInterceptor.php.sample @@ -21,11 +21,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function publicChildMethod(\Laminas\Code\Generator\ClassGenerator $classGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicChildMethod'); - if (!$pluginInfo) { - return parent::publicChildMethod($classGenerator, $param1, $param2, $param3, $array); - } else { - return $this->___callPlugins('publicChildMethod', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('publicChildMethod', func_get_args(), $pluginInfo) : parent::publicChildMethod($classGenerator, $param1, $param2, $param3, $array); } /** @@ -34,11 +30,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function publicMethodWithReference(\Laminas\Code\Generator\ClassGenerator &$classGenerator, &$param1, array &$array) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicMethodWithReference'); - if (!$pluginInfo) { - return parent::publicMethodWithReference($classGenerator, $param1, $array); - } else { - return $this->___callPlugins('publicMethodWithReference', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('publicMethodWithReference', func_get_args(), $pluginInfo) : parent::publicMethodWithReference($classGenerator, $param1, $array); } /** @@ -47,11 +39,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function publicChildWithoutParameters() { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicChildWithoutParameters'); - if (!$pluginInfo) { - return parent::publicChildWithoutParameters(); - } else { - return $this->___callPlugins('publicChildWithoutParameters', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('publicChildWithoutParameters', func_get_args(), $pluginInfo) : parent::publicChildWithoutParameters(); } /** @@ -60,11 +48,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function public71($arg1, string $arg2, ?int $arg3, ?int $arg4 = null) : void { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'public71'); - if (!$pluginInfo) { - parent::public71($arg1, $arg2, $arg3, $arg4); - } else { - $this->___callPlugins('public71', func_get_args(), $pluginInfo); - } + $pluginInfo ? $this->___callPlugins('public71', func_get_args(), $pluginInfo) : parent::public71($arg1, $arg2, $arg3, $arg4); } /** @@ -73,11 +57,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function public71Another(?\DateTime $arg1, $arg2 = false) : ?string { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'public71Another'); - if (!$pluginInfo) { - return parent::public71Another($arg1, $arg2); - } else { - return $this->___callPlugins('public71Another', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('public71Another', func_get_args(), $pluginInfo) : parent::public71Another($arg1, $arg2); } /** @@ -86,11 +66,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function publicWithSelf($arg = false) : \Magento\Framework\Code\GeneratorTest\SourceClassWithNamespace { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicWithSelf'); - if (!$pluginInfo) { - return parent::publicWithSelf($arg); - } else { - return $this->___callPlugins('publicWithSelf', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('publicWithSelf', func_get_args(), $pluginInfo) : parent::publicWithSelf($arg); } /** @@ -99,11 +75,7 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function publicParentMethod(\Laminas\Code\Generator\DocBlockGenerator $docBlockGenerator, $param1 = '', $param2 = '\\', $param3 = '\'', array $array = []) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicParentMethod'); - if (!$pluginInfo) { - return parent::publicParentMethod($docBlockGenerator, $param1, $param2, $param3, $array); - } else { - return $this->___callPlugins('publicParentMethod', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('publicParentMethod', func_get_args(), $pluginInfo) : parent::publicParentMethod($docBlockGenerator, $param1, $param2, $param3, $array); } /** @@ -112,10 +84,6 @@ class Interceptor extends \Magento\Framework\Code\GeneratorTest\SourceClassWithN public function publicParentWithoutParameters() { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'publicParentWithoutParameters'); - if (!$pluginInfo) { - return parent::publicParentWithoutParameters(); - } else { - return $this->___callPlugins('publicParentWithoutParameters', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('publicParentWithoutParameters', func_get_args(), $pluginInfo) : parent::publicParentWithoutParameters(); } } diff --git a/dev/tests/integration/testsuite/Magento/GiftMessage/_files/customer/order_with_message.php b/dev/tests/integration/testsuite/Magento/GiftMessage/_files/customer/order_with_message.php new file mode 100644 index 0000000000000..55b38d9900acd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GiftMessage/_files/customer/order_with_message.php @@ -0,0 +1,102 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Workaround\Override\Fixture\Resolver; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\GiftMessage\Model\Message; +use Magento\GiftMessage\Model\ResourceModel\Message as MessageResource; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address as OrderAddress; + +Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple.php'); + +$addressData = include __DIR__ . '/../../../../Magento/Sales/_files/address_data.php'; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Order $order */ +/** @var Order\Payment $payment */ +/** @var Order\Item $orderItem */ +/** @var array $addressData Data for creating addresses for the orders. */ +$orders = [ + [ + 'increment_id' => '999999990', + 'state' => Order::STATE_NEW, + 'status' => 'processing', + 'grand_total' => 120.00, + 'subtotal' => 120.00, + 'base_grand_total' => 120.00, + 'store_id' => 1, + 'website_id' => 1, + ], + [ + 'increment_id' => '999999991', + 'state' => Order::STATE_PROCESSING, + 'status' => 'processing', + 'grand_total' => 130.00, + 'base_grand_total' => 130.00, + 'subtotal' => 130.00, + 'total_paid' => 130.00, + 'store_id' => 1, + 'website_id' => 1, + ] +]; + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->create(OrderRepositoryInterface::class); + +$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class); +$payment->setMethod('checkmo'); +$productRepository = $objectManager->create(ProductRepositoryInterface::class); +$product = $productRepository->get('simple'); + +/** @var array $orderData */ +foreach ($orders as $orderData) { + /** @var Magento\Sales\Model\Order $order */ + $order = $objectManager->create(Order::class); + + // Reset addresses + /** @var Order\Address $billingAddress */ + $billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]); + $billingAddress->setAddressType('billing'); + + $shippingAddress = clone $billingAddress; + $shippingAddress->setId(null)->setAddressType('shipping'); + + /** @var MessageResource $message */ + $message = $objectManager->create(MessageResource::class); + + /** @var Message $message */ + $messageModel = $objectManager->create(Message::class); + + $messageModel->setSender('John Doe'); + $messageModel->setRecipient('Jane Roe'); + $messageModel->setMessage('Gift Message Text'); + $message->save($messageModel); + + /** @var Order\Item $orderItem */ + $orderItem = $objectManager->create(Order\Item::class); + $orderItem->setProductId($product->getId()) + ->setQtyOrdered(2) + ->setBasePrice($product->getPrice()) + ->setPrice($product->getPrice()) + ->setRowTotal($product->getPrice()) + ->setProductType('simple'); + + $order + ->setData($orderData) + ->addItem($orderItem) + ->setCustomerIsGuest(false) + ->setCustomerId(1) + ->setCustomerEmail('customer@example.com') + ->setBillingAddress($billingAddress) + ->setShippingAddress($shippingAddress) + ->setPayment($payment); + $order->setGiftMessageId($messageModel->getId()); + $orderRepository->save($order); +} diff --git a/dev/tests/integration/testsuite/Magento/GiftMessage/_files/customer/order_with_message_rollback.php b/dev/tests/integration/testsuite/Magento/GiftMessage/_files/customer/order_with_message_rollback.php new file mode 100644 index 0000000000000..5aaf728243729 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GiftMessage/_files/customer/order_with_message_rollback.php @@ -0,0 +1,46 @@ +<?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\Registry; +use Magento\GiftMessage\Model\Message; +use Magento\Sales\Api\Data\OrderInterfaceFactory; +use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +$product = $productRepository->get('simple'); +$productRepository->delete($product); + +/** @var OrderRepositoryInterface $orderRepository */ +$orderRepository = $objectManager->get(OrderRepositoryInterface::class); +/** @var OrderInterfaceFactory $orderFactory */ +$orderFactory = $objectManager->create(OrderInterfaceFactory::class); +$orders = []; +$orders[] = $orderFactory->create()->loadByIncrementId('999999990'); +$orders[] = $orderFactory->create()->loadByIncrementId('999999991'); + +foreach ($orders as $order) { + if ($order->getGiftMessageId()) { + $message = $objectManager->create(Message::class); + $message->load($order->getGiftMessageId()); + $message->delete(); + } + if ($order->getId()) { + $orderRepository->delete($order); + } +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/guest_subscriber.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/guest_subscriber.php new file mode 100644 index 0000000000000..e10dcd5985a2e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/guest_subscriber.php @@ -0,0 +1,24 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +$storeId = $objectManager->get(StoreManagerInterface::class) + ->getStore() + ->getId(); + +/** @var Subscriber $subscriber */ +$subscriber = $objectManager->create(Subscriber::class); + +$subscriber->setStoreId($storeId) + ->setSubscriberEmail('guest@example.com') + ->setSubscriberStatus(Subscriber::STATUS_SUBSCRIBED) + ->save(); diff --git a/dev/tests/integration/testsuite/Magento/Newsletter/_files/guest_subscriber_rollback.php b/dev/tests/integration/testsuite/Magento/Newsletter/_files/guest_subscriber_rollback.php new file mode 100644 index 0000000000000..225f6515b5ce7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Newsletter/_files/guest_subscriber_rollback.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Registry; +use Magento\Newsletter\Model\Subscriber; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$storeId = $objectManager->get(StoreManagerInterface::class) + ->getStore() + ->getId(); + +/** @var Subscriber $subscriber */ +$subscriber = $objectManager->get(Subscriber::class); +$subscriber->loadBySubscriberEmail('guest@example.com', (int)$storeId); +if ($subscriber->getId()) { + $subscriber->delete(); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Cron/CleanExpiredQuotesTest.php b/dev/tests/integration/testsuite/Magento/Sales/Cron/CleanExpiredQuotesTest.php index f6dee4c7ff0fa..f69c97a635e76 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Cron/CleanExpiredQuotesTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Cron/CleanExpiredQuotesTest.php @@ -9,6 +9,7 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Quote\Model\QuoteRepository; +use Magento\Quote\Model\ResourceModel\Quote\CollectionFactory as QuoteCollectionFactory; use Magento\TestFramework\Helper\Bootstrap; /** @@ -25,14 +26,9 @@ class CleanExpiredQuotesTest extends \PHPUnit\Framework\TestCase private $cleanExpiredQuotes; /** - * @var QuoteRepository + * @var QuoteCollectionFactory */ - private $quoteRepository; - - /** - * @var SearchCriteriaBuilder - */ - private $searchCriteriaBuilder; + private $quoteCollectionFactory; /** * @inheritdoc @@ -41,8 +37,7 @@ protected function setUp(): void { $objectManager = Bootstrap::getObjectManager(); $this->cleanExpiredQuotes = $objectManager->get(CleanExpiredQuotes::class); - $this->quoteRepository = $objectManager->get(QuoteRepository::class); - $this->searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); + $this->quoteCollectionFactory = $objectManager->get(QuoteCollectionFactory::class); } /** @@ -53,17 +48,43 @@ protected function setUp(): void */ public function testExecute() { - $searchCriteria = $this->searchCriteriaBuilder->create(); //Initial count - should be equal to stores number. - $this->assertEquals(2, $this->quoteRepository->getList($searchCriteria)->getTotalCount()); + $this->assertQuotesCount(2); //Deleting expired quotes $this->cleanExpiredQuotes->execute(); - $totalCount = $this->quoteRepository->getList($searchCriteria)->getTotalCount(); + //Only 1 will be deleted for the store that has all of them expired by config (default_store) - $this->assertEquals( - 1, - $totalCount - ); + $this->assertQuotesCount(1); + } + + /** + * Check if outdated quotes are deleted. + * + * @magentoConfigFixture default_store checkout/cart/delete_quote_after -365 + * @magentoDataFixture Magento/Sales/_files/quotes_big_amount.php + */ + public function testExecuteWithBigAmountOfQuotes() + { + //Initial count - should be equal to 1000 + $this->assertQuotesCount(1000); + + //Deleting expired quotes + $this->cleanExpiredQuotes->execute(); + + //There should be no quotes anymore + $this->assertQuotesCount(0); + } + + /** + * Optimized assert quotes count + * Uses collection getSize in order to get quick result + * + * @param int $expected + */ + private function assertQuotesCount(int $expected): void + { + $totalCount = $this->quoteCollectionFactory->create()->getSize(); + $this->assertEquals($expected, $totalCount); } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quotes_big_amount.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quotes_big_amount.php new file mode 100644 index 0000000000000..c666f938d9125 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quotes_big_amount.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\Config; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\QuoteRepository; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreRepository; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var $objectManager ObjectManager */ +$objectManager = Bootstrap::getObjectManager(); +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = $objectManager->get(QuoteFactory::class); +/** @var QuoteRepository $quoteRepository */ +$quoteRepository = $objectManager->get(QuoteRepository::class); +/** @var StoreRepository $storeRepository */ +$storeRepository = $objectManager->get(StoreRepository::class); +/** @var Config $appConfig */ +$appConfig = $objectManager->get(Config::class); +$appConfig->clean(); + +/** @var Store $defaultStore */ +$defaultStore = $storeRepository->getActiveStoreByCode('default'); + +for ($i = 0; $i < 1000; $i++) { + /** @var Quote $quote */ + $quote = $quoteFactory->create(); + $quote->setStoreId($defaultStore->getId()); + $quoteRepository->save($quote); +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/quotes_big_amount_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/quotes_big_amount_rollback.php new file mode 100644 index 0000000000000..24832a297949d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/quotes_big_amount_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Registry; +use Magento\Quote\Model\QuoteRepository; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var QuoteRepository $quoteRepository */ +$quoteRepository = $objectManager->get(QuoteRepository::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +$searchCriteria = $searchCriteriaBuilder->create(); +$items = $quoteRepository->getList($searchCriteria) + ->getItems(); +foreach ($items as $item) { + $quoteRepository->delete($item); +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php index 84ee7d8984cc4..cab007aa6af9c 100644 --- a/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php +++ b/dev/tests/integration/testsuite/Magento/Wishlist/Model/WishlistTest.php @@ -217,6 +217,27 @@ public function testUpdateItemQtyInWishList(): void $this->assertEquals(55, $updatedItem->getQty()); } + /** + * Update description of wishlist item + * + * @magentoDataFixture Magento/Wishlist/_files/wishlist.php + * + * @return void + */ + public function testUpdateItemDescriptionInWishList(): void + { + $itemDescription = 'Test Description'; + $wishlist = $this->getWishlistByCustomerId->execute(1); + $item = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $item->setDescription($itemDescription); + $this->assertNotNull($item); + $buyRequest = $this->dataObjectFactory->create(['data' => ['qty' => 55]]); + $wishlist->updateItem($item, $buyRequest); + $updatedItem = $this->getWishlistByCustomerId->getItemBySku(1, 'simple'); + $this->assertEquals(55, $updatedItem->getQty()); + $this->assertEquals($itemDescription, $updatedItem->getDescription()); + } + /** * @return void */ diff --git a/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js b/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js index 52374a24e8c68..c54473d4d757f 100644 --- a/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js +++ b/dev/tests/js/jasmine/tests/lib/mage/requirejs/mixins.test.js @@ -183,5 +183,31 @@ define(['rjsResolver', 'mixins'], function (resolver, mixins) { require([name], function () {}); }); + + it('applies mixins for modules that have no dependencies', function (done) { + var name = 'tests/assets/mixins/mixins-applied-no-dependencies', + mixinName = 'tests/assets/mixins/mixins-applied-no-dependencies-ext'; + + mixins.hasMixins.and.returnValue(true); + mixins.getMixins.and.returnValue([mixinName]); + + define(name, { + value: 'original' + }); + + define(mixinName, [], function () { + return function (module) { + module.value = 'changed'; + + return module; + }; + }); + + require([name], function (module) { + expect(module.value).toBe('changed'); + + done(); + }); + }); }); }); diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt index 1bb13d553c754..a9c65eb29c2e5 100644 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt +++ b/dev/tests/static/testsuite/Magento/Test/Php/_files/phpstan/blacklist/common.txt @@ -3,9 +3,10 @@ # Example: # app/code/Magento/Catalog # dev/tests/static/framework/bootstrap.php -lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php lib/internal/Magento/Framework/Cache/Backend/Eaccelerator.php lib/internal/Magento/Framework/Image/Adapter/ImageMagick.php +lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/InterceptorTest.php +lib/internal/Magento/Framework/Interception/Test/Unit/Config/ConfigTest.php dev/tests/integration/framework/deployTestModules.php dev/tests/integration/testsuite/Magento/Framework/Filter/DirectiveProcessor/SimpleDirectiveTest.php dev/tests/integration/testsuite/Magento/Framework/Session/ConfigTest.php diff --git a/lib/internal/Magento/Framework/Exception/InvalidArgumentException.php b/lib/internal/Magento/Framework/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000000..a92308ec41bde --- /dev/null +++ b/lib/internal/Magento/Framework/Exception/InvalidArgumentException.php @@ -0,0 +1,14 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Exception; + +/** + * An exception to be thrown when arguments are invalidated + */ +class InvalidArgumentException extends LocalizedException +{ +} diff --git a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php index 69a0d3029e18d..4a3ed34810723 100644 --- a/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php +++ b/lib/internal/Magento/Framework/Interception/Code/Generator/Interceptor.php @@ -7,17 +7,11 @@ namespace Magento\Framework\Interception\Code\Generator; -/** - * Class Interceptor - ˚* - * @package Magento\Framework\Interception\Code\Generator - */ -class Interceptor extends \Magento\Framework\Code\Generator\EntityAbstract +use Magento\Framework\Code\Generator\EntityAbstract; + +class Interceptor extends EntityAbstract { - /** - * Entity type - */ - const ENTITY_TYPE = 'interceptor'; + public const ENTITY_TYPE = 'interceptor'; /** * Returns default result class name @@ -52,9 +46,8 @@ protected function _getDefaultConstructorDefinition() $parameters = []; $body = "\$this->___init();\n"; if ($constructor) { - foreach ($constructor->getParameters() as $parameter) { - $parameters[] = $this->_getMethodParameterInfo($parameter); - } + $parameters = array_map([$this, '_getMethodParameterInfo'], $constructor->getParameters()); + $body .= count($parameters) ? "parent::__construct({$this->_getParameterList($parameters)});" : "parent::__construct();"; @@ -70,7 +63,7 @@ protected function _getDefaultConstructorDefinition() /** * Returns list of methods for class generator * - * @return mixed + * @return array */ protected function _getClassMethods() { @@ -107,10 +100,7 @@ protected function isInterceptedMethod(\ReflectionMethod $method) */ protected function _getMethodInfo(\ReflectionMethod $method) { - $parameters = []; - foreach ($method->getParameters() as $parameter) { - $parameters[] = $this->_getMethodParameterInfo($parameter); - } + $parameters = array_map([$this, '_getMethodParameterInfo'], $method->getParameters()); $returnTypeValue = $this->getReturnTypeValue($method); $methodInfo = [ @@ -118,22 +108,18 @@ protected function _getMethodInfo(\ReflectionMethod $method) 'parameters' => $parameters, 'body' => str_replace( [ - '%methodName%', + '%method%', '%return%', '%parameters%' ], [ $method->getName(), - $returnTypeValue === 'void' ? '' : ' return', + $returnTypeValue === 'void' ? '' : 'return ', $this->_getParameterList($parameters) ], <<<'METHOD_BODY' -$pluginInfo = $this->pluginList->getNext($this->subjectType, '%methodName%'); -if (!$pluginInfo) { - %return% parent::%methodName%(%parameters%); -} else { - %return% $this->___callPlugins('%methodName%', func_get_args(), $pluginInfo); -} +$pluginInfo = $this->pluginList->getNext($this->subjectType, '%method%'); +%return%$pluginInfo ? $this->___callPlugins('%method%', func_get_args(), $pluginInfo) : parent::%method%(%parameters%); METHOD_BODY ), 'returnType' => $returnTypeValue, @@ -206,11 +192,7 @@ protected function _validateData() if ($resultClassName !== $sourceClassName . '\\Interceptor') { $this->_addError( - 'Invalid Interceptor class name [' . - $resultClassName . - ']. Use ' . - $sourceClassName . - '\\Interceptor' + 'Invalid Interceptor class name ' . $resultClassName . '. Use ' . $sourceClassName . '\\Interceptor' ); $result = false; } diff --git a/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php b/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php index 4609fe09f1f0d..b67af21878c3d 100644 --- a/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php +++ b/lib/internal/Magento/Framework/Interception/Code/InterfaceValidator.php @@ -5,30 +5,32 @@ */ namespace Magento\Framework\Interception\Code; +use Magento\Framework\Code\Reader\ArgumentsReader; use Magento\Framework\Exception\ValidatorException; use Magento\Framework\Phrase; +/** + * @SuppressWarnings(PHPMD.NPathComplexity) + */ class InterfaceValidator { - const METHOD_BEFORE = 'before'; - - const METHOD_AROUND = 'around'; - - const METHOD_AFTER = 'after'; + public const METHOD_BEFORE = 'before'; + public const METHOD_AROUND = 'around'; + public const METHOD_AFTER = 'after'; /** * Arguments reader model * - * @var \Magento\Framework\Code\Reader\ArgumentsReader + * @var ArgumentsReader */ protected $_argumentsReader; /** - * @param \Magento\Framework\Code\Reader\ArgumentsReader $argumentsReader + * @param ArgumentsReader $argumentsReader */ - public function __construct(\Magento\Framework\Code\Reader\ArgumentsReader $argumentsReader = null) + public function __construct(ArgumentsReader $argumentsReader = null) { - $this->_argumentsReader = $argumentsReader ?: new \Magento\Framework\Code\Reader\ArgumentsReader(); + $this->_argumentsReader = $argumentsReader ?? new ArgumentsReader(); } /** @@ -50,7 +52,7 @@ public function validate($pluginClass, $interceptedType) $type = new \ReflectionClass($interceptedType); foreach ($plugin->getMethods(\ReflectionMethod::IS_PUBLIC) as $pluginMethod) { - /** @var $pluginMethod \ReflectionMethod */ + /** @var \ReflectionMethod $pluginMethod */ $originMethodName = $this->getOriginMethodName($pluginMethod->getName()); if ($originMethodName === null) { continue; @@ -63,19 +65,11 @@ public function validate($pluginClass, $interceptedType) ) ); } - $originMethod = $type->getMethod($originMethodName); $pluginMethodParameters = $this->getMethodParameters($pluginMethod); - $originMethodParameters = $this->getMethodParameters($originMethod); - - $methodType = $this->getMethodType($pluginMethod->getName()); - $subject = array_shift($pluginMethodParameters); - if (!$this->_argumentsReader->isCompatibleType( - $subject['type'], - $interceptedType - ) || $subject['type'] === null - ) { + if ($subject['type'] === null + || !$this->_argumentsReader->isCompatibleType($subject['type'], $interceptedType)) { throw new ValidatorException( new Phrase( 'Invalid [%1] $%2 type in %3::%4. It must be compatible with %5', @@ -84,45 +78,50 @@ public function validate($pluginClass, $interceptedType) ); } - switch ($methodType) { - case self::METHOD_BEFORE: - $this->validateMethodsParameters( - $pluginMethodParameters, - $originMethodParameters, - $pluginClass, - $pluginMethod->getName() - ); - break; - case self::METHOD_AROUND: - $proceed = array_shift($pluginMethodParameters); - if (!$this->_argumentsReader->isCompatibleType($proceed['type'], '\\Closure')) { - throw new ValidatorException( - new Phrase( - 'Invalid [%1] $%2 type in %3::%4. It must be compatible with \\Closure', - [$proceed['type'], $proceed['name'], $pluginClass, $pluginMethod->getName()] - ) - ); - } - $this->validateMethodsParameters( - $pluginMethodParameters, - $originMethodParameters, - $pluginClass, - $pluginMethod->getName() + $originMethod = $type->getMethod($originMethodName); + $originMethodParameters = $this->getMethodParameters($originMethod); + $methodType = $this->getMethodType($pluginMethod->getName()); + + if (self::METHOD_AFTER === $methodType && count($pluginMethodParameters) > 1) { + // remove result + array_shift($pluginMethodParameters); + $matchedParameters = array_intersect_key($originMethodParameters, $pluginMethodParameters); + $this->validateMethodsParameters( + $pluginMethodParameters, + $matchedParameters, + $pluginClass, + $pluginMethod->getName() + ); + continue; + } + + if (self::METHOD_BEFORE === $methodType) { + $this->validateMethodsParameters( + $pluginMethodParameters, + $originMethodParameters, + $pluginClass, + $pluginMethod->getName() + ); + continue; + } + + if (self::METHOD_AROUND === $methodType) { + $proceed = array_shift($pluginMethodParameters); + if (!$this->_argumentsReader->isCompatibleType($proceed['type'], '\\Closure')) { + throw new ValidatorException( + new Phrase( + 'Invalid [%1] $%2 type in %3::%4. It must be compatible with \\Closure', + [$proceed['type'], $proceed['name'], $pluginClass, $pluginMethod->getName()] + ) ); - break; - case self::METHOD_AFTER: - if (count($pluginMethodParameters) > 1) { - // remove result - array_shift($pluginMethodParameters); - $matchedParameters = array_intersect_key($originMethodParameters, $pluginMethodParameters); - $this->validateMethodsParameters( - $pluginMethodParameters, - $matchedParameters, - $pluginClass, - $pluginMethod->getName() - ); - } - break; + } + $this->validateMethodsParameters( + $pluginMethodParameters, + $originMethodParameters, + $pluginClass, + $pluginMethod->getName() + ); + continue; } } } @@ -170,8 +169,7 @@ protected function validateMethodsParameters(array $pluginParameters, array $ori protected function getParametersType(\ReflectionParameter $parameter) { $parameterClass = $parameter->getClass(); - $type = $parameterClass ? '\\' . $parameterClass->getName() : ($parameter->isArray() ? 'array' : null); - return $type; + return $parameterClass ? '\\' . $parameterClass->getName() : ($parameter->isArray() ? 'array' : null); } /** @@ -183,17 +181,16 @@ protected function getParametersType(\ReflectionParameter $parameter) */ protected function getOriginMethodName($pluginMethodName) { - switch ($this->getMethodType($pluginMethodName)) { - case self::METHOD_BEFORE: - case self::METHOD_AROUND: - return lcfirst(substr($pluginMethodName, 6)); + $methodType = $this->getMethodType($pluginMethodName); - case self::METHOD_AFTER: - return lcfirst(substr($pluginMethodName, 5)); - - default: - return null; + if (self::METHOD_AFTER === $methodType) { + return lcfirst(substr($pluginMethodName, 5)); + } + if (self::METHOD_BEFORE === $methodType || self::METHOD_AROUND === $methodType) { + return lcfirst(substr($pluginMethodName, 6)); } + + return null; } /** @@ -205,12 +202,14 @@ protected function getOriginMethodName($pluginMethodName) */ protected function getMethodType($pluginMethodName) { - if (substr($pluginMethodName, 0, 6) == self::METHOD_BEFORE) { + if (0 === strpos($pluginMethodName, self::METHOD_AFTER)) { + return self::METHOD_AFTER; + } + if (0 === strpos($pluginMethodName, self::METHOD_BEFORE)) { return self::METHOD_BEFORE; - } elseif (substr($pluginMethodName, 0, 6) == self::METHOD_AROUND) { + } + if (0 === strpos($pluginMethodName, self::METHOD_AROUND)) { return self::METHOD_AROUND; - } elseif (substr($pluginMethodName, 0, 5) == self::METHOD_AFTER) { - return self::METHOD_AFTER; } return null; diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/Interceptor.txt b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/Interceptor.txt index fe9b4dad7022e..87646be998c0a 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/Interceptor.txt +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/Interceptor.txt @@ -18,11 +18,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\Sample public function getValue() { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'getValue'); - if (!$pluginInfo) { - return parent::getValue(); - } else { - return $this->___callPlugins('getValue', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('getValue', func_get_args(), $pluginInfo) : parent::getValue(); } /** @@ -31,11 +27,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\Sample public function setValue($value) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'setValue'); - if (!$pluginInfo) { - return parent::setValue($value); - } else { - return $this->___callPlugins('setValue', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('setValue', func_get_args(), $pluginInfo) : parent::setValue($value); } /** @@ -44,11 +36,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\Sample public function & getReference() { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'getReference'); - if (!$pluginInfo) { - return parent::getReference(); - } else { - return $this->___callPlugins('getReference', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('getReference', func_get_args(), $pluginInfo) : parent::getReference(); } /** @@ -57,11 +45,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\Sample public function firstVariadicParameter(... $variadicValue) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'firstVariadicParameter'); - if (!$pluginInfo) { - return parent::firstVariadicParameter(... $variadicValue); - } else { - return $this->___callPlugins('firstVariadicParameter', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('firstVariadicParameter', func_get_args(), $pluginInfo) : parent::firstVariadicParameter(... $variadicValue); } /** @@ -70,11 +54,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\Sample public function secondVariadicParameter($value, ... $variadicValue) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'secondVariadicParameter'); - if (!$pluginInfo) { - return parent::secondVariadicParameter($value, ... $variadicValue); - } else { - return $this->___callPlugins('secondVariadicParameter', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('secondVariadicParameter', func_get_args(), $pluginInfo) : parent::secondVariadicParameter($value, ... $variadicValue); } /** @@ -83,10 +63,6 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\Sample public function byRefVariadic(&... $variadicValue) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'byRefVariadic'); - if (!$pluginInfo) { - return parent::byRefVariadic(... $variadicValue); - } else { - return $this->___callPlugins('byRefVariadic', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('byRefVariadic', func_get_args(), $pluginInfo) : parent::byRefVariadic(... $variadicValue); } } diff --git a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/TInterceptor.txt b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/TInterceptor.txt index 71ea5b0475cfc..5ff09d204c5ef 100644 --- a/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/TInterceptor.txt +++ b/lib/internal/Magento/Framework/Interception/Test/Unit/Code/Generator/_files/TInterceptor.txt @@ -18,11 +18,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function returnVoid() : void { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'returnVoid'); - if (!$pluginInfo) { - parent::returnVoid(); - } else { - $this->___callPlugins('returnVoid', func_get_args(), $pluginInfo); - } + $pluginInfo ? $this->___callPlugins('returnVoid', func_get_args(), $pluginInfo) : parent::returnVoid(); } /** @@ -31,11 +27,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function getNullableValue() : ?string { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'getNullableValue'); - if (!$pluginInfo) { - return parent::getNullableValue(); - } else { - return $this->___callPlugins('getNullableValue', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('getNullableValue', func_get_args(), $pluginInfo) : parent::getNullableValue(); } /** @@ -44,11 +36,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function getValue() : string { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'getValue'); - if (!$pluginInfo) { - return parent::getValue(); - } else { - return $this->___callPlugins('getValue', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('getValue', func_get_args(), $pluginInfo) : parent::getValue(); } /** @@ -57,11 +45,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function setValue(string $value) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'setValue'); - if (!$pluginInfo) { - return parent::setValue($value); - } else { - return $this->___callPlugins('setValue', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('setValue', func_get_args(), $pluginInfo) : parent::setValue($value); } /** @@ -70,11 +54,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function typeHintedFirstVariadicParameter(string ... $variadicValue) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'typeHintedFirstVariadicParameter'); - if (!$pluginInfo) { - return parent::typeHintedFirstVariadicParameter(... $variadicValue); - } else { - return $this->___callPlugins('typeHintedFirstVariadicParameter', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('typeHintedFirstVariadicParameter', func_get_args(), $pluginInfo) : parent::typeHintedFirstVariadicParameter(... $variadicValue); } /** @@ -83,11 +63,7 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function typeHintedSecondVariadicParameter(string $value, string ... $variadicValue) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'typeHintedSecondVariadicParameter'); - if (!$pluginInfo) { - return parent::typeHintedSecondVariadicParameter($value, ... $variadicValue); - } else { - return $this->___callPlugins('typeHintedSecondVariadicParameter', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('typeHintedSecondVariadicParameter', func_get_args(), $pluginInfo) : parent::typeHintedSecondVariadicParameter($value, ... $variadicValue); } /** @@ -96,10 +72,6 @@ class Interceptor extends \Magento\Framework\Interception\Code\Generator\TSample public function byRefTypeHintedVariadic(string &... $variadicValue) { $pluginInfo = $this->pluginList->getNext($this->subjectType, 'byRefTypeHintedVariadic'); - if (!$pluginInfo) { - return parent::byRefTypeHintedVariadic(... $variadicValue); - } else { - return $this->___callPlugins('byRefTypeHintedVariadic', func_get_args(), $pluginInfo); - } + return $pluginInfo ? $this->___callPlugins('byRefTypeHintedVariadic', func_get_args(), $pluginInfo) : parent::byRefTypeHintedVariadic(... $variadicValue); } } diff --git a/lib/web/mage/gallery/gallery.js b/lib/web/mage/gallery/gallery.js index 02ba72f64127b..6309fa267ea93 100644 --- a/lib/web/mage/gallery/gallery.js +++ b/lib/web/mage/gallery/gallery.js @@ -270,7 +270,10 @@ define([ next: $t('Next'), previous: $t('Previous') }), - mainImageIndex; + mainImageIndex, + $element = settings.$element, + $fotoramaElement, + $fotoramaStage; if (settings.breakpoints) { _.each(_.values(settings.breakpoints), function (breakpoint) { @@ -285,19 +288,38 @@ define([ settings.breakpoints = breakpoints; } - _.extend(config, config.options); - config.options = undefined; - - config.click = false; - config.breakpoints = null; + _.extend(config, config.options, + { + options: undefined, + click: false, + breakpoints: null + } + ); settings.currentConfig = config; - settings.$element.css('min-height', settings.$element.height()); - settings.$element.html(tpl); - settings.$element.removeClass('_block-content-loading'); - settings.$elementF = $(settings.$element.children()[0]); - settings.$elementF.fotorama(config); - settings.$element.css('min-height', ''); - settings.fotoramaApi = settings.$elementF.data('fotorama'); + + $element + .css('min-height', settings.$element.height()) + .append(tpl); + + $fotoramaElement = $element.find('[data-gallery-role="gallery"]'); + + $fotoramaStage = $fotoramaElement.find('.fotorama__stage'); + $fotoramaStage.css('position', 'absolute'); + + $fotoramaElement.fotorama(config); + $fotoramaElement.find('.fotorama__stage__frame.fotorama__active') + .one('f:load', function () { + // Remove placeholder when main gallery image loads. + $element.find('.gallery-placeholder__image').remove(); + $element + .removeClass('_block-content-loading') + .css('min-height', ''); + + $fotoramaStage.css('position', ''); + }); + settings.$elementF = $fotoramaElement; + settings.fotoramaApi = $fotoramaElement.data('fotorama'); + $.extend(true, config, this.startConfig); mainImageIndex = getMainImageIndex(config.data); diff --git a/lib/web/mage/gallery/gallery.less b/lib/web/mage/gallery/gallery.less index 10fba50fe239b..86ccdb858bf19 100644 --- a/lib/web/mage/gallery/gallery.less +++ b/lib/web/mage/gallery/gallery.less @@ -395,6 +395,11 @@ position: absolute; top: 0; width: @fotorama-arw-size; + + ._block-content-loading & { + opacity: 0; + } + .fotorama__arr__arr { &:extend(.fotorama-sprite); .fotorama-abs-center(); diff --git a/lib/web/mage/polyfill.js b/lib/web/mage/polyfill.js index 94232b29404fa..478b6544bd379 100644 --- a/lib/web/mage/polyfill.js +++ b/lib/web/mage/polyfill.js @@ -1,19 +1,21 @@ -try { - if (!window.localStorage || !window.sessionStorage) { - throw new Error(); - } +(function (root, doc) { + 'use strict'; + + var Storage; - localStorage.setItem('storage_test', 1); - localStorage.removeItem('storage_test'); -} catch (e) { - (function () { - 'use strict'; + try { + if (!root.localStorage || !root.sessionStorage) { + throw new Error(); + } + localStorage.setItem('storage_test', 1); + localStorage.removeItem('storage_test'); + } catch (e) { /** * Returns a storage object to shim local or sessionStorage * @param {String} type - either 'local' or 'session' */ - var Storage = function (type) { + Storage = function (type) { var data; /** @@ -32,7 +34,7 @@ try { } else { expires = ''; } - document.cookie = name + '=' + value + expires + '; path=/'; + doc.cookie = name + '=' + value + expires + '; path=/'; } /** @@ -41,7 +43,7 @@ try { */ function readCookie(name) { var nameEQ = name + '=', - ca = document.cookie.split(';'), + ca = doc.cookie.split(';'), i = 0, c; @@ -70,11 +72,11 @@ try { return 'localstorage'; } - if (!window.name) { - window.name = new Date().getTime(); + if (!root.name) { + root.name = new Date().getTime(); } - return 'sessionStorage' + window.name; + return 'sessionStorage' + root.name; } /** @@ -170,7 +172,7 @@ try { }; }; - window.localStorage.prototype = window.localStorage = new Storage('local'); - window.sessionStorage.prototype = window.sessionStorage = new Storage('session'); - })(); -} + root.localStorage.prototype = root.localStorage = new Storage('local'); + root.sessionStorage.prototype = root.sessionStorage = new Storage('session'); + } +})(window, document); diff --git a/lib/web/mage/requirejs/mixins.js b/lib/web/mage/requirejs/mixins.js index 613605038f4b9..cb5ee8e462054 100644 --- a/lib/web/mage/requirejs/mixins.js +++ b/lib/web/mage/requirejs/mixins.js @@ -232,9 +232,12 @@ require([ * from it every time require call happens. */ defContext.defQueue.shift = function () { - var queueItem = Array.prototype.shift.call(this); + var queueItem = Array.prototype.shift.call(this), + lastDeps = queueItem && queueItem[1]; - queueItem[1] = processNames(queueItem[1], defContext); + if (Array.isArray(lastDeps)) { + queueItem[1] = processNames(queueItem[1], defContext); + } return queueItem; };