Skip to content

Commit 84fe1f9

Browse files
committed
MC-29270: Storefront: Visible/Not-Visible child products of configurable product on storefront
1 parent a7a572b commit 84fe1f9

File tree

7 files changed

+600
-41
lines changed

7 files changed

+600
-41
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\TestFramework\Catalog\Model\Layer;
9+
10+
use Magento\Catalog\Model\Layer\Search as CatalogLayerSearch;
11+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
12+
use Magento\Framework\ObjectManagerInterface;
13+
use Magento\Framework\Search\Request\Builder;
14+
use Magento\Framework\Search\Request\Config as RequestConfig;
15+
use Magento\Search\Model\Search;
16+
17+
/**
18+
* Quick search products by query.
19+
*/
20+
class QuickSearchByQuery
21+
{
22+
/**
23+
* @var ObjectManager
24+
*/
25+
private $objectManager;
26+
27+
/**
28+
* @param ObjectManagerInterface $objectManager
29+
*/
30+
public function __construct(
31+
ObjectManagerInterface $objectManager
32+
) {
33+
$this->objectManager = $objectManager;
34+
}
35+
36+
/**
37+
* Flush search instances cache and find products by search query.
38+
*
39+
* @param string $query
40+
* @return Collection
41+
*/
42+
public function execute(string $query): Collection
43+
{
44+
$this->removeInstancesCache();
45+
$productCollection = $this->getCatalogLayerSearch()->getProductCollection();
46+
$productCollection->addSearchFilter($query);
47+
$productCollection->setOrder('relevance', 'desc');
48+
49+
return $productCollection;
50+
}
51+
52+
/**
53+
* Retrieve empty catalog layer search instance.
54+
*
55+
* @return CatalogLayerSearch
56+
*/
57+
private function getCatalogLayerSearch(): CatalogLayerSearch
58+
{
59+
return $this->objectManager->create(CatalogLayerSearch::class);
60+
}
61+
62+
/**
63+
* Remove instances cache which related to search.
64+
*
65+
* @return void
66+
*/
67+
private function removeInstancesCache(): void
68+
{
69+
$this->objectManager->removeSharedInstance(RequestConfig::class);
70+
$this->objectManager->removeSharedInstance(Builder::class);
71+
$this->objectManager->removeSharedInstance(Search::class);
72+
}
73+
}

dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
namespace Magento\CatalogSearch\Model\Search;
1010

1111
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
12-
use Magento\Catalog\Model\Layer\Search as CatalogLayerSearch;
1312
use Magento\Catalog\Model\Product;
1413
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\CollectionFactory;
15-
use Magento\Framework\Search\Request\Builder;
16-
use Magento\Framework\Search\Request\Config as RequestConfig;
17-
use Magento\Search\Model\Search;
14+
use Magento\TestFramework\Catalog\Model\Layer\QuickSearchByQuery;
1815
use Magento\TestFramework\Helper\Bootstrap;
1916
use Magento\TestFramework\ObjectManager;
17+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
2018
use PHPUnit\Framework\TestCase;
2119

2220
/**
@@ -42,9 +40,9 @@ class AttributeSearchWeightTest extends TestCase
4240
private $collectedAttributesWeight = [];
4341

4442
/**
45-
* @var CatalogLayerSearch
43+
* @var QuickSearchByQuery
4644
*/
47-
private $catalogLayerSearch;
45+
private $quickSearchByQuery;
4846

