diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index b72bd8c441588..c950b49348dc3 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -656,11 +656,7 @@ public function getProductsIdsBySkus(array $productSkuList) */ private function getResultKey(string $sku, array $productSkuList): string { - $key = array_search(strtolower($sku), array_map('strtolower', $productSkuList)); - if ($key !== false) { - $sku = $productSkuList[$key]; - } - return $sku; + return in_array(strtolower($sku), array_map('strtolower', $productSkuList)) ? $sku : ''; } /** diff --git a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php index d4e049aedd9c2..d67a50875b81d 100644 --- a/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php +++ b/app/code/Magento/CatalogGraphQl/DataProvider/Product/SearchCriteriaBuilder.php @@ -8,18 +8,23 @@ namespace Magento\CatalogGraphQl\DataProvider\Product; use Magento\Catalog\Api\Data\EavAttributeInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Eav\Model\Config; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\Search\FilterGroupBuilder; use Magento\Framework\Api\Search\SearchCriteriaInterface; use Magento\Framework\Api\SortOrder; +use Magento\Framework\Api\SortOrderBuilder; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder; -use Magento\Catalog\Model\Product\Visibility; -use Magento\Framework\Api\SortOrderBuilder; /** * Build search criteria + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ + class SearchCriteriaBuilder { /** @@ -51,6 +56,11 @@ class SearchCriteriaBuilder */ private $sortOrderBuilder; + /** + * @var Config + */ + private Config $eavConfig; + /** * @param Builder $builder * @param ScopeConfigInterface $scopeConfig @@ -58,6 +68,7 @@ class SearchCriteriaBuilder * @param FilterGroupBuilder $filterGroupBuilder * @param Visibility $visibility * @param SortOrderBuilder $sortOrderBuilder + * @param Config $eavConfig */ public function __construct( Builder $builder, @@ -65,14 +76,16 @@ public function __construct( FilterBuilder $filterBuilder, FilterGroupBuilder $filterGroupBuilder, Visibility $visibility, - SortOrderBuilder $sortOrderBuilder + SortOrderBuilder $sortOrderBuilder = null, + Config $eavConfig = null ) { $this->scopeConfig = $scopeConfig; $this->filterBuilder = $filterBuilder; $this->filterGroupBuilder = $filterGroupBuilder; $this->builder = $builder; $this->visibility = $visibility; - $this->sortOrderBuilder = $sortOrderBuilder; + $this->sortOrderBuilder = $sortOrderBuilder ?? ObjectManager::getInstance()->get(SortOrderBuilder::class); + $this->eavConfig = $eavConfig ?? ObjectManager::getInstance()->get(Config::class); } /** @@ -87,9 +100,13 @@ public function build(array $args, bool $includeAggregation): SearchCriteriaInte $searchCriteria = $this->builder->build('products', $args); $isSearch = !empty($args['search']); $this->updateRangeFilters($searchCriteria); - if ($includeAggregation) { - $this->preparePriceAggregation($searchCriteria); + $attributeData = $this->eavConfig->getAttribute(Product::ENTITY, 'price'); + $priceOptions = $attributeData->getData(); + + if ($priceOptions['is_filterable'] != 0) { + $this->preparePriceAggregation($searchCriteria); + } $requestName = 'graphql_product_search_with_aggregation'; } else { $requestName = 'graphql_product_search'; @@ -142,7 +159,7 @@ private function addEntityIdSort(SearchCriteriaInterface $searchCriteria): void { $sortOrderArray = $searchCriteria->getSortOrders(); $sortDir = SortOrder::SORT_DESC; - if (count($sortOrderArray) > 0) { + if (is_array($sortOrderArray) && count($sortOrderArray) > 0) { $sortOrder = end($sortOrderArray); // in the case the last sort order is by position, sort IDs in descendent order $sortDir = $sortOrder->getField() === EavAttributeInterface::POSITION diff --git a/app/code/Magento/CatalogGraphQl/Test/Unit/DataProvider/Product/SearchCriteriaBuilderTest.php b/app/code/Magento/CatalogGraphQl/Test/Unit/DataProvider/Product/SearchCriteriaBuilderTest.php new file mode 100644 index 0000000000000..59970335d3d10 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Test/Unit/DataProvider/Product/SearchCriteriaBuilderTest.php @@ -0,0 +1,152 @@ +builder = $this->createMock(Builder::class); + $this->scopeConfig = $this->createMock(ScopeConfigInterface::class); + $this->filterBuilder = $this->createMock(FilterBuilder::class); + $this->filterGroupBuilder = $this->createMock(FilterGroupBuilder::class); + $this->sortOrderBuilder = $this->createMock(SortOrderBuilder::class); + $this->visibility = $this->createMock(Visibility::class); + $this->eavConfig = $this->createMock(Config::class); + $this->model = new SearchCriteriaBuilder( + $this->builder, + $this->scopeConfig, + $this->filterBuilder, + $this->filterGroupBuilder, + $this->visibility, + $this->sortOrderBuilder, + $this->eavConfig + ); + } + + public function testBuild(): void + { + $args = ['search' => '', 'pageSize' => 20, 'currentPage' => 1]; + + $filter = $this->createMock(Filter::class); + + $searchCriteria = $this->getMockBuilder(SearchCriteriaInterface::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $attributeInterface = $this->getMockBuilder(Attribute::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + + $attributeInterface->setData(['is_filterable' => 0]); + + $this->builder->expects($this->any()) + ->method('build') + ->with('products', $args) + ->willReturn($searchCriteria); + $searchCriteria->expects($this->any())->method('getFilterGroups')->willReturn([]); + $this->eavConfig->expects($this->any()) + ->method('getAttribute') + ->with(Product::ENTITY, 'price') + ->willReturn($attributeInterface); + + $this->sortOrderBuilder->expects($this->once()) + ->method('setField') + ->with('_id') + ->willReturnSelf(); + $this->sortOrderBuilder->expects($this->once()) + ->method('setDirection') + ->with('DESC') + ->willReturnSelf(); + $this->sortOrderBuilder->expects($this->any()) + ->method('create') + ->willReturn([]); + + $this->filterBuilder->expects($this->once()) + ->method('setField') + ->with('visibility') + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('setValue') + ->with("") + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('setConditionType') + ->with('in') + ->willReturnSelf(); + + $this->filterBuilder->expects($this->once())->method('create')->willReturn($filter); + + $this->filterGroupBuilder->expects($this->any()) + ->method('addFilter') + ->with($filter) + ->willReturnSelf(); + + $this->model->build($args, true); + } +} diff --git a/app/code/Magento/CatalogInventory/Test/Fixture/SourceItem.php b/app/code/Magento/CatalogInventory/Test/Fixture/SourceItem.php new file mode 100644 index 0000000000000..68a887c580860 --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Fixture/SourceItem.php @@ -0,0 +1,90 @@ + 'SKU-%uniqid%', + 'source_code' => 'Source%uniqid%', + 'quantity' => 5, + 'status' => SourceItemInterface::STATUS_IN_STOCK, + ]; + + /** + * @var ServiceFactory + */ + private ServiceFactory $serviceFactory; + + /** + * @var ProcessorInterface + */ + private ProcessorInterface $dataProcessor; + + /** + * @var DataMerger + */ + private DataMerger $dataMerger; + + /** + * @param ServiceFactory $serviceFactory + * @param ProcessorInterface $dataProcessor + * @param DataMerger $dataMerger + */ + public function __construct( + ServiceFactory $serviceFactory, + ProcessorInterface $dataProcessor, + DataMerger $dataMerger + ) { + $this->serviceFactory = $serviceFactory; + $this->dataProcessor = $dataProcessor; + $this->dataMerger = $dataMerger; + } + + /** + * @param array $data + * @return DataObject|null + */ + public function apply(array $data = []): ?DataObject + { + $service = $this->serviceFactory->create(SourceItemsSaveInterface::class, 'execute'); + + return $service->execute(['sourceItems' => [$this->prepareData($data)]]); + } + + /** + * @inheritdoc + */ + public function revert(DataObject $data): void + { + $service = $this->serviceFactory->create(SourceItemsDeleteInterface::class, 'execute'); + $service->execute(['sourceItems' => [$this->prepareData($data->getData())]]); + } + + /** + * Prepare product data + * + * @param array $data + * @return array + */ + private function prepareData(array $data): array + { + $data = $this->dataMerger->merge(self::DEFAULT_DATA, $data, false); + + return $this->dataProcessor->process($this, $data); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php index d329bdf0200a4..629fedde8caa1 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Products/AdaptUrlRewritesToVisibilityAttribute.php @@ -79,6 +79,7 @@ public function execute(array $productIds, int $visibility, int $storeId): void [ UrlRewrite::ENTITY_ID => $product->getId(), UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::STORE_ID => $storeId, ] ); } elseif ($visibility !== Visibility::VISIBILITY_NOT_VISIBLE) { diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index b4351e6c5727d..e7a6203d8d15b 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -111,6 +111,9 @@ public function collectRates(RateRequest $request) } } elseif ($item->getProduct()->isVirtual()) { $request->setPackageValue($request->getPackageValue() - $item->getBaseRowTotal()); + $request->setPackageValueWithDiscount( + $request->getPackageValueWithDiscount() - $item->getBaseRowTotal() + ); } } } @@ -133,8 +136,7 @@ public function collectRates(RateRequest $request) $freeQty += $item->getQty() * ($child->getQty() - $freeShipping); } } - } elseif ( - ($item->getFreeShipping() || $item->getAddress()->getFreeShipping()) && + } elseif (($item->getFreeShipping() || $item->getAddress()->getFreeShipping()) && ($item->getFreeShippingMethod() == null || $item->getFreeShippingMethod() && $item->getFreeShippingMethod() == 'tablerate_bestway') ) { diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php index aef9fb04c19ae..64725d8d40a1c 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Shipping.php @@ -73,7 +73,7 @@ public function collect( $address->setFreeMethodWeight($data['freeMethodWeight']); $isFreeShipping = $this->freeShipping->isFreeShipping($quote, $shippingAssignment->getItems()); - $address->setFreeShipping($isFreeShipping); + $address->setFreeShipping((int)$isFreeShipping); // recalculate weights $data = $this->getAssignmentWeightData($address, $shippingAssignment->getItems()); $address->setItemQty($data['addressQty']); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php index d889240de5b02..495a19c97b3ef 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php @@ -209,11 +209,16 @@ public function testCollect(): void $this->shippingAssignment->expects($this->atLeastOnce()) ->method('getItems') ->willReturn([$this->cartItem]); - $this->freeShipping->method('isFreeShipping') + $isFreeShipping = true; + $this->freeShipping + ->expects($this->once()) + ->method('isFreeShipping') ->with($this->quote, [$this->cartItem]) - ->willReturn(true); - $this->address->method('setFreeShipping') - ->with(true); + ->willReturn($isFreeShipping); + $this->address + ->expects($this->once()) + ->method('setFreeShipping') + ->with((int)$isFreeShipping); $this->total->expects($this->atLeastOnce()) ->method('setTotalAmount'); $this->total->expects($this->atLeastOnce()) diff --git a/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php b/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php index 449ab230b568d..c2639c8bb5664 100644 --- a/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Ui/Component/Listing/Column/PriceTest.php @@ -76,8 +76,7 @@ public function testPrepareDataSource( array $dataSource, string $currencyCode, ?int $expectedStoreId = null - ): void - { + ): void { $itemName = 'itemName'; $oldItemValue = 'oldItemValue'; $newItemValue = 'newItemValue'; @@ -159,11 +158,24 @@ public function testPrepareDataSourceDataProvider(): array ] ] ]; + $dataSource5 = [ + 'data' => [ + 'items' => [ + [ + 'itemName' => 'oldItemValue', + 'store_id' => '123Test', + 'base_currency_code' => '', + ] + ] + ] + ]; + return [ [true, $dataSource1, 'US'], [false, $dataSource2, 'SAR'], [false, $dataSource3, 'SAR', 2], [false, $dataSource4, 'SAR'], + [false, $dataSource5, 'INR'], ]; } } diff --git a/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php b/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php index cc323730f14b4..38655cf4a69d8 100644 --- a/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php +++ b/app/code/Magento/Sales/Ui/Component/Listing/Column/Price.php @@ -76,13 +76,13 @@ public function prepareDataSource(array $dataSource) { if (isset($dataSource['data']['items'])) { foreach ($dataSource['data']['items'] as & $item) { - $currencyCode = isset($item['base_currency_code']) ? $item['base_currency_code'] : null; + $currencyCode = $item['base_currency_code'] ?? null; + if (!$currencyCode) { - $storeId = isset($item['store_id']) && (int)$item['store_id'] !== 0 ? $item['store_id'] : + $itemStoreId = $item['store_id'] ?? ''; + $storeId = $itemStoreId && is_numeric($itemStoreId) ? $itemStoreId : $this->context->getFilterParam('store_id', Store::DEFAULT_STORE_ID); - $store = $this->storeManager->getStore( - $storeId - ); + $store = $this->storeManager->getStore($storeId); $currencyCode = $store->getBaseCurrency()->getCurrencyCode(); } $basePurchaseCurrency = $this->currency->load($currencyCode); diff --git a/app/code/Magento/SalesRule/etc/extension_attributes.xml b/app/code/Magento/SalesRule/etc/extension_attributes.xml index c69c309d8741b..8cdf0fd7cd39c 100644 --- a/app/code/Magento/SalesRule/etc/extension_attributes.xml +++ b/app/code/Magento/SalesRule/etc/extension_attributes.xml @@ -12,4 +12,7 @@ - \ No newline at end of file + + + + diff --git a/app/code/Magento/Wishlist/Block/Rss/Link.php b/app/code/Magento/Wishlist/Block/Rss/Link.php index 28affa8d3372d..4f24d2d118fbd 100644 --- a/app/code/Magento/Wishlist/Block/Rss/Link.php +++ b/app/code/Magento/Wishlist/Block/Rss/Link.php @@ -9,42 +9,48 @@ */ namespace Magento\Wishlist\Block\Rss; +use Magento\Framework\App\Rss\UrlBuilderInterface; +use Magento\Framework\Url\EncoderInterface; +use Magento\Framework\View\Element\Template; +use Magento\Framework\View\Element\Template\Context; +use Magento\Wishlist\Helper\Data; + /** * @api * @since 100.0.2 */ -class Link extends \Magento\Framework\View\Element\Template +class Link extends Template { /** - * @var \Magento\Wishlist\Helper\Data + * @var Data */ - protected $wishlistHelper; + protected Data $wishlistHelper; /** - * @var \Magento\Framework\App\Rss\UrlBuilderInterface + * @var UrlBuilderInterface */ - protected $rssUrlBuilder; + protected UrlBuilderInterface $rssUrlBuilder; /** - * @var \Magento\Framework\Url\EncoderInterface + * @var EncoderInterface */ - protected $urlEncoder; + protected EncoderInterface $urlEncoder; /** - * @param \Magento\Framework\View\Element\Template\Context $context - * @param \Magento\Wishlist\Helper\Data $wishlistHelper - * @param \Magento\Framework\App\Rss\UrlBuilderInterface $rssUrlBuilder - * @param \Magento\Framework\Url\EncoderInterface $urlEncoder + * @param Context $context + * @param Data $wishlistHelper + * @param UrlBuilderInterface $rssUrlBuilder + * @param EncoderInterface $urlEncoder * @param array $data */ public function __construct( - \Magento\Framework\View\Element\Template\Context $context, - \Magento\Wishlist\Helper\Data $wishlistHelper, - \Magento\Framework\App\Rss\UrlBuilderInterface $rssUrlBuilder, - \Magento\Framework\Url\EncoderInterface $urlEncoder, + Context $context, + Data $wishlistHelper, + UrlBuilderInterface $rssUrlBuilder, + EncoderInterface $urlEncoder, array $data = [] ) { - $data['wishlistHelper'] = $this->wishlistHelper; + $data['wishlistHelper'] = $wishlistHelper; parent::__construct($context, $data); $this->wishlistHelper = $wishlistHelper; $this->rssUrlBuilder = $rssUrlBuilder; diff --git a/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontShareCustomerWishlistWithRssFeedLinkActionGroup.xml b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontShareCustomerWishlistWithRssFeedLinkActionGroup.xml new file mode 100644 index 0000000000000..6664242951e3d --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/ActionGroup/StorefrontShareCustomerWishlistWithRssFeedLinkActionGroup.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml index cf87432b3ba79..cc6e9c489a652 100755 --- a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml @@ -45,4 +45,20 @@ wishlist/general/show_in_sidebar 0 + + rss/config/active + 0 + + + rss/wishlist/active + 0 + + + rss/config/active + 1 + + + rss/wishlist/active + 1 + diff --git a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml index 3f16133be96a9..1db9f5754638a 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Section/StorefrontCustomerWishlistShareSection.xml @@ -11,6 +11,7 @@
+
diff --git a/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistWithRssFeedLinkTest.xml b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistWithRssFeedLinkTest.xml new file mode 100644 index 0000000000000..75bc9263c199e --- /dev/null +++ b/app/code/Magento/Wishlist/Test/Mftf/Test/StorefrontShareWishlistWithRssFeedLinkTest.xml @@ -0,0 +1,67 @@ + + + + + + + + + + <description value="Customer should be able to share wishlist with RSS feed link"/> + <severity value="AVERAGE"/> + <testCaseId value="AC-2445"/> + <group value="wishlist"/> + </annotations> + <before> + <magentoCLI command="config:set {{EnableRssConfig.path}} {{EnableRssConfig.value}}" + stepKey="enableRssConfig"/> + <magentoCLI command="config:set {{EnableWishlistRssConfig.path}} {{EnableWishlistRssConfig.value}}" + stepKey="enableWishlistRssConfig"/> + <magentoCLI command="cache:clean config full_page block_html" stepKey="cleanCache"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + </before> + <after> + <magentoCLI command="config:set {{DisableRssConfig.path}} {{DisableRssConfig.value}}" + stepKey="disableRssConfig"/> + <magentoCLI command="config:set {{DisableWishlistRssConfig.path}} {{DisableWishlistRssConfig.value}}" + stepKey="disableWishlistRssConfig"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <!-- Customer logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutCustomer"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + </after> + + <!-- Sign in as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$createCustomer$"/> + </actionGroup> + + <actionGroup ref="OpenProductFromCategoryPageActionGroup" stepKey="openProductFromCategory"> + <argument name="category" value="$createCategory$"/> + <argument name="product" value="$createProduct$"/> + </actionGroup> + + <actionGroup ref="StorefrontCustomerAddProductToWishlistActionGroup" stepKey="addToWishlistProduct"> + <argument name="productVar" value="$createProduct$"/> + </actionGroup> + + <actionGroup ref="StorefrontShareCustomerWishlistWithRssFeedLinkActionGroup" stepKey="shareWishlistWithRssFeed"> + <argument name="email" value="{{Wishlist.shareInfo_emails}}"/> + <argument name="message" value="{{Wishlist.shareInfo_message}}"/> + </actionGroup> + <actionGroup ref="AssertStorefrontCustomerMessagesActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="Your wish list has been shared."/> + </actionGroup> + </test> +</tests> diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php index 7eb7400837f81..3554a6c72a77f 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/ProcessUrlRewriteOnChangeVisibilityObserverTest.php @@ -67,8 +67,7 @@ public function testMakeProductInvisibleViaMassAction() /** @var StoreManagerInterface $storeManager */ $storeManager = $this->objectManager->get(StoreManagerInterface::class); - $storeManager->setCurrentStore(0); - + $firstStore = current($product->getStoreIds()); $testStore = $storeManager->getStore('test'); $productFilter = [ UrlRewrite::ENTITY_TYPE => 'product', @@ -80,7 +79,7 @@ public function testMakeProductInvisibleViaMassAction() 'target_path' => "catalog/product/view/id/" . $product->getId(), 'is_auto_generated' => 1, 'redirect_type' => 0, - 'store_id' => '1', + 'store_id' => $firstStore, ], [ 'request_path' => "product-1.html", @@ -100,12 +99,14 @@ public function testMakeProductInvisibleViaMassAction() 'catalog_product_attribute_update_before', [ 'attributes_data' => [ ProductInterface::VISIBILITY => Visibility::VISIBILITY_NOT_VISIBLE ], - 'product_ids' => [$product->getId()] + 'product_ids' => [$product->getId()], + 'store_id' => $firstStore, ] ); $actual = $this->getActualResults($productFilter); - $this->assertCount(0, $actual); + //Initially count was 2, when visibility of 1 store view is set to not visible individually, the new count is 1 + $this->assertCount(1, $actual); } /** diff --git a/dev/tests/integration/testsuite/Magento/Cookie/Model/Config/Backend/DomainTest.php b/dev/tests/integration/testsuite/Magento/Cookie/Model/Config/Backend/DomainTest.php index 392bb9f7f1bad..8b56c9f94e733 100644 --- a/dev/tests/integration/testsuite/Magento/Cookie/Model/Config/Backend/DomainTest.php +++ b/dev/tests/integration/testsuite/Magento/Cookie/Model/Config/Backend/DomainTest.php @@ -6,13 +6,15 @@ namespace Magento\Cookie\Model\Config\Backend; use Magento\Framework\Exception\LocalizedException; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; /** * Test \Magento\Cookie\Model\Config\Backend\Domain * * @magentoAppArea adminhtml */ -class DomainTest extends \PHPUnit\Framework\TestCase +class DomainTest extends TestCase { /** * @param string $value @@ -22,10 +24,8 @@ class DomainTest extends \PHPUnit\Framework\TestCase */ public function testBeforeSave($value, $exceptionMessage = null) { - /** @var $domain \Magento\Cookie\Model\Config\Backend\Domain */ - $domain = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\Cookie\Model\Config\Backend\Domain::class - ); + /** @var $domain Domain */ + $domain = Bootstrap::getObjectManager()->create(Domain::class); $domain->setValue($value); $domain->setPath('path'); try { @@ -45,18 +45,19 @@ public function testBeforeSave($value, $exceptionMessage = null) /** * @return array */ - public function beforeSaveDataProvider() + public function beforeSaveDataProvider(): array { return [ - 'not string' => [['array'], 'Invalid domain name: must be a string'], - 'invalid hostname' => [ + 'notString' => [['array'], 'Invalid domain name: must be a string'], + 'invalidHostname' => [ 'http://', 'Invalid domain name: The input does not match the expected structure for a DNS hostname; ' . 'The input does not appear to be a valid URI hostname; ' . 'The input does not appear to be a valid local network name', ], - 'valid hostname' => ['hostname.com'], - 'empty string' => [''], + 'validHostname' => ['hostname.com'], + 'emptyString' => [''], + 'invalidCharacter' => ['hostname,com', 'Invalid domain name: invalid character in cookie domain'], ]; } } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php index e938945f2c411..b88eccd69f2de 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/ShippingMethodManagementTest.php @@ -11,7 +11,6 @@ use Magento\Customer\Api\Data\GroupInterface; use Magento\Customer\Api\GroupRepositoryInterface; use Magento\Customer\Model\Vat; -use Magento\Customer\Observer\AfterAddressSaveObserver; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\Config\MutableScopeConfigInterface; use Magento\Framework\DataObject; @@ -23,12 +22,13 @@ use Magento\Quote\Api\GuestShippingMethodManagementInterface; use Magento\Quote\Api\ShippingMethodManagementInterface; use Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver; -use Magento\Quote\Observer\Frontend\Quote\Address\VatValidator; use Magento\Store\Model\ScopeInterface; use Magento\Tax\Api\Data\TaxClassInterface; use Magento\Tax\Api\TaxClassRepositoryInterface; use Magento\Tax\Model\ClassModel; use Magento\Tax\Model\Config as TaxConfig; +use Magento\TestFramework\Fixture\DataFixtureStorage; +use Magento\TestFramework\Fixture\DataFixtureStorageManager; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\Quote\Model\GetQuoteByReservedOrderId; use PHPUnit\Framework\TestCase; @@ -50,6 +50,11 @@ class ShippingMethodManagementTest extends TestCase /** @var TaxClassRepositoryInterface $taxClassRepository */ private $taxClassRepository; + /** + * @var DataFixtureStorage + */ + private $fixtures; + /** * @inheritdoc */ @@ -58,6 +63,7 @@ protected function setUp(): void $this->objectManager = Bootstrap::getObjectManager(); $this->groupRepository = $this->objectManager->get(GroupRepositoryInterface::class); $this->taxClassRepository = $this->objectManager->get(TaxClassRepositoryInterface::class); + $this->fixtures = $this->objectManager->get(DataFixtureStorageManager::class)->getStorage(); } /** @@ -127,6 +133,56 @@ public function testTableRateFreeShipping() } } + /** + * phpcs:disable Generic.Files.LineLength.TooLong + * + * @magentoConfigFixture default_store carriers/tablerate/active 1 + * @magentoConfigFixture default_store carriers/flatrate/active 0 + * @magentoConfigFixture current_store carriers/tablerate/condition_name package_value_with_discount + * @magentoConfigFixture default_store carriers/tablerate/include_virtual_price 0 + * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"simple", "type_id":"simple"} as:p1 + * @magentoDataFixture Magento\Catalog\Test\Fixture\Product with:{"sku":"virtual", "type_id":"virtual", "weight":0} as:p2 + * @magentoDataFixture Magento\Quote\Test\Fixture\GuestCart as:cart + * @magentoDataFixture Magento\Quote\Test\Fixture\AddProductToCart with:{"cart_id":"$cart.id$", "product_id":"$p1.id$"} + * @magentoDataFixture Magento\Quote\Test\Fixture\AddProductToCart with:{"cart_id":"$cart.id$", "product_id":"$p2.id$"} + * @magentoDataFixture Magento\Quote\Test\Fixture\SetBillingAddress with:{"cart_id":"$cart.id$"} + * @magentoDataFixture Magento\Quote\Test\Fixture\SetShippingAddress with:{"cart_id":"$cart.id$"} + * @magentoDataFixture Magento/OfflineShipping/_files/tablerates_price.php + * @return void + * @throws NoSuchEntityException + */ + public function testTableRateWithoutIncludingVirtualProduct() + { + $cartId = (int)$this->fixtures->get('cart')->getId(); + + if (!$cartId) { + $this->fail('quote fixture failed'); + } + + /** @var QuoteRepository $quoteRepository */ + $quoteRepository = $this->objectManager->get(QuoteRepository::class); + $quote = $quoteRepository->get($cartId); + + /** @var QuoteIdToMaskedQuoteIdInterface $maskedQuoteId */ + $maskedQuoteId = $this->objectManager->get(QuoteIdToMaskedQuoteIdInterface::class)->execute($cartId); + + /** @var GuestShippingMethodManagementInterface $shippingEstimation */ + $shippingEstimation = $this->objectManager->get(GuestShippingMethodManagementInterface::class); + $result = $shippingEstimation->estimateByExtendedAddress( + $maskedQuoteId, + $quote->getShippingAddress() + ); + + $this->assertCount(1, $result); + $rate = reset($result); + $expectedResult = [ + 'method_code' => 'bestway', + 'amount' => 15, + ]; + $this->assertEquals($expectedResult['method_code'], $rate->getMethodCode()); + $this->assertEquals($expectedResult['amount'], $rate->getAmount()); + } + /** * Test table rate amount for the cart that contains some items with free shipping applied. * diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_cart.php b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_cart.php index 21dcae2c6de38..1c2c297fba4c0 100644 --- a/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_cart.php +++ b/dev/tests/integration/testsuite/Magento/SalesRule/_files/cart_rule_free_shipping_by_cart.php @@ -64,7 +64,7 @@ 'is_rss' => 1, 'use_auto_generation' => 0, 'uses_per_coupon' => 0, - 'simple_free_shipping' => 2, + 'simple_free_shipping' => 1, 'website_ids' => [ $objectManager->get(StoreManagerInterface::class)->getWebsite()->getId() diff --git a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php index e145484d22a43..5b794c26b2dda 100644 --- a/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php +++ b/lib/internal/Magento/Framework/Session/Config/Validator/CookieDomainValidator.php @@ -6,10 +6,13 @@ namespace Magento\Framework\Session\Config\Validator; +use Laminas\Validator\Hostname; +use Magento\Framework\Validator\AbstractValidator; + /** * Session cookie domain validator */ -class CookieDomainValidator extends \Magento\Framework\Validator\AbstractValidator +class CookieDomainValidator extends AbstractValidator { /** * @inheritDoc @@ -17,17 +20,29 @@ class CookieDomainValidator extends \Magento\Framework\Validator\AbstractValidat public function isValid($value) { $this->_clearMessages(); + if (!is_string($value)) { $this->_addMessages(['must be a string']); + return false; } - $validator = new \Laminas\Validator\Hostname(\Laminas\Validator\Hostname::ALLOW_ALL); + //Hostname validator allows [;,] and returns the validator as true but, + //these are unacceptable cookie domain characters hence need explicit validation for the same + if (preg_match('/[;,]/', $value)) { + $this->_addMessages(['invalid character in cookie domain']); + + return false; + } + + $validator = new Hostname(Hostname::ALLOW_ALL); if (!empty($value) && !$validator->isValid($value)) { $this->_addMessages($validator->getMessages()); + return false; } + return true; } }