diff --git a/app/code/Magento/Backend/view/adminhtml/web/js/translate.js b/app/code/Magento/Backend/view/adminhtml/web/js/translate.js index d6e1547600c4e..eae1394c15027 100644 --- a/app/code/Magento/Backend/view/adminhtml/web/js/translate.js +++ b/app/code/Magento/Backend/view/adminhtml/web/js/translate.js @@ -35,7 +35,7 @@ define([ * @return {String} */ this.translate = function (text) { - return _data[text] ? _data[text] : text; + return typeof _data[text] === 'string' ? _data[text] : text; }; return this; diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php index ebe04fb63b217..f477c11896688 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPrice.php @@ -15,6 +15,13 @@ use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Framework\Search\Request\IndexScopeResolverInterface; +/** + * Class LinkedProductSelectBuilderByIndexPrice + * + * Provide Select object for retrieve product id by index price. + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class LinkedProductSelectBuilderByIndexPrice implements LinkedProductSelectBuilderInterface { /** @@ -83,13 +90,13 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); - $websiteId = $this->storeManager->getStore()->getWebsiteId(); + $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); $customerGroupId = $this->customerSession->getCustomerGroupId(); $priceSelect = $this->resource->getConnection()->select() diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php index 841fe17bdcf05..77e886f12723c 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php @@ -74,7 +74,7 @@ public function __construct( /** * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $priceAttribute = $this->eavConfig->getAttribute(Product::ENTITY, 'price'); @@ -104,7 +104,7 @@ public function build($productId) if (!$this->catalogHelper->isPriceGlobal()) { $priceSelectStore = clone $priceSelect; - $priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId()); + $priceSelectStore->where('t.store_id = ?', $storeId); $selects[] = $priceSelectStore; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php index 5c47185a85bf4..ef0eeca10502a 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php @@ -12,6 +12,10 @@ use Magento\Store\Model\Store; /** + * LinkedProductSelectBuilderBySpecialPrice + * + * Provide Select object for retrieve product id by special price + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class LinkedProductSelectBuilderBySpecialPrice implements LinkedProductSelectBuilderInterface @@ -88,16 +92,16 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $connection = $this->resource->getConnection(); $specialPriceAttribute = $this->eavConfig->getAttribute(Product::ENTITY, 'special_price'); $specialPriceFromDate = $this->eavConfig->getAttribute(Product::ENTITY, 'special_from_date'); $specialPriceToDate = $this->eavConfig->getAttribute(Product::ENTITY, 'special_to_date'); - $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore()); + $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore($storeId)); $currentDate = $this->dateTime->formatDate($timestamp, false); $productTable = $this->resource->getTableName('catalog_product_entity'); @@ -145,7 +149,7 @@ public function build($productId) if (!$this->catalogHelper->isPriceGlobal()) { $priceSelectStore = clone $specialPrice; - $priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId()); + $priceSelectStore->where('t.store_id = ?', $storeId); $selects[] = $priceSelectStore; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php index 37281193d6a1b..f104f92ea86c5 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php @@ -9,10 +9,19 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; +/** + * LinkedProductSelectBuilderByTierPrice + * + * Provide Select object for retrieve product id by tier price + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) + */ class LinkedProductSelectBuilderByTierPrice implements LinkedProductSelectBuilderInterface { /** * Default website id + * + * Constant represents default website id */ const DEFAULT_WEBSITE_ID = 0; @@ -72,9 +81,9 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); @@ -103,7 +112,7 @@ public function build($productId) if (!$this->catalogHelper->isPriceGlobal()) { $priceSelectStore = clone $priceSelect; - $priceSelectStore->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()); + $priceSelectStore->where('t.website_id = ?', $this->storeManager->getStore($storeId)->getWebsiteId()); $selects[] = $priceSelectStore; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php index 5782834c06d85..17ca389777c5b 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php @@ -6,6 +6,9 @@ namespace Magento\Catalog\Model\ResourceModel\Product; +/** + * Collect Select object for list of products + */ class LinkedProductSelectBuilderComposite implements LinkedProductSelectBuilderInterface { /** @@ -22,14 +25,15 @@ public function __construct($linkedProductSelectBuilder) } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { $selects = []; foreach ($this->linkedProductSelectBuilder as $productSelectBuilder) { - $selects = array_merge($selects, $productSelectBuilder->build($productId)); + $selects[] = $productSelectBuilder->build($productId, $storeId); } + $selects = array_merge(...$selects); return $selects; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php index 3b870ed61f36d..3411c124bf5ce 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderInterface.php @@ -11,8 +11,11 @@ interface LinkedProductSelectBuilderInterface { /** + * Build Select objects + * * @param int $productId + * @param int $storeId * @return \Magento\Framework\DB\Select[] */ - public function build($productId); + public function build(int $productId, int $storeId) : array; } diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml index 7107cc2a560d1..cf2e809fefa5e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyActionGroup.xml @@ -16,7 +16,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml index 42813aef05be5..a65bb297971c7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/ChangeSeoUrlKeyForSubCategoryActionGroup.xml @@ -16,7 +16,7 @@ - + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectDropDownOptionValueActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectDropDownOptionValueActionGroup.xml new file mode 100644 index 0000000000000..31b18e1f0d37e --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectDropDownOptionValueActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + Selects the provided Product Option Value under the provided DropDown Product Option Title on a Storefront Product page. + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectRadioButtonOptionValueActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectRadioButtonOptionValueActionGroup.xml new file mode 100644 index 0000000000000..5f2a130956cbe --- /dev/null +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageSelectRadioButtonOptionValueActionGroup.xml @@ -0,0 +1,22 @@ + + + + + + + Selects the provided Product Option Value under the provided Radio Button Product Option Title on a Storefront Product page. + + + + + + + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml index abf01f00dbbcc..be04c297cec25 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml @@ -1,63 +1,72 @@ - - - - - - GridPerPageValues - RememberCategoryPagination - - - - 9,12,20,24 - - - - 1 - - - - DefaultCatalogStorefrontFlagZero - DefaultListAllowAll - DefaultFlatCatalogProduct - - - - 0 - - - - 0 - - - - 0 - - - - UseFlatCatalogProduct - UseFlatCatalogCategory - - - - 1 - - - - 1 - - - - DefaultFlatCatalogProduct - DefaultFlatCatalogCategory - - - - 0 - - + + + + + + GridPerPageValues + RememberCategoryPagination + + + + 9,12,20,24 + + + + 1 + + + + DefaultCatalogStorefrontFlagZero + DefaultListAllowAll + DefaultFlatCatalogProduct + + + + 0 + + + + 0 + + + + 0 + + + + UseFlatCatalogProduct + UseFlatCatalogCategory + + + + 1 + + + + 1 + + + + DefaultFlatCatalogProduct + DefaultFlatCatalogCategory + + + + 0 + + + + catalog/frontend/grid_per_page_values + 12,24,36 + + + catalog/frontend/grid_per_page_values + 1,2 + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml index b5d5d61f6468b..bde7a94662d04 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCategorySEOSection.xml @@ -9,7 +9,8 @@
- + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml index 7ce795c78f25b..09eb4ad954274 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontCategoryBottomToolbarSection.xml @@ -12,5 +12,6 @@ +
diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index 631649e33b0fd..a5a02ad95b1f7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -62,6 +62,7 @@ + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 976f714d7b3e1..4c3d519106389 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -17,6 +17,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml index 423b7b6224b23..f178d55b97fca 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveAnchoredCategoryTest.xml @@ -16,6 +16,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index 10347584b4cda..04110dbd73a4c 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -17,6 +17,9 @@ + + + diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php index 6f3d8e1a84b17..945c61f44e99c 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/Indexer/LinkedProductSelectBuilderByIndexPriceTest.php @@ -37,6 +37,21 @@ class LinkedProductSelectBuilderByIndexPriceTest extends \PHPUnit\Framework\Test */ private $baseSelectProcessorMock; + /** + * @var \Magento\Framework\Search\Request\IndexScopeResolverInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private $indexScopeResolverMock; + + /** + * @var \Magento\Framework\Indexer\Dimension|\PHPUnit\Framework\MockObject\MockObject + */ + private $dimensionMock; + + /** + * @var \Magento\Framework\Indexer\DimensionFactory|\PHPUnit\Framework\MockObject\MockObject + */ + private $dimensionFactoryMock; + /** * @var \Magento\Catalog\Model\ResourceModel\Product\Indexer\LinkedProductSelectBuilderByIndexPrice */ @@ -85,6 +100,7 @@ protected function setUp() public function testBuild() { $productId = 10; + $storeId = 1; $metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadataInterface::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); @@ -108,6 +124,6 @@ public function testBuild() $metadata->expects($this->once())->method('getLinkField')->willReturn('row_id'); $this->resourceMock->expects($this->any())->method('getTableName'); $this->baseSelectProcessorMock->expects($this->once())->method('process')->willReturnSelf(); - $this->model->build($productId); + $this->model->build($productId, $storeId); } } diff --git a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php index 502d9532e8a05..e67568b80898e 100644 --- a/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php +++ b/app/code/Magento/CatalogInventory/Model/Quote/Item/QuantityValidator.php @@ -222,9 +222,11 @@ private function checkOptionsQtyIncrements(Item $quoteItem, array $options): voi { $removeErrors = true; foreach ($options as $option) { + $optionValue = $option->getValue(); + $optionQty = $quoteItem->getData('qty') * $optionValue; $result = $this->stockState->checkQtyIncrements( $option->getProduct()->getId(), - $quoteItem->getData('qty'), + $optionQty, $option->getProduct()->getStore()->getWebsiteId() ); if ($result->getHasError()) { diff --git a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php index 48c463fc18b80..aaafc4ecd4c5d 100644 --- a/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php +++ b/app/code/Magento/CatalogRule/Model/ResourceModel/Product/LinkedProductSelectBuilderByCatalogRulePrice.php @@ -86,9 +86,9 @@ public function __construct( /** * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { - $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore()); + $timestamp = $this->localeDate->scopeTimeStamp($this->storeManager->getStore($storeId)); $currentDate = $this->dateTime->formatDate($timestamp, false); $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField(); $productTable = $this->resource->getTableName('catalog_product_entity'); @@ -108,7 +108,7 @@ public function build($productId) sprintf('t.product_id = %s.%s', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS, $linkField), [] )->where('parent.entity_id = ?', $productId) - ->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId()) + ->where('t.website_id = ?', $this->storeManager->getStore($storeId)->getWebsiteId()) ->where('t.customer_group_id = ?', $this->customerSession->getCustomerGroupId()) ->where('t.rule_date = ?', $currentDate) ->order('t.rule_price ' . Select::SQL_ASC) diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleDeleteAllActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleDeleteAllActionGroup.xml new file mode 100644 index 0000000000000..5860137c1ab8d --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleDeleteAllActionGroup.xml @@ -0,0 +1,41 @@ + + + + + + Open Catalog Price Rule grid and delete all rules one by one. Need to avoid interference with other tests that test catalog price rules. + + + + + + + + + + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillActionsActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillActionsActionGroup.xml new file mode 100644 index 0000000000000..da8571769ef31 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillActionsActionGroup.xml @@ -0,0 +1,27 @@ + + + + + + Fill Catalog Price Rule actions fields: Apply, Discount Amount, Discard subsequent rules. + + + + + + + + + + + + + + + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillMainInfoActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillMainInfoActionGroup.xml new file mode 100644 index 0000000000000..e609550d19461 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleFillMainInfoActionGroup.xml @@ -0,0 +1,34 @@ + + + + + + Fill Catalog Price Rule main info fields: Name, Description, Active (1/0), Priority. + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml new file mode 100644 index 0000000000000..84cc7b862ef7c --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSaveAndApplyActionGroup.xml @@ -0,0 +1,21 @@ + + + + + + Clicks Save and Apply on a Admin Catalog Price Rule creation/edit page. Validates that the Success Message is present. Validates that applied rules success message is present. + + + + + + + + + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSelectCustomerGroupsActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSelectCustomerGroupsActionGroup.xml new file mode 100644 index 0000000000000..8c37325aff722 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminCatalogPriceRuleSelectCustomerGroupsActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + Fill Catalog Price Rule customer groups multiselect on new/edit page. + + + + + + + + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml index 82e7a6979e34b..9ad27d22caba6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminSaveAndApplyRulesActionGroup.xml @@ -10,9 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Clicks Save on a Admin Catalog Price Rule creation/edit page. Validates that the Success Message is present. Clicks Apply Rules. Validates that the Success Message is present. + DEPRECATED. Please use AdminCatalogPriceRuleSaveAndApplyActionGroup instead. Clicks Save on a Admin Catalog Price Rule creation/edit page. Validates that the Success Message is present. Clicks Apply Rules. Validates that the Success Message is present. - + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml index cd2f7a207a3e2..b6fc92e1a1df6 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogSelectCustomerGroupActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - Selects the provided Customer Group Name on the Admin Catalog Price Rule creation/edit page. + DEPRECATED. Please use AdminCatalogPriceRuleSelectCustomerGroupsActionGroup instead. Selects the provided Customer Group Name on the Admin Catalog Price Rule creation/edit page. diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml index bdc09c56353df..732aee0ad63d7 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup.xml @@ -18,8 +18,8 @@ - - + + diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml index 7d375da6dfb65..be0fdb2e0b419 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminNewCatalogPriceRuleSection.xml @@ -20,7 +20,8 @@ - + + @@ -34,10 +35,15 @@ + + + + - + + diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml new file mode 100644 index 0000000000000..8b72b7616b6ff --- /dev/null +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test.xml @@ -0,0 +1,289 @@ + + + + + + + + + + <description value="Admin should be able to apply catalog rule for configurable product with assigned simple products"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-27708"/> + <group value="catalog"/> + <group value="configurable_product"/> + <group value="catalog_rule_configurable"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create category for first configurable product --> + <createData entity="SimpleSubCategory" stepKey="firstSimpleCategory"/> + + <!-- Create first configurable product with two options --> + <createData entity="ApiConfigurableProduct" stepKey="createFirstConfigProduct"> + <requiredEntity createDataKey="firstSimpleCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createFirstConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createFirstConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createFirstConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addFirstProductToAttributeSet"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getFirstConfigAttributeFirstOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getFirstConfigAttributeSecondOption"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + </getData> + + <!-- Create two child products for first configurable product --> + <createData entity="ApiSimpleOne" stepKey="createFirstConfigFirstChildProduct"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createFirstConfigSecondChildProduct"> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createFirstConfigProductOption"> + <requiredEntity createDataKey="createFirstConfigProduct"/> + <requiredEntity createDataKey="createFirstConfigProductAttribute"/> + <requiredEntity createDataKey="getFirstConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getFirstConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddFirstChild"> + <requiredEntity createDataKey="createFirstConfigProduct"/> + <requiredEntity createDataKey="createFirstConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createFirstConfigProductAddSecondChild"> + <requiredEntity createDataKey="createFirstConfigProduct"/> + <requiredEntity createDataKey="createFirstConfigSecondChildProduct"/> + </createData> + + <!-- Add customizable options to first product --> + <updateData createDataKey="createFirstConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateFirstProductWithOption"/> + + <!-- Create category for second configurable product --> + <createData entity="SimpleSubCategory" stepKey="secondSimpleCategory"/> + + <!-- Create second configurable product with two options --> + <createData entity="ApiConfigurableProduct" stepKey="createSecondConfigProduct"> + <requiredEntity createDataKey="secondSimpleCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createSecondConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createSecondConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createSecondConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="addSecondProductToAttributeSet"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getSecondConfigAttributeFirstOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getSecondConfigAttributeSecondOption"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + </getData> + + <!-- Create two child products for second configurable product --> + <createData entity="ApiSimpleOne" stepKey="createSecondConfigFirstChildProduct"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createSecondConfigSecondChildProduct"> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createSecondConfigProductOption"> + <requiredEntity createDataKey="createSecondConfigProduct"/> + <requiredEntity createDataKey="createSecondConfigProductAttribute"/> + <requiredEntity createDataKey="getSecondConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getSecondConfigAttributeSecondOption"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddFirstChild"> + <requiredEntity createDataKey="createSecondConfigProduct"/> + <requiredEntity createDataKey="createSecondConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createSecondConfigProductAddSecondChild"> + <requiredEntity createDataKey="createSecondConfigProduct"/> + <requiredEntity createDataKey="createSecondConfigSecondChildProduct"/> + </createData> + + <!-- Add customizable options to second product --> + <updateData createDataKey="createSecondConfigProduct" entity="productWithOptionRadiobutton" stepKey="updateSecondProductWithOption"/> + + <!--Create customer group --> + <createData entity="CustomCustomerGroup" stepKey="customerGroup"/> + + <!-- Create Customer --> + <createData entity="SimpleUsCustomerWithNewCustomerGroup" stepKey="createCustomer"> + <requiredEntity createDataKey="customerGroup" /> + </createData> + + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createFirstConfigProduct" stepKey="deleteFirstConfigProduct"/> + <deleteData createDataKey="createFirstConfigFirstChildProduct" stepKey="deleteFirstConfigFirstChildProduct"/> + <deleteData createDataKey="createFirstConfigSecondChildProduct" stepKey="deleteFirstConfigSecondChildProduct"/> + <deleteData createDataKey="createFirstConfigProductAttribute" stepKey="deleteFirstConfigProductAttribute"/> + <deleteData createDataKey="firstSimpleCategory" stepKey="deleteFirstSimpleCategory"/> + + <deleteData createDataKey="createSecondConfigProduct" stepKey="deleteSecondConfigProduct"/> + <deleteData createDataKey="createSecondConfigFirstChildProduct" stepKey="deleteSecondConfigFirstChildProduct"/> + <deleteData createDataKey="createSecondConfigSecondChildProduct" stepKey="deleteSecondConfigSecondChildProduct"/> + <deleteData createDataKey="createSecondConfigProductAttribute" stepKey="deleteSecondConfigProductAttribute"/> + <deleteData createDataKey="secondSimpleCategory" stepKey="deleteSimpleCategory"/> + + <!-- Customer log out --> + <!-- Must logout before delete customer otherwise magento fails during logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromStorefront"/> + + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="customerGroup" stepKey="deleteCustomerGroup"/> + + <!-- Delete created price rules --> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + <!-- Admin log out --> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!-- Create catalog price rule --> + <executeJS function="return '$$customerGroup.code$$'" stepKey="customerGroupName"/> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingCatalogPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForCatalogPriceRule"> + <argument name="groups" value=""{$customerGroupName}""/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForCatalogPriceRule"> + <argument name="apply" value="{{CatalogRuleToFixed.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyCatalogPriceRule"/> + + <actionGroup ref="AdminReindexAndFlushCache" stepKey="reindexAndFlushCache"/> + + <!-- Login to storefront from customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomerOnStorefront"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Assert first product in category --> + <amOnPage url="{{StorefrontCategoryPage.url($$firstSimpleCategory.custom_attributes[url_key]$$)}}" stepKey="goToFirstCategoryPageStorefront"/> + <waitForPageLoad stepKey="waitForFirstCategoryPageLoad"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkFirstProductPriceInCategory"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Assert second product in category --> + <amOnPage url="{{StorefrontCategoryPage.url($$secondSimpleCategory.custom_attributes[url_key]$$)}}" stepKey="goToSecondCategoryPageStorefront"/> + <waitForPageLoad stepKey="waitForSecondCategoryPageLoad"/> + <actionGroup ref="StorefrontCheckCategoryConfigurableProductWithUpdatedPriceActionGroup" stepKey="checkSecondProductPriceInCategory"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Assert first product in storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createFirstConfigProduct.custom_attributes[url_key]$$)}}" stepKey="amOnFirstProductPage"/> + <waitForPageLoad stepKey="waitForFirstProductPageLoad"/> + <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkFirstProductPriceInStorefrontProductPage"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Add first product with selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="firstConfigProductSelectFirstOptionValue"> + <argument name="attributeLabel" value="$$createFirstConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="firstConfigProductSelectSecondOptionValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons1.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstConfigProductToCart"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + </actionGroup> + + <!-- Add first product with another selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="firstConfigProductSelectFirstOptionAnotherValue"> + <argument name="attributeLabel" value="$$createFirstConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="firstConfigProductSelectSecondOptionAnotherValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons3.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstConfigProductWithOtherOptionsToCart"> + <argument name="productName" value="$$createFirstConfigProduct.name$$"/> + </actionGroup> + + <!-- Assert second product in storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createSecondConfigProduct.custom_attributes[url_key]$$)}}" stepKey="amOnSecondProductPage"/> + <waitForPageLoad stepKey="waitForSecondProductPageLoad"/> + <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="checkSecondProductPriceInStorefrontProductPage"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + <argument name="expectedPrice" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <!-- Add second product with selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="secondConfigProductSelectFirstOptionValue"> + <argument name="attributeLabel" value="$$createSecondConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="secondConfigProductSelectSecondOptionValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons1.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondConfigProductToCart"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + </actionGroup> + + <!-- Add second product with another selected options to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="secondConfigProductSelectFirstOptionAnotherValue"> + <argument name="attributeLabel" value="$$createSecondConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="StorefrontProductPageSelectRadioButtonOptionValueActionGroup" stepKey="secondConfigProductSelectSecondOptionAnotherValue"> + <argument name="attributeLabel" value="{{ProductOptionRadiobuttonWithTwoFixedOptions.title}}"/> + <argument name="optionLabel" value="{{ProductOptionValueRadioButtons3.title}}"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondConfigProductWithOtherOptionsToCart"> + <argument name="productName" value="$$createSecondConfigProduct.name$$"/> + </actionGroup> + + <!--Assert products prices in the cart --> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="amOnShoppingCartPage"/> + <waitForPageLoad stepKey="waitForShoppingCartPageLoad"/> + <waitForElementVisible selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="waitForCartFullyLoaded"/> + <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForFirstProductOption"/> + <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createFirstConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertFirstProductPriceForSecondProductOption"/> + <see userInput="$210.69" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForFirstProductOption"/> + <see userInput="$120.70" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createSecondConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertSecondProductPriceForSecondProductOption"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml index 1bc794ae80cd7..c110daee35428 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml @@ -11,14 +11,14 @@ <annotations> <features value="CatalogRuleConfigurable"/> <stories value="Apply catalog price rule"/> - <title value="Apply catalog rule for configurable product with assigned simple products"/> - <description value="Admin should be able to apply catalog rule for configurable product with assigned simple products"/> + <title value="DEPRECATED. Apply catalog rule for configurable product with assigned simple products"/> + <description value="DEPRECATED. Admin should be able to apply catalog rule for configurable product with assigned simple products"/> <severity value="CRITICAL"/> <testCaseId value="MC-14063"/> <group value="catalogRuleConfigurable"/> <group value="mtf_migrated"/> <skip> - <issueId value="MC-17140"/> + <issueId value="DEPRECATED">Use AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProducts2Test instead</issueId> </skip> </annotations> <before> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml new file mode 100644 index 0000000000000..bc6c89f2f1155 --- /dev/null +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptions2Test.xml @@ -0,0 +1,220 @@ +<?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="AdminApplyCatalogRuleForConfigurableProductWithOptions2Test"> + <annotations> + <features value="CatalogRuleConfigurable"/> + <stories value="Apply catalog price rule"/> + <title value="Apply catalog price rule for configurable product with options"/> + <description value="Admin should be able to apply the catalog rule for configurable product with options"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-27707"/> + <group value="catalog"/> + <group value="configurable_product"/> + <group value="catalog_rule_configurable"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="simpleCategory"/> + + <!-- Create configurable product with three options --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="simpleCategory"/> + </createData> + + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption2" stepKey="createConfigProductAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="productAttributeOption3" stepKey="createConfigProductAttributeThirdOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeFirstOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="2" stepKey="getConfigAttributeSecondOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <getData entity="ProductAttributeOptionGetter" index="3" stepKey="getConfigAttributeThirdOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create three child products --> + <createData entity="ApiSimpleOne" stepKey="createConfigFirstChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createConfigSecondChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + </createData> + + <createData entity="ApiSimpleOne" stepKey="createConfigThirdChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeThirdOption"/> + </createData> + + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeFirstOption"/> + <requiredEntity createDataKey="getConfigAttributeSecondOption"/> + <requiredEntity createDataKey="getConfigAttributeThirdOption"/> + </createData> + + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddFirstChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigFirstChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddSecondChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigSecondChildProduct"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddThirdChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigThirdChildProduct"/> + </createData> + + <!-- Login as Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginToAdminPanel"/> + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createConfigSecondChildProduct" stepKey="deleteSecondSimpleProduct"/> + <deleteData createDataKey="createConfigThirdChildProduct" stepKey="deleteThirdSimpleProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="simpleCategory" stepKey="deleteCategory"/> + + <actionGroup ref="AdminCatalogPriceRuleDeleteAllActionGroup" stepKey="deleteAllCatalogPriceRule"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!-- Create price rule for first configurable product option --> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingFirstPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForFirstPriceRule"> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="fillConditionsForFirstPriceRule"> + <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="targetSelectValue" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + <argument name="indexA" value="1"/> + <argument name="indexB" value="1"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForFirstPriceRule"> + <argument name="apply" value="{{CatalogRuleToFixed.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyFirstPriceRule"/> + + <!-- Create price rule for second configurable product option --> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForThirdPriceRule"> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="fillConditionsForThirdPriceRule"> + <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="targetSelectValue" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + <argument name="indexA" value="1"/> + <argument name="indexB" value="1"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForThirdPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplyThirdPriceRule"/> + + <!-- Create price rule for third configurable product option --> + <actionGroup ref="AdminOpenNewCatalogPriceRuleFormPageActionGroup" stepKey="startCreatingSecondPriceRule"/> + <actionGroup ref="AdminCatalogPriceRuleFillMainInfoActionGroup" stepKey="fillMainInfoForSecondPriceRule"> + <argument name="groups" value="'NOT LOGGED IN'"/> + </actionGroup> + <actionGroup ref="CreateCatalogPriceRuleConditionWithAttributeAndOptionActionGroup" stepKey="fillConditionsForSecondPriceRule"> + <argument name="attributeName" value="$$createConfigProductAttribute.attribute[frontend_labels][0][label]$$"/> + <argument name="targetSelectValue" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> + <argument name="indexA" value="1"/> + <argument name="indexB" value="1"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleFillActionsActionGroup" stepKey="fillActionsForSecondPriceRule"> + <argument name="apply" value="{{CatalogRuleWithoutDiscount.simple_action}}"/> + <argument name="discountAmount" value="{{CatalogRuleWithoutDiscount.discount_amount}}"/> + </actionGroup> + <actionGroup ref="AdminCatalogPriceRuleSaveAndApplyActionGroup" stepKey="saveAndApplySecondPriceRule"/> + + <actionGroup ref="AdminReindexAndFlushCache" stepKey="reindexAndFlushCache"/> + + <!-- Assert product in storefront product page --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="amOnProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <actionGroup ref="StorefrontAssertUpdatedProductPriceInStorefrontProductPageActionGroup" stepKey="assertUpdatedProductPriceInStorefrontProductPage"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + <argument name="expectedPrice" value="As low as ${{CatalogRuleToFixed.discount_amount}}"/> + </actionGroup> + + <executeJS function="return '$' + ({{CatalogRuleToFixed.discount_amount}}).toFixed(2);" stepKey="firstOptionPrice"/> + <executeJS function="return '$' + ({{ApiConfigurableProduct.price}} * (100 - {{_defaultCatalogRule.discount_amount}})/100).toFixed(2);" stepKey="secondOptionPrice"/> + + <!-- Assert product options price in storefront product page --> + <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToFirstProductOption"> + <argument name="option" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + <argument name="expectedPrice" value="{$firstOptionPrice} Regular Price ${{ApiConfigurableProduct.price}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToSecondProductOption"> + <argument name="option" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + <argument name="expectedPrice" value="{$secondOptionPrice} Regular Price ${{ApiConfigurableProduct.price}}"/> + </actionGroup> + + <actionGroup ref="StorefrontAssertCatalogPriceRuleAppliedToProductOptionActionGroup" stepKey="assertCatalogPriceRuleAppliedToThirdProductOption"> + <argument name="option" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> + <argument name="expectedPrice" value="{{ApiConfigurableProduct.price}}"/> + </actionGroup> + + <!-- Add product with selected option to the cart --> + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectFirstOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addFirstOptionToCart"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectSecondOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addSecondOptionToCart"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <actionGroup ref="StorefrontProductPageSelectDropDownOptionValueActionGroup" stepKey="selectThirdOptionValue"> + <argument name="attributeLabel" value="$$createConfigProductAttribute.default_frontend_label$$"/> + <argument name="optionLabel" value="$$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$"/> + </actionGroup> + <actionGroup ref="AddToCartFromStorefrontProductPageActionGroup" stepKey="addThirdOptionToCart"> + <argument name="productName" value="$$createConfigProduct.name$$"/> + </actionGroup> + + <!--Assert product price in the cart --> + <actionGroup ref="StorefrontOpenCartFromMinicartActionGroup" stepKey="openCartPage"/> + <waitForElementVisible selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="waitForPriceAppears"/> + <see userInput="{$firstOptionPrice}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeFirstOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForFirstProductOption"/> + <see userInput="{$secondOptionPrice}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeSecondOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForSecondProductOption"/> + <see userInput="{{ApiConfigurableProduct.price}}" selector="{{CheckoutCartProductSection.ProductPriceByOption($$createConfigProductAttributeThirdOption.option[store_labels][1][label]$$)}}" stepKey="assertProductPriceForThirdProductOption"/> + </test> +</tests> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml index fcf5e2c038047..05f30fd6fcbde 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml @@ -11,14 +11,14 @@ <annotations> <features value="CatalogRuleConfigurable"/> <stories value="Apply catalog price rule"/> - <title value="Apply catalog price rule for configurable product with options"/> - <description value="Admin should be able to apply the catalog rule for configurable product with options"/> + <title value="DEPRECATED. Apply catalog price rule for configurable product with options"/> + <description value="DEPRECATED. Admin should be able to apply the catalog rule for configurable product with options"/> <severity value="CRITICAL"/> <testCaseId value="MC-14062"/> <group value="catalogRuleConfigurable"/> <group value="mtf_migrated"/> <skip> - <issueId value="MC-17140"/> + <issueId value="DEPRECATED">Use AdminApplyCatalogRuleForConfigurableProductWithOptions2Test instead</issueId> </skip> </annotations> <before> diff --git a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php index 3506437ea038d..30a7c723940e2 100644 --- a/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php +++ b/app/code/Magento/CatalogSearch/Model/ResourceModel/Fulltext/Collection.php @@ -385,6 +385,8 @@ public function addFieldToFilter($field, $condition = null) public function clear() { $this->searchResult = null; + $this->setFlag('has_category_filter', false); + return parent::clear(); } @@ -394,6 +396,8 @@ public function clear() protected function _reset() { $this->searchResult = null; + $this->setFlag('has_category_filter', false); + return parent::_reset(); } @@ -423,7 +427,11 @@ public function _loadEntities($printQuery = false, $logQuery = false) throw $e; } + $position = 0; foreach ($rows as $value) { + if ($this->getFlag('has_category_filter')) { + $value['cat_index_position'] = $position++; + } $object = $this->getNewEmptyItem()->setData($value); $this->addItem($object); if (isset($this->_itemsById[$object->getId()])) { @@ -432,6 +440,9 @@ public function _loadEntities($printQuery = false, $logQuery = false) $this->_itemsById[$object->getId()] = [$object]; } } + if ($this->getFlag('has_category_filter')) { + $this->setFlag('has_category_filter', false); + } return $this; } @@ -669,6 +680,7 @@ public function addCategoryFilter(\Magento\Catalog\Model\Category $category) if ($this->defaultFilterStrategyApplyChecker->isApplicable()) { parent::addCategoryFilter($category); } else { + $this->setFlag('has_category_filter', true); $this->_productLimitationPrice(); } diff --git a/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchWithPaginationActionGroup.xml b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchWithPaginationActionGroup.xml new file mode 100644 index 0000000000000..95ebfa40adb26 --- /dev/null +++ b/app/code/Magento/CatalogSearch/Test/Mftf/ActionGroup/StorefrontQuickSearchWithPaginationActionGroup.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="StorefrontQuickSearchWithPaginationActionGroup"> + <annotations> + <description>Navigate to catalog search page with prepared GET params to get search results with particular page number.</description> + </annotations> + <arguments> + <argument name="phrase" type="string" defaultValue="{{_defaultProduct.name}}"/> + <argument name="pageNumber" type="string" defaultValue="1"/> + </arguments> + <amOnPage url="{{StorefrontCatalogSearchPage.url}}?q={{phrase}}&p={{pageNumber}}" stepKey="navigateToCatalogSearchPageWithPreparedRequest"/> + <waitForPageLoad stepKey="waitForCatalogSearchPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml index 0e36e0ecbe71f..fa169373c1096 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionDropDownActionGroup.xml @@ -10,13 +10,13 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontSelectOptionDropDownActionGroup"> <annotations> - <description>Selects the provided Product Option Value under the provided Product Option Title on a Storefront Product page.</description> + <description>DEPRECATED. Please use StorefrontProductPageSelectDropDownOptionValueActionGroup instead. Selects the provided Product Option Value under the provided Product Option Title on a Storefront Product page.</description> </annotations> <arguments> <argument name="optionTitle" defaultValue="ProductOptionDropDown"/> <argument name="option" defaultValue="ProductOptionValueDropdown2.title"/> </arguments> - + <selectOption selector="{{StorefrontProductInfoMainSection.productOptionSelect(optionTitle.title)}}" userInput="{{option}}" stepKey="fillOptionDropDown"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml index c0de6f8f8466f..ba75a03d6ff04 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontSelectOptionRadioButtonActionGroup.xml @@ -10,7 +10,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontSelectOptionRadioButtonActionGroup"> <annotations> - <description>Checks the provided Product Option radio button for the provided Product Option Price on a Storefront Product page.</description> + <description>DEPRECATED. Please use StorefrontProductPageSelectRadioButtonOptionValueActionGroup instead. Checks the provided Product Option radio button for the provided Product Option Price on a Storefront Product page.</description> </annotations> <arguments> <argument name="optionTitle" defaultValue="ProductOptionRadiobutton"/> diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php index 3f1c22548c8d8..fc5e149c0726e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/LinkedProductSelectBuilder.php @@ -39,11 +39,11 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ - public function build($productId) + public function build(int $productId, int $storeId) : array { - $selects = $this->linkedProductSelectBuilder->build($productId); + $selects = $this->linkedProductSelectBuilder->build($productId, $storeId); foreach ($selects as $select) { $this->baseSelectProcessor->process($select); diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php b/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php index d3ce508b31e0d..29717a057cbdb 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionsProvider.php @@ -62,14 +62,16 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function getProducts(ProductInterface $product) { - $key = $this->storeManager->getStore()->getId() . '-' . $product->getId(); + $productId = $product->getId(); + $storeId = $product->getStoreId() ?: $this->storeManager->getStore()->getId(); + $key = $storeId . '-' . $productId; if (!isset($this->linkedProductMap[$key])) { $productIds = $this->resource->getConnection()->fetchCol( - '(' . implode(') UNION (', $this->linkedProductSelectBuilder->build($product->getId())) . ')' + '(' . implode(') UNION (', $this->linkedProductSelectBuilder->build($productId, $storeId)) . ')' ); $this->linkedProductMap[$key] = $this->collectionFactory->create() diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php index 3ef03f32ae05d..0794ba2cb42bf 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Product/LinkedProductSelectBuilderTest.php @@ -50,6 +50,7 @@ protected function setUp() public function testBuild() { $productId = 42; + $storeId = 1; /** @var Select|\PHPUnit_Framework_MockObject_MockObject $selectMock */ $selectMock = $this->getMockBuilder(Select::class) @@ -67,6 +68,6 @@ public function testBuild() ->method('process') ->with($selectMock); - $this->assertEquals($expectedResult, $this->subject->build($productId)); + $this->assertEquals($expectedResult, $this->subject->build($productId, $storeId)); } } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php index 7c83645a9fda3..29f48dbe7a460 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/LowestPriceOptionsProviderTest.php @@ -6,8 +6,8 @@ namespace Magento\ConfigurableProduct\Test\Unit\Pricing\Price; +use Magento\Catalog\Model\Product; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; -use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\ResourceModel\Product\LinkedProductSelectBuilderInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Store\Api\Data\StoreInterface; @@ -102,9 +102,11 @@ protected function setUp() public function testGetProducts() { $productId = 1; + $storeId = 1; $linkedProducts = ['some', 'linked', 'products', 'dataobjects']; - $product = $this->getMockBuilder(ProductInterface::class)->disableOriginalConstructor()->getMock(); + $product = $this->createMock(Product::class); $product->expects($this->any())->method('getId')->willReturn($productId); + $product->expects($this->any())->method('getStoreId')->willReturn($storeId); $this->linkedProductSelectBuilder->expects($this->any())->method('build')->with($productId)->willReturn([]); $this->productCollection ->expects($this->once()) diff --git a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php index b9102bc5e00c4..ddf79f413df37 100644 --- a/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Elasticsearch5/Model/Client/Elasticsearch.php @@ -285,7 +285,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false, + 'index' => true, ], ], ], @@ -296,7 +296,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'mapping' => $this->prepareFieldInfo( [ 'type' => 'text', - 'index' => false, + 'index' => true, ] ), ], diff --git a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php index d933d8bb5d0b5..d2b677a95c7c0 100644 --- a/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch/Model/Client/Elasticsearch.php @@ -278,7 +278,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => 'no' + 'index' => 'not_analyzed', ], ], ], @@ -288,7 +288,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping' => 'string', 'mapping' => [ 'type' => 'string', - 'index' => 'no' + 'index' => 'not_analyzed', ], ], ] diff --git a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php index c35b5d45e1356..4693b7502c5c1 100644 --- a/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php +++ b/app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchResultApplier.php @@ -60,6 +60,7 @@ public function apply() { if (empty($this->searchResult->getItems())) { $this->collection->getSelect()->where('NULL'); + return; } @@ -85,15 +86,33 @@ public function apply() private function sliceItems(array $items, int $size, int $currentPage): array { if ($size !== 0) { - $totalPages = (int) ceil(count($items)/$size); - $currentPage = min($currentPage, $totalPages); - $offset = ($currentPage - 1) * $size; - if ($offset < 0) { - $offset = 0; + // Check that current page is in a range of allowed page numbers, based on items count and items per page, + // than calculate offset for slicing items array. + $itemsCount = count($items); + $maxAllowedPageNumber = ceil($itemsCount/$size); + if ($currentPage < 1) { + $currentPage = 1; + } + if ($currentPage > $maxAllowedPageNumber) { + $currentPage = $maxAllowedPageNumber; } - $items = array_slice($items, $offset, $this->size); + + $offset = $this->getOffset($currentPage, $size); + $items = array_slice($items, $offset, $size); } return $items; } + + /** + * Get offset for given page. + * + * @param int $pageNumber + * @param int $pageSize + * @return int + */ + private function getOffset(int $pageNumber, int $pageSize): int + { + return ($pageNumber - 1) * $pageSize; + } } diff --git a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php index 99fd416b5cd3e..5a735da96b754 100644 --- a/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch/Test/Unit/Elasticsearch5/Model/Client/ElasticsearchTest.php @@ -11,7 +11,7 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; /** - * Class ElasticsearchTest + * Test elasticsearch client methods. */ class ElasticsearchTest extends \PHPUnit\Framework\TestCase { @@ -379,7 +379,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true ], ], ], @@ -389,7 +389,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, ], ], ], @@ -449,7 +449,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true, ], ], ], @@ -459,7 +459,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, ], ], ] diff --git a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php index ff299be786aa8..2c1c283c5b24d 100644 --- a/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php +++ b/app/code/Magento/Elasticsearch6/Model/Client/Elasticsearch.php @@ -292,7 +292,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false, + 'index' => true, ], ], ], @@ -302,7 +302,7 @@ public function addFieldsMapping(array $fields, $index, $entityType) 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, 'copy_to' => '_search' ], ], diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml new file mode 100644 index 0000000000000..e763df7dd3227 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml @@ -0,0 +1,67 @@ +<?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="StorefrontProductQuickSearchUsingElasticSearch6Test"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Storefront Search"/> + <title value="Product quick search doesn't throw exception after ES is chosen as search engine with different amount per page"/> + <description value="Verify no elastic search exception is thrown when searching for products, when displayed products per page are greater or equal the size of available products."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-28917"/> + <useCaseId value="MC-25138"/> + <group value="catalog"/> + <group value="elasticsearch"/> + <group value="SearchEngineElasticsearch"/> + <group value="catalog_search"/> + </annotations> + <before> + <createData entity="SimpleProduct2" stepKey="createFirstProduct"> + <field key="name">AAA Product Simple AAA</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSecondProduct"> + <field key="name">Product Simple AAA</field> + </createData> + <magentoCLI command="config:set {{CustomGridPerPageValuesConfigData.path}} {{CustomGridPerPageValuesConfigData.value}}" stepKey="setCustomGridPerPageValues"/> + </before> + + <after> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + <magentoCLI command="config:set {{DefaultGridPerPageValuesConfigData.path}} {{DefaultGridPerPageValuesConfigData.value}}" stepKey="setDefaultGridPerPageValues"/> + </after> + + <actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontHomePage"/> + <actionGroup ref="StorefrontCheckQuickSearchStringActionGroup" stepKey="quickSearchSimpleProduct"> + <argument name="phrase" value="AAA"/> + </actionGroup> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductOnCatalogSearchPage"> + <argument name="product" value="$createFirstProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertSecondProductIsMissingOnCatalogSearchPage"> + <argument name="productName" value="$createSecondProduct.name$"/> + </actionGroup> + <click selector="{{StorefrontCategoryBottomToolbarSection.nextPage}}" stepKey="clickNextPageCatalogSearchPager"/> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondProductOnCatalogSearchPage"> + <argument name="product" value="$createSecondProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertFirstProductIsMissingOnCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> + </actionGroup> + <selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage"> + <argument name="product" value="$createFirstProduct$"/> + </actionGroup> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertSecondProductDisplayedOnCatalogSearchPage"> + <argument name="product" value="$createSecondProduct$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml new file mode 100644 index 0000000000000..b4eb436fc1b2a --- /dev/null +++ b/app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml @@ -0,0 +1,46 @@ +<?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="StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest" extends="StorefrontProductQuickSearchUsingElasticSearch6Test"> + <annotations> + <features value="CatalogSearch"/> + <stories value="Storefront Search"/> + <title value="Product quick search doesn't throw exception after ES is chosen as search engine with selected page out of range"/> + <description value="Verify no elastic search exception is thrown when try to get page with selected page out of range."/> + <severity value="BLOCKER"/> + <testCaseId value="MC-29383"/> + <useCaseId value="MC-25138"/> + <group value="catalog"/> + <group value="elasticsearch"/> + <group value="SearchEngineElasticsearch"/> + <group value="catalog_search"/> + </annotations> + <remove keyForRemoval="selectDisplayedProductInGridPerPage"/> + <remove keyForRemoval="assertFirstProductDisplayedOnCatalogSearchPage"/> + <remove keyForRemoval="assertSecondProductDisplayedOnCatalogSearchPage"/> + <grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfLastPage"/> + <actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage"> + <argument name="phrase" value="AAA"/> + <argument name="pageNumber" value="999"/> + </actionGroup> + <scrollTo selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="scrollToBottomToolbarPager"/> + <grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfCurrentPage"/> + <assertEquals stepKey="assertCurrentPageIsLastPageOfCatalogSearchResult"> + <expectedResult type="variable">grabNumberOfLastPage</expectedResult> + <actualResult type="variable">grabNumberOfCurrentPage</actualResult> + </assertEquals> + <actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertProductOnLastCatalogSearchPage"> + <argument name="product" value="$createSecondProduct$"/> + </actionGroup> + <actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertFirstProductIsMissingOnLastCatalogSearchPage"> + <argument name="productName" value="$createFirstProduct.name$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php index 607624d7b5e8e..3d840d5a808af 100644 --- a/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php +++ b/app/code/Magento/Elasticsearch6/Test/Unit/Model/Client/ElasticsearchTest.php @@ -445,7 +445,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true, ], ], ], @@ -455,7 +455,7 @@ public function testAddFieldsMapping() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, 'copy_to' => '_search' ], ], @@ -515,7 +515,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'integer', - 'index' => false + 'index' => true, ], ], ], @@ -525,7 +525,7 @@ public function testAddFieldsMappingFailure() 'match_mapping_type' => 'string', 'mapping' => [ 'type' => 'text', - 'index' => false, + 'index' => true, 'copy_to' => '_search' ], ], diff --git a/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php b/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php index ca989c9f5235f..57081804fca80 100644 --- a/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php +++ b/app/code/Magento/ProductAlert/Block/Email/AbstractEmail.php @@ -6,8 +6,6 @@ namespace Magento\ProductAlert\Block\Email; use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Framework\App\ObjectManager; -use Magento\ProductAlert\Block\Product\ImageProvider; /** * Product Alert Abstract Email Block @@ -43,32 +41,23 @@ abstract class AbstractEmail extends \Magento\Framework\View\Element\Template */ protected $imageBuilder; - /** - * @var ImageProvider - */ - private $imageProvider; - /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode * @param PriceCurrencyInterface $priceCurrency * @param \Magento\Catalog\Block\Product\ImageBuilder $imageBuilder * @param array $data - * @param ImageProvider $imageProvider */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Framework\Filter\Input\MaliciousCode $maliciousCode, PriceCurrencyInterface $priceCurrency, \Magento\Catalog\Block\Product\ImageBuilder $imageBuilder, - array $data = [], - ImageProvider $imageProvider = null + array $data = [] ) { $this->imageBuilder = $imageBuilder; $this->priceCurrency = $priceCurrency; $this->_maliciousCode = $maliciousCode; - $this->imageProvider = $imageProvider ?: ObjectManager::getInstance()->get(ImageProvider::class); - parent::__construct($context, $data); } @@ -173,6 +162,8 @@ protected function _getUrlParams() } /** + * Get Price Render + * * @return \Magento\Framework\Pricing\Render */ protected function getPriceRender() @@ -227,6 +218,6 @@ public function getProductPriceHtml( */ public function getImage($product, $imageId, $attributes = []) { - return $this->imageProvider->getImage($product, $imageId, $attributes); + return $this->imageBuilder->create($product, $imageId, $attributes); } } diff --git a/app/code/Magento/ProductAlert/Block/Product/ImageProvider.php b/app/code/Magento/ProductAlert/Block/Product/ImageProvider.php deleted file mode 100644 index 61d8d1987c2d7..0000000000000 --- a/app/code/Magento/ProductAlert/Block/Product/ImageProvider.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ProductAlert\Block\Product; - -use Magento\Store\Model\App\Emulation; -use Magento\Catalog\Block\Product\ImageBuilder; -use Magento\Catalog\Model\Product; -use Magento\Store\Model\StoreManagerInterface; -use Magento\Framework\App\Area; -use Magento\Catalog\Block\Product\Image; - -/** - * Provides product image to be used in the Product Alert Email. - */ -class ImageProvider -{ - /** - * @var ImageBuilder - */ - private $imageBuilder; - - /** - * @var StoreManagerInterface - */ - private $storeManager; - - /** - * @var Emulation - */ - private $appEmulation; - - /** - * @param ImageBuilder $imageBuilder - * @param StoreManagerInterface $storeManager - * @param Emulation $appEmulation - */ - public function __construct( - ImageBuilder $imageBuilder, - StoreManagerInterface $storeManager, - Emulation $appEmulation - ) { - $this->imageBuilder = $imageBuilder; - $this->storeManager = $storeManager; - $this->appEmulation = $appEmulation; - } - - /** - * @param Product $product - * @param string $imageId - * @param array $attributes - * @return Image - * @throws \Exception - */ - public function getImage(Product $product, $imageId, $attributes = []) - { - $storeId = $this->storeManager->getStore()->getId(); - $this->appEmulation->startEnvironmentEmulation($storeId, Area::AREA_FRONTEND, true); - - try { - $image = $this->imageBuilder->create($product, $imageId, $attributes); - } catch (\Exception $exception) { - $this->appEmulation->stopEnvironmentEmulation(); - throw $exception; - } - - $this->appEmulation->stopEnvironmentEmulation(); - return $image; - } -} diff --git a/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php b/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php index c5872701ef9c8..48907197ab7b3 100644 --- a/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php +++ b/app/code/Magento/ProductAlert/Test/Unit/Block/Email/StockTest.php @@ -25,11 +25,6 @@ class StockTest extends \PHPUnit\Framework\TestCase */ protected $imageBuilder; - /** - * @var \Magento\ProductAlert\Block\Product\ImageProvider|\PHPUnit_Framework_MockObject_MockObject - */ - private $imageProviderMock; - protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -39,16 +34,11 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->imageProviderMock = $this->getMockBuilder(\Magento\ProductAlert\Block\Product\ImageProvider::class) - ->disableOriginalConstructor() - ->getMock(); - $this->_block = $objectManager->getObject( \Magento\ProductAlert\Block\Email\Stock::class, [ 'maliciousCode' => $this->_filter, - 'imageBuilder' => $this->imageBuilder, - 'imageProvider' => $this->imageProviderMock + 'imageBuilder' => $this->imageBuilder ] ); } @@ -88,8 +78,7 @@ public function testGetImage() ->disableOriginalConstructor() ->getMock(); - $this->imageProviderMock->expects($this->atLeastOnce())->method('getImage')->willReturn($productImageMock); - + $this->imageBuilder->expects($this->atLeastOnce())->method('create')->willReturn($productImageMock); $this->assertInstanceOf( \Magento\Catalog\Block\Product\Image::class, $this->_block->getImage($productMock, $imageId, $attributes) diff --git a/app/code/Magento/ProductAlert/Test/Unit/Block/Product/ImageProviderTest.php b/app/code/Magento/ProductAlert/Test/Unit/Block/Product/ImageProviderTest.php deleted file mode 100644 index 172e5f486f30b..0000000000000 --- a/app/code/Magento/ProductAlert/Test/Unit/Block/Product/ImageProviderTest.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\ProductAlert\Test\Unit\Block\Product; - -class ImageProviderTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var \Magento\Catalog\Block\Product\ImageBuilder|\PHPUnit_Framework_MockObject_MockObject - */ - private $imageBuilderMock; - - /** - * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - private $storeManagerMock; - - /** - * @var \Magento\Store\Model\App\Emulation|\PHPUnit_Framework_MockObject_MockObject - */ - private $emulationMock; - - /** - * @var \Magento\ProductAlert\Block\Product\ImageProvider - */ - private $model; - - protected function setUp() - { - $this->imageBuilderMock = $this->getMockBuilder(\Magento\Catalog\Block\Product\ImageBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->disableOriginalConstructor() - ->getMock(); - $this->emulationMock = $this->getMockBuilder(\Magento\Store\Model\App\Emulation::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->model = new \Magento\ProductAlert\Block\Product\ImageProvider( - $this->imageBuilderMock, - $this->storeManagerMock, - $this->emulationMock - ); - } - - /** - * Test that image is created successfully with app emulation enabled. - */ - public function testGetImage() - { - $imageId = 'test_image_id'; - $attributes = []; - - $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); - $imageMock = $this->createMock(\Magento\Catalog\Block\Product\Image::class); - $storeMock = $this->createMock(\Magento\Store\Api\Data\StoreInterface::class); - - $this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); - $this->emulationMock->expects($this->once())->method('startEnvironmentEmulation'); - $this->imageBuilderMock->expects($this->once()) - ->method('create') - ->with($productMock, $imageId, $attributes) - ->willReturn($imageMock); - $this->emulationMock->expects($this->once())->method('stopEnvironmentEmulation'); - - $this->assertEquals($imageMock, $this->model->getImage($productMock, $imageId, $attributes)); - } - - /** - * Test that app emulation stops when exception occurs. - * - * @expectedException \Exception - * @expectedExceptionMessage Image Builder Exception - */ - public function testGetImageThrowsAnException() - { - $imageId = 1; - $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) - ->disableOriginalConstructor() - ->getMock(); - $storeMock = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->emulationMock->expects($this->once())->method('startEnvironmentEmulation'); - $this->storeManagerMock->expects($this->atLeastOnce())->method('getStore')->willReturn($storeMock); - - $this->imageBuilderMock->expects($this->once()) - ->method('create') - ->with($productMock, $imageId) - ->willThrowException(new \Exception("Image Builder Exception")); - - $this->emulationMock->expects($this->once())->method('stopEnvironmentEmulation'); - $this->model->getImage($productMock, $imageId); - } -} diff --git a/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php b/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php index e3a2056a89ec0..9a5381c094243 100644 --- a/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/ProductAlert/Test/Unit/Model/ObserverTest.php @@ -11,6 +11,9 @@ /** * Class ObserverTest + * + * Is used to test Product Alert Observer + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) */ @@ -168,7 +171,7 @@ protected function setUp() ); $this->storeMock = $this->getMockBuilder(\Magento\Store\Model\Store::class) ->disableOriginalConstructor() - ->setMethods(['getDefaultStore', 'getId']) + ->setMethods(['getDefaultStore', 'getId', 'setWebsiteId']) ->getMock(); $this->customerRepositoryMock = $this->getMockBuilder(\Magento\Customer\Api\CustomerRepositoryInterface::class) ->getMock(); @@ -285,12 +288,13 @@ public function testProcessPriceEmailThrowsException() $this->storeMock->expects($this->any())->method('getDefaultStore')->willReturnSelf(); $this->websiteMock->expects($this->once())->method('getDefaultStore')->willReturn($this->storeMock); $this->storeMock->expects($this->any())->method('getId')->willReturn(2); + $this->storeMock->expects($this->any())->method('setWebsiteId')->willReturnSelf(); $this->scopeConfigMock->expects($this->once())->method('getValue')->willReturn(true); $this->priceColFactoryMock->expects($this->once())->method('create')->willReturnSelf(); $this->priceColFactoryMock->expects($this->once())->method('addWebsiteFilter')->willReturnSelf(); - + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($this->storeMock); $items = [ new \Magento\Framework\DataObject([ 'customer_id' => $id diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml new file mode 100644 index 0000000000000..e14bb5342db91 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test.xml @@ -0,0 +1,87 @@ +<?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="AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test"> + <annotations> + <features value="UrlRewrite"/> + <stories value="Update url rewrites"/> + <title value="Check url rewrites in catalog categories after changing url key"/> + <description value="Check url rewrites in catalog categories after changing url key for store view and moving category"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-25622"/> + <group value="catalog"/> + <group value="url_rewrite"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create two sub-categories in default category with simple products --> + <createData entity="_defaultCategory" stepKey="createFirstCategory"/> + <createData entity="_defaultProduct" stepKey="createFirstSimpleProduct"> + <requiredEntity createDataKey="createFirstCategory"/> + </createData> + <createData entity="_defaultCategory" stepKey="createSecondCategory"/> + <createData entity="_defaultProduct" stepKey="createSecondSimpleProduct"> + <requiredEntity createDataKey="createSecondCategory"/> + </createData> + + <!-- Log in to backend --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Create additional Store View in Main Website Store --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreView"/> + <magentoCLI command="indexer:reindex" stepKey="reindexAll"/> + </before> + + <after> + <deleteData createDataKey="createFirstCategory" stepKey="deleteFirstCategory"/> + <deleteData createDataKey="createSecondCategory" stepKey="deleteSecondCategory"/> + <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> + <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"/> + <actionGroup ref="ClearFiltersAdminDataGridActionGroup" stepKey="clearWebsitesGridFilters"/> + <actionGroup ref="logout" stepKey="logoutFromAdmin"/> + </after> + + <!-- On the categories editing page change store view to created additional view --> + <actionGroup ref="SwitchCategoryStoreViewActionGroup" stepKey="openFirstCategoryAndSwitchToCustomStoreView"> + <argument name="Store" value="customStore.name"/> + <argument name="CatName" value="$createFirstCategory.name$"/> + </actionGroup> + + <!-- Change url key for category for first category; save --> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeFirstCategoryUrlKey"> + <argument name="value" value="{{SimpleRootSubCategory.url_key}}"/> + </actionGroup> + + <!-- Change store view to "All store views" for first category --> + <actionGroup ref="SwitchCategoryToAllStoreViewActionGroup" stepKey="switchToAllStoreViews"> + <argument name="CatName" value="$createFirstCategory.name$"/> + </actionGroup> + + <!-- Move first category inside second category --> + <actionGroup ref="MoveCategoryActionGroup" stepKey="moveFirstCategoryInsideSecondCategory"> + <argument name="childCategory" value="$createFirstCategory.name$"/> + <argument name="parentCategory" value="$createSecondCategory.name$"/> + </actionGroup> + + <!-- Open first category storefront page --> + <amOnPage url="$createSecondCategory.custom_attributes[url_key]$/$createFirstCategory.custom_attributes[url_key]$.html" stepKey="openFirstCategoryStorefrontPage"/> + <waitForPageLoad stepKey="waitForFirstCategoryStorefrontPageLoad"/> + <see userInput="$createFirstSimpleProduct.name$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeFirstProductInCategory"/> + + <!-- Switch to custom store view--> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="customStore"/> + </actionGroup> + + <!-- Assert category url with custom store view --> + <seeInCurrentUrl url="{{SimpleRootSubCategory.url_key}}.html" stepKey="seeUpdatedUrlKey"/> + <see userInput="$createFirstSimpleProduct.name$" selector="{{StorefrontCategoryMainSection.productsList}}" stepKey="seeFirstProductInCategoryAgain"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml index badda06b827ea..7f9ee3020c388 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategoryTest.xml @@ -11,12 +11,15 @@ <annotations> <features value="Url Rewrite"/> <stories value="Update url rewrites"/> - <title value="Check url rewrites in catalog categories after changing url key"/> - <description value="Check url rewrites in catalog categories after changing url key for store view and moving category"/> + <title value="DEPRECATED. Check url rewrites in catalog categories after changing url key"/> + <description value="DEPRECATED. Check url rewrites in catalog categories after changing url key for store view and moving category"/> <severity value="CRITICAL"/> <testCaseId value="MC-5352"/> <group value="url_rewrite"/> <group value="mtf_migrated"/> + <skip> + <issueId value="DEPRECATED">Use AdminCheckUrlRewritesInCatalogCategoriesAfterChangingUrlKeyForStoreViewAndMovingCategory2Test instead</issueId> + </skip> </annotations> <before> <!-- Create two sub-categories in default category with simple products --> diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php index 9ee3b3baa5fc2..9ac5f6959d12e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchTest.php @@ -42,7 +42,7 @@ public function testFilterForNonExistingCategory() { products(filter: {category_id: {eq: "99999999"}}) { filters { - name + name } } } @@ -204,7 +204,7 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi { return <<<QUERY { - products(filter:{ + products(filter:{ $attributeCode: {in:["{$firstOption}", "{$secondOption}"]} } pageSize: 3 @@ -212,7 +212,7 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi ) { total_count - items + items { name sku @@ -225,14 +225,14 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } + } aggregations{ attribute_code count @@ -243,8 +243,8 @@ private function getQueryProductsWithArrayOfCustomAttributes($attributeCode, $fi count } } - - } + + } } QUERY; } @@ -262,16 +262,16 @@ public function testFilterProductsByDropDownCustomAttribute() $optionValue = $this->getDefaultAttributeOptionValue($attributeCode); $query = <<<QUERY { - products(filter:{ + products(filter:{ $attributeCode: {eq: "{$optionValue}"} } - + pageSize: 3 currentPage: 1 ) { total_count - items + items { name sku @@ -284,14 +284,14 @@ public function testFilterProductsByDropDownCustomAttribute() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - + } aggregations{ attribute_code @@ -304,8 +304,8 @@ public function testFilterProductsByDropDownCustomAttribute() value } } - - } + + } } QUERY; @@ -365,7 +365,7 @@ private function reIndexAndCleanCache() : void $appDir = dirname(Bootstrap::getInstance()->getAppTempDir()); $out = ''; // phpcs:ignore Magento2.Security.InsecureFunction - exec("php -f {$appDir}/bin/magento indexer:reindex", $out); + exec("php -f {$appDir}/bin/magento indexer:reindex catalog_category_product", $out); CacheCleaner::cleanAll(); } @@ -393,15 +393,15 @@ public function testFilterProductsByMultiSelectCustomAttributes() } $query = <<<QUERY { - products(filter:{ - $attributeCode: {in:["{$optionValues[0]}", "{$optionValues[1]}", "{$optionValues[2]}"]} + products(filter:{ + $attributeCode: {in:["{$optionValues[0]}", "{$optionValues[1]}", "{$optionValues[2]}"]} } pageSize: 3 currentPage: 1 ) { total_count - items + items { name sku @@ -414,14 +414,14 @@ public function testFilterProductsByMultiSelectCustomAttributes() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } + } aggregations{ attribute_code count @@ -430,11 +430,11 @@ public function testFilterProductsByMultiSelectCustomAttributes() { label value - + } } - - } + + } } QUERY; @@ -485,7 +485,7 @@ public function testSearchAndFilterByCustomAttribute() ) { total_count - items + items { name sku @@ -498,15 +498,15 @@ public function testSearchAndFilterByCustomAttribute() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - - } + + } aggregations { attribute_code @@ -518,10 +518,10 @@ public function testSearchAndFilterByCustomAttribute() label value } - } - } - + + } + } QUERY; $response = $this->graphQlQuery($query); @@ -631,7 +631,7 @@ public function testFilterByCategoryIdAndCustomAttribute() ) { total_count - items + items { name sku @@ -644,7 +644,7 @@ public function testFilterByCategoryIdAndCustomAttribute() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count @@ -664,7 +664,7 @@ public function testFilterByCategoryIdAndCustomAttribute() value } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -788,7 +788,7 @@ public function testFilterBySingleProductUrlKey() ) { total_count - items + items { name sku @@ -802,7 +802,7 @@ public function testFilterBySingleProductUrlKey() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count @@ -822,7 +822,7 @@ public function testFilterBySingleProductUrlKey() value } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -851,17 +851,17 @@ public function testFilterBySingleProductUrlKey() ) { total_count - items + items { name sku url_key } - + filters{ name request_var - filter_items_count + filter_items_count } aggregations { @@ -875,7 +875,7 @@ public function testFilterBySingleProductUrlKey() value } } - } + } } QUERY; $response = $this->graphQlQuery($query2); @@ -914,7 +914,7 @@ public function testFilterByMultipleProductUrlKeys() ) { total_count - items + items { name sku @@ -923,12 +923,12 @@ public function testFilterByMultipleProductUrlKeys() page_info{ current_page page_size - + } filters{ name request_var - filter_items_count + filter_items_count } aggregations { @@ -942,7 +942,7 @@ public function testFilterByMultipleProductUrlKeys() value } } - } + } } QUERY; $response = $this->graphQlQuery($query); @@ -1198,10 +1198,10 @@ public function testFilterByMultipleFilterFieldsSortedByMultipleSortFields() products( filter: { - price:{to :"50"} + price:{to :"50"} sku:{in:["simple1", "simple2"]} name:{match:"Simple"} - + } pageSize:4 currentPage:1 @@ -1236,10 +1236,10 @@ public function testFilterByMultipleFilterFieldsSortedByMultipleSortFields() page_size current_page } - sort_fields + sort_fields { default - options + options { value label @@ -1382,7 +1382,7 @@ public function testFilteringForProductsFromMultipleCategories() $query = <<<QUERY { - products(filter:{ + products(filter:{ category_id :{in:["4","5","12"]} }) { @@ -1429,7 +1429,7 @@ public function testFilterProductsBySingleCategoryId() category_id:{eq:"{$queryCategoryId}"} } pageSize:2 - + ) { items @@ -1446,7 +1446,7 @@ public function testFilterProductsBySingleCategoryId() } } total_count - + } } @@ -1520,7 +1520,7 @@ public function testSearchAndSortByRelevance() ) { total_count - items + items { name sku @@ -1533,14 +1533,14 @@ public function testSearchAndSortByRelevance() filters{ name request_var - filter_items_count + filter_items_count filter_items{ label items_count value_string __typename } - } + } aggregations{ attribute_code count @@ -1550,9 +1550,9 @@ public function testSearchAndSortByRelevance() value count } - } + } } - + } QUERY; $response = $this->graphQlQuery($query); @@ -1679,7 +1679,7 @@ public function testProductBasicFullTextSearchQuery() items_count label value_string - } + } } aggregations{ attribute_code @@ -1838,11 +1838,11 @@ public function testQueryFilterNoMatchingItems() { products( filter: - { + { price:{from:"50"} - + description:{match:"Description"} - + } pageSize:2 currentPage:1 @@ -1995,7 +1995,7 @@ public function testFilterProductsThatAreOutOfStockWithConfigSettings() sku:{eq:"simple_visible_in_stock"} } pageSize:20 - + ) { items @@ -2004,7 +2004,7 @@ public function testFilterProductsThatAreOutOfStockWithConfigSettings() name } total_count - + } } QUERY; diff --git a/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php b/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php index 1d582862c2428..2947bd352aab3 100644 --- a/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php +++ b/setup/src/Magento/Setup/Fixtures/AttributeSet/Pattern.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Setup\Fixtures\AttributeSet; /** @@ -32,7 +34,7 @@ class Pattern * @param string $name * @param int $attributesPerSet * @param int $optionsPerAttribute - * @param callable $attributePattern callback in f($index, $attributeData) format + * @param callable $attributePattern callback in f($index, $attributeData) format * @return array */ public function generateAttributeSet( @@ -46,9 +48,9 @@ public function generateAttributeSet( 'attributes' => [] ]; for ($index = 1; $index <= $attributesPerSet; $index++) { - $attributeData = $this->generateAttribute( + $attributeData = $this->generateAttribute( $index, - is_array($optionsPerAttribute) ? $optionsPerAttribute[$index-1] : $optionsPerAttribute + is_array($optionsPerAttribute) ? $optionsPerAttribute[$index - 1] : $optionsPerAttribute ); if (is_callable($attributePattern)) { $attributeData = $attributePattern($index, $attributeData); @@ -72,7 +74,7 @@ private function generateAttribute($index, $optionsPerAttribute) $attribute['attribute_code'] = $attribute['attribute_code'] . $index; $attribute['frontend_label'] = $attribute['frontend_label'] . $index; $attribute['options'] = ['option' => $this->generateOptions($optionsPerAttribute)]; - $attribute['default_option'] = $attribute['options']['option'][0]['label']; + $attribute['default_value'] = $attribute['options']['option'][0]['value']; return $attribute; } diff --git a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php index 8e2e842a7d805..84fb2e5beed4b 100644 --- a/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php +++ b/setup/src/Magento/Setup/Fixtures/SimpleProductsFixture.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Setup\Fixtures; use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Catalog\Model\ProductFactory; +use Magento\Eav\Model\Entity\Attribute; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection; use Magento\Eav\Model\ResourceModel\Entity\Attribute\CollectionFactory; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection as AttributeSetCollection; use Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\CollectionFactory as AttributeSetCollectionFactory; use Magento\Setup\Model\FixtureGenerator\ProductGenerator; use Magento\Setup\Model\SearchTermDescriptionGeneratorFactory; @@ -68,7 +72,7 @@ class SimpleProductsFixture extends Fixture private $defaultAttributeSetId; /** - * @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Collection + * @var Collection */ private $attributeCollectionFactory; @@ -97,6 +101,11 @@ class SimpleProductsFixture extends Fixture */ private $priceProvider; + /** + * @var int[] + */ + private $additionalAttributeSetIds; + /** * @param FixtureModel $fixtureModel * @param ProductFactory $productFactory @@ -184,35 +193,38 @@ public function execute() 'Short simple product Description %s' ); - $additionalAttributeSets = $this->getAdditionalAttributeSets(); - $attributeSet = function ($index) use ($defaultAttributeSets, $additionalAttributeSets) { + $additionalAttributeSetIds = $this->getAdditionalAttributeSetIds(); + $attributeSet = function ($index) use ($defaultAttributeSets, $additionalAttributeSetIds) { // phpcs:ignore mt_srand($index); $attributeSetCount = count(array_keys($defaultAttributeSets)); if ($attributeSetCount > (($index - 1) % (int)$this->fixtureModel->getValue('categories', 30))) { - // phpcs:ignore Magento2.Functions.DiscouragedFunction + // phpcs:ignore Magento2.Security.InsecureFunction return array_keys($defaultAttributeSets)[mt_rand(0, count(array_keys($defaultAttributeSets)) - 1)]; } else { - $customSetsAmount = count($additionalAttributeSets); + $customSetsAmount = count($additionalAttributeSetIds); return $customSetsAmount - ? $additionalAttributeSets[$index % count($additionalAttributeSets)]['attribute_set_id'] + ? $additionalAttributeSetIds[$index % $customSetsAmount] : $this->getDefaultAttributeSetId(); } }; + $additionalAttributeValues = $this->getAdditionalAttributeValues(); $additionalAttributes = function ( $attributeSetId, $index ) use ( $defaultAttributeSets, - $additionalAttributeSets + $additionalAttributeValues ) { $attributeValues = []; // phpcs:ignore mt_srand($index); - if (isset($defaultAttributeSets[$attributeSetId])) { - foreach ($defaultAttributeSets[$attributeSetId] as $attributeCode => $values) { - // phpcs:ignore Magento2.Functions.DiscouragedFunction + $attributeValuesByAttributeSet = $defaultAttributeSets[$attributeSetId] + ?? $additionalAttributeValues[$attributeSetId]; + if (!empty($attributeValuesByAttributeSet)) { + foreach ($attributeValuesByAttributeSet as $attributeCode => $values) { + // phpcs:ignore Magento2.Security.InsecureFunction $attributeValues[$attributeCode] = $values[mt_rand(0, count($values) - 1)]; } } @@ -279,10 +291,10 @@ private function getDefaultAttributeSetId() } /** - * Get default attribute sets with attributes + * Get default attribute sets with attributes. * - * @see config/attributeSets.xml * @return array + * @see config/attributeSets.xml */ private function getDefaultAttributeSets() { @@ -301,17 +313,7 @@ private function getDefaultAttributeSets() 'attribute_code', array_column($attributesData, 'attribute_code') ); - /** @var \Magento\Eav\Model\Entity\Attribute $attribute */ - foreach ($attributeCollection as $attribute) { - $values = []; - $options = $attribute->getOptions(); - foreach (($options ?: []) as $option) { - if ($option->getValue()) { - $values[] = $option->getValue(); - } - } - $attributes[$attribute->getAttributeSetId()][$attribute->getAttributeCode()] = $values; - } + $attributes = $this->processAttributeValues($attributeCollection, $attributes); } } $attributes[$this->getDefaultAttributeSetId()] = []; @@ -381,16 +383,64 @@ private function readDescriptionConfig($configSrc) } /** - * Get additional attribute sets + * Get additional attribute set ids. + * + * @return int[] + */ + private function getAdditionalAttributeSetIds() + { + if (null === $this->additionalAttributeSetIds) { + /** @var AttributeSetCollection $sets */ + $sets = $this->attributeSetCollectionFactory->create(); + $sets->addFieldToFilter( + 'attribute_set_name', + ['like' => AttributeSetsFixture::PRODUCT_SET_NAME . '%'] + ); + $this->additionalAttributeSetIds = $sets->getAllIds(); + } + + return $this->additionalAttributeSetIds; + } + + /** + * Get values of additional attributes. * - * @return \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection[] + * @return array */ - private function getAdditionalAttributeSets() + private function getAdditionalAttributeValues(): array { - /** @var \Magento\Eav\Model\ResourceModel\Entity\Attribute\Set\Collection $sets */ - $sets = $this->attributeSetCollectionFactory->create(); - $sets->addFieldToFilter('attribute_set_name', ['like' => AttributeSetsFixture::PRODUCT_SET_NAME . '%']); + $attributeCollection = $this->attributeCollectionFactory->create(); + $attributeCollection->setAttributeSetsFilter($this->getAdditionalAttributeSetIds()) + ->addFieldToFilter('attribute_code', ['like' => 'attribute_set%']); + $attributeCollection->getSelect()->columns(['entity_attribute.attribute_set_id']); + + return $this->processAttributeValues($attributeCollection); + } - return $sets->getData(); + /** + * Maps attribute values by attribute set and attribute code. + * + * @param Collection $attributeCollection + * @param array $attributes + * @return array + */ + private function processAttributeValues( + Collection $attributeCollection, + array $attributes = [] + ): array { + /** @var Attribute $attribute */ + foreach ($attributeCollection as $attribute) { + $values = []; + $options = $attribute->getOptions() ?? []; + $attributeSetId = $attribute->getAttributeSetId() ?? $this->getDefaultAttributeSetId(); + foreach ($options as $option) { + if ($option->getValue()) { + $values[] = $option->getValue(); + } + } + $attributes[$attributeSetId][$attribute->getAttributeCode()] = $values; + } + + return $attributes; } } diff --git a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php index f267343bd3fd3..dcb7430c0e82a 100644 --- a/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php +++ b/setup/src/Magento/Setup/Test/Unit/Fixtures/AttributeSet/PatternTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Setup\Test\Unit\Fixtures\AttributeSet; @@ -28,7 +29,7 @@ public function testGenerateAttributeSet() 'frontend_label' => 'Attribute 1', 'frontend_input' => 'select', 'backend_type' => 1, - 'default_option' => 'option 1', + 'default_value' => 'option_1', 'options' => [ 'option' => [ [