4947
/**
5048
* @inheritdoc
@@ -53,7 +51,7 @@ protected function setUp()
5351
{
5452
$this->objectManager = Bootstrap::getObjectManager();
5553
$this->productAttributeRepository = $this->objectManager->get(ProductAttributeRepositoryInterface::class);
56-
$this->catalogLayerSearch = $this->objectManager->get(CatalogLayerSearch::class);
54+
$this->quickSearchByQuery = $this->objectManager->get(QuickSearchByQuery::class);
5755
$this->collectCurrentProductAttributesWeights();
5856
}
5957

@@ -85,9 +83,7 @@ public function testAttributeSearchWeight(
8583
array $expectedProductNames
8684
): void {
8785
$this->updateAttributesWeight($attributeWeights);
88-
$this->removeInstancesCache();
89-
$products = $this->findProducts($searchQuery);
90-
$actualProductNames = $this->collectProductsName($products);
86+
$actualProductNames = $this->collectProductsName($this->quickSearchByQuery->execute($searchQuery));
9187
$this->assertEquals($expectedProductNames, $actualProductNames, 'Products order is not as expected.');
9288
}
9389

@@ -175,47 +171,19 @@ protected function updateAttributesWeight(array $attributeWeights): void
175171
/**
176172
* Get all names from founded products.
177173
*
178-
* @param Product[] $products
174+
* @param Collection $productsCollection
179175
* @return array
180176
*/
181-
protected function collectProductsName(array $products): array
177+
protected function collectProductsName(Collection $productsCollection): array
182178
{
183179
$result = [];
184-
foreach ($products as $product) {
180+
foreach ($productsCollection as $product) {
185181
$result[] = $product->getName();
186182
}
187183

188184
return $result;
189185
}
190186

191-
/**
192-
* Reindex catalogsearch fulltext index.
193-
*
194-
* @return void
195-
*/
196-
protected function removeInstancesCache(): void
197-
{
198-
$this->objectManager->removeSharedInstance(RequestConfig::class);
199-
$this->objectManager->removeSharedInstance(Builder::class);
200-
$this->objectManager->removeSharedInstance(Search::class);
201-
$this->objectManager->removeSharedInstance(CatalogLayerSearch::class);
202-
}
203-
204-
/**
205-
* Find products by search query.
206-
*
207-
* @param string $query
208-
* @return Product[]
209-
*/
210-
protected function findProducts(string $query): array
211-
{
212-
$testProductCollection = $this->catalogLayerSearch->getProductCollection();
213-
$testProductCollection->addSearchFilter($query);
214-
$testProductCollection->setOrder('relevance', 'desc');
215-
216-
return $testProductCollection->getItems();
217-
}
218-
219187
/**
220188
* Collect weight of attributes which use in test.
221189
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\ConfigurableProduct\Block\Product\View\Type;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Catalog\Block\Product\View;
12+
use Magento\Catalog\Model\Product\Visibility;
13+
use Magento\ConfigurableProduct\Helper\Data;
14+
use Magento\ConfigurableProduct\Model\ConfigurableAttributeData;
15+
use Magento\Framework\Json\EncoderInterface;
16+
use Magento\Framework\Registry;
17+
use Magento\Framework\View\Result\Page;
18+
use Magento\TestFramework\Helper\Bootstrap;
19+
use Magento\TestFramework\ObjectManager;
20+
use PHPUnit\Framework\TestCase;
21+
22+
/**
23+
* Test cases related to render configurable options.
24+
*
25+
* @magentoAppArea frontend
26+
* @magentoDbIsolation enabled
27+
*/
28+
class RenderConfigurableOptionsTest extends TestCase
29+
{
30+
/**
31+
* @var ObjectManager
32+
*/
33+
private $objectManager;
34+
35+
/**
36+
* @var EncoderInterface
37+
*/
38+
private $jsonEncoder;
39+
40+
/**
41+
* @var Data
42+
*/
43+
private $configurableHelper;
44+
45+
/**
46+
* @var ProductRepositoryInterface
47+
*/
48+
private $productRepository;
49+
50+
/**
51+
* @var ConfigurableAttributeData
52+
*/
53+
private $configurableAttributeData;
54+
55+
/**
56+
* @var Registry
57+
*/
58+
private $registry;
59+
60+
/**
61+
* @var Page
62+
*/
63+
private $page;
64+
65+
/**
66+
* @inheritdoc
67+
*/
68+
protected function setUp()
69+
{
70+
$this->objectManager = Bootstrap::getObjectManager();
71+
$this->jsonEncoder = $this->objectManager->get(EncoderInterface::class);
72+
$this->configurableHelper = $this->objectManager->get(Data::class);
73+
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
74+
$this->configurableAttributeData = $this->objectManager->get(ConfigurableAttributeData::class);
75+
$this->registry = $this->objectManager->get(Registry::class);
76+
$this->page = $this->objectManager->create(Page::class);
77+
parent::setUp();
78+
}
79+
80+
/**
81+
* Assert that all configurable options was rendered correctly if one of
82+
* child product is visible on catalog\search.
83+
*
84+
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_two_child_products.php
85+
*
86+
* @return void
87+
*/
88+
public function testRenderConfigurableOptionsBlockWithOneVisibleOption(): void
89+
{
90+
$configurableProduct = $this->productRepository->get('Configurable product');
91+
$childProduct = $this->productRepository->get('Simple option 1');
92+
$childProduct->setVisibility(Visibility::VISIBILITY_BOTH);
93+
$this->productRepository->save($childProduct);
94+
$allProducts = $configurableProduct->getTypeInstance()->getUsedProducts($configurableProduct, null);
95+
$options = $this->configurableHelper->getOptions($configurableProduct, $allProducts);
96+
$confAttrData = $this->configurableAttributeData->getAttributesData($configurableProduct, $options);
97+
$attributesJson = str_replace(
98+
['[', ']'],
99+
['\[', '\]'],
100+
$this->jsonEncoder->encode($confAttrData['attributes'])
101+
);
102+
$optionsHtml = $this->getConfigurableOptionsHtml('Configurable product');
103+
$this->assertRegExp("/\"spConfig\": {\"attributes\":{$attributesJson}/", $optionsHtml);
104+
}
105+
106+
/**
107+
* Render configurable options block.
108+
*
109+
* @param string $configurableSku
110+
* @return string
111+
*/
112+
private function getConfigurableOptionsHtml(string $configurableSku): string
113+
{
114+
$product = $this->productRepository->get($configurableSku);
115+
$this->registry->unregister('product');
116+
$this->registry->register('product', $product);
117+
$optionsBlock = $this->getOptionsWrapperBlockWithOnlyConfigurableBlock();
118+
$optionHtml = $optionsBlock->toHtml();
119+
$this->registry->unregister('product');
120+
121+
return $optionHtml;
122+
}
123+
124+
/**
125+
* Get options wrapper without extra blocks(only configurable child block).
126+
*
127+
* @return View
128+
*/
129+
private function getOptionsWrapperBlockWithOnlyConfigurableBlock(): View
130+
{
131+
$this->page->addHandle([
132+
'default',
133+
'catalog_product_view',
134+
'catalog_product_view_type_configurable',
135+
]);
136+
$this->page->getLayout()->generateXml();
137+
138+
/** @var View $productInfoOptionsWrapper */
139+
$productInfoOptionsWrapper = $this->page->getLayout()->getBlock('product.info.options.wrapper');
140+
$productInfoOptionsWrapper->unsetChild('product_options');
141+
$productInfoOptionsWrapper->unsetChild('html_calendar');
142+
143+
return $productInfoOptionsWrapper;
144+
}
145+
}

0 commit comments

Comments
 (0)