Skip to content

Commit d05be5c

Browse files
committed
MC-29293: Storefront: Configurable Product with Out Of Stock Child(s)
1 parent 2e995b9 commit d05be5c

File tree

5 files changed

+625
-0
lines changed

5 files changed

+625
-0
lines changed
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\ConfigurableProduct\Block\Product\View\Type;
9+
10+
use Magento\Catalog\Block\Product\ListProduct;
11+
use Magento\Framework\ObjectManagerInterface;
12+
use Magento\Framework\View\LayoutInterface;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
use PHPUnit\Framework\TestCase;
15+
16+
/**
17+
* Class checks configurable product displaying on category view page
18+
*
19+
* @magentoDbIsolation disabled
20+
* @magentoAppIsolation enabled
21+
*/
22+
class ConfigurableViewOnCategoryPageTest extends TestCase
23+
{
24+
/** @var ObjectManagerInterface */
25+
private $objectManager;
26+
27+
/** @var LayoutInterface */
28+
private $layout;
29+
30+
/** @var ListProduct $listingBlock */
31+
private $listingBlock;
32+
33+
/**
34+
* @inheritdoc
35+
*/
36+
protected function setUp()
37+
{
38+
parent::setUp();
39+
40+
$this->objectManager = Bootstrap::getObjectManager();
41+
$this->layout = $this->objectManager->get(LayoutInterface::class);
42+
$this->listingBlock = $this->layout->createBlock(ListProduct::class);
43+
$this->listingBlock->setCategoryId(333);
44+
}
45+
46+
/**
47+
* @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1
48+
*
49+
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php
50+
*
51+
* @return void
52+
*/
53+
public function testOutOfStockProductWithEnabledConfigView(): void
54+
{
55+
$collection = $this->listingBlock->getLoadedProductCollection();
56+
$this->assertEquals(1, $collection->getSize());
57+
$this->assertCount(1, $collection->getItems());
58+
}
59+
60+
/**
61+
* @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 0
62+
*
63+
* @magentoDataFixture Magento/ConfigurableProduct/_files/configurable_product_with_out_of_stock_children.php
64+
*
65+
* @return void
66+
*/
67+
public function testOutOfStockProductWithDisabledConfigView(): void
68+
{
69+
$collection = $this->listingBlock->getLoadedProductCollection();
70+
$this->assertEquals(0, $collection->getSize());
71+
$this->assertCount(0, $collection->getItems());
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
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\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
13+
use Magento\Catalog\Model\ResourceModel\Product as ProductResource;
14+
use Magento\CatalogInventory\Api\Data\StockStatusInterface;
15+
use Magento\Framework\ObjectManagerInterface;
16+
use Magento\Framework\Serialize\SerializerInterface;
17+
use Magento\Framework\View\LayoutInterface;
18+
use Magento\Store\Model\Store;
19+
use Magento\Store\Model\StoreManagerInterface;
20+
use Magento\TestFramework\Helper\Bootstrap;
21+
use PHPUnit\Framework\TestCase;
22+
23+
/**
24+
* Class checks configurable product view with out of stock children
25+
*
26+
* @magentoAppArea frontend
27+
* @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
28+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
29+
*/
30+
class ConfigurableViewOnProductPageTest extends TestCase
31+
{
32+
private const STOCK_DISPLAY_TEMPLATE = 'Magento_Catalog::product/view/type/default.phtml';
33+
34+
/** @var ObjectManagerInterface */
35+
private $objectManager;
36+
37+
/** @var ProductRepositoryInterface */
38+
private $productRepository;
39+
40+
/** @var LayoutInterface */
41+
private $layout;
42+
43+
/** @var Configurable */
44+
private $block;
45+
46+
/** @var SerializerInterface */
47+
private $json;
48+
49+
/** @var ProductResource */
50+
private $productResource;
51+
52+
/**
53+
* @inheritdoc
54+
*/
55+
protected function setUp()
56+
{
57+
parent::setUp();
58+
59+
$this->objectManager = Bootstrap::getObjectManager();
60+
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
61+
$this->layout = $this->objectManager->get(LayoutInterface::class);
62+
$this->block = $this->layout->createBlock(Configurable::class);
63+
$this->json = $this->objectManager->get(SerializerInterface::class);
64+
$this->productResource = $this->objectManager->get(ProductResource::class);
65+
}
66+
67+
/**
68+
* @dataProvider oneChildNotVisibleDataProvider
69+
* @magentoDbIsolation disabled
70+
*
71+
* @param string $sku
72+
* @param array $data
73+
* @param array $expectedData
74+
* @return void
75+
*/
76+
public function testOneChildNotVisible(string $sku, array $data, array $expectedData): void
77+
{
78+
$configurableProduct = $this->prepareProductToTest($sku, $data);
79+
$result = $this->renderStockBlock($configurableProduct);
80+
$this->performAsserts($result, $expectedData);
81+
}
82+
83+
/**
84+
* @return array
85+
*/
86+
public function oneChildNotVisibleDataProvider(): array
87+
{
88+
return [
89+
'one_child_out_of_stock' => [
90+
'sku' => 'simple_10',
91+
'data' => [
92+
'stock_data' => [
93+
'use_config_manage_stock' => 1,
94+
'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK,
95+
],
96+
],
97+
'expected_data' => [
98+
'stock_status' => 'In stock',
99+
'options' => [
100+
[
101+
'label' => 'Option 2',
102+
'product' => 'simple_20',
103+
],
104+
],
105+
],
106+
],
107+
'one_child_disabled' => [
108+
'sku' => 'simple_10',
109+
'data' => [
110+
'status' => Status::STATUS_DISABLED,
111+
'stock_data' => [
112+
'use_config_manage_stock' => 1,
113+
'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK,
114+
],
115+
],
116+
'expected_data' => [
117+
'stock_status' => 'In stock',
118+
'options' => [
119+
[
120+
'label' => 'Option 2',
121+
'product' => 'simple_20',
122+
],
123+
],
124+
],
125+
],
126+
];
127+
}
128+
129+
/**
130+
* @magentoConfigFixture current_store cataloginventory/options/show_out_of_stock 1
131+
*
132+
* @dataProvider oneChildNotVisibleDataProviderWithEnabledConfig
133+
*
134+
* @param string $sku
135+
* @param array $data
136+
* @param array $expectedData
137+
* @return void
138+
*/
139+
public function testOneChildNotVisibleWithEnabledShowOutOfStockProducts(
140+
string $sku,
141+
array $data,
142+
array $expectedData
143+
): void {
144+
$configurableProduct = $this->prepareProductToTest($sku, $data);
145+
$result = $this->renderStockBlock($configurableProduct);
146+
$this->performAsserts($result, $expectedData);
147+
}
148+
149+
/**
150+
* @return array
151+
*/
152+
public function oneChildNotVisibleDataProviderWithEnabledConfig(): array
153+
{
154+
return [
155+
'one_child_out_of_stock' => [
156+
'sku' => 'simple_10',
157+
'data' => [
158+
'stock_data' => [
159+
'use_config_manage_stock' => 1,
160+
'is_in_stock' => StockStatusInterface::STATUS_OUT_OF_STOCK,
161+
],
162+
],
163+
'expected_data' => [
164+
'stock_status' => 'In stock',
165+
'options' => [
166+
[
167+
'label' => 'Option 2',
168+
'product' => 'simple_20'
169+
],
170+
[
171+
'label' => 'Option 1',
172+
'product' => 'simple_10',
173+
],
174+
],
175+
],
176+
],
177+
'one_child_disabled' => [
178+
'sku' => 'simple_10',
179+
'data' => [
180+
'status' => Status::STATUS_DISABLED,
181+
'stock_data' => [
182+
'use_config_manage_stock' => 1,
183+
'is_in_stock' => StockStatusInterface::STATUS_IN_STOCK,
184+
],
185+
],
186+
'expected_data' => [
187+
'stock_status' => 'In stock',
188+
'options' => [
189+
[
190+
'label' => 'Option 2',
191+
'product' => 'simple_20',
192+
],
193+
],
194+
],
195+
],
196+
];
197+
}
198+
199+
/**
200+
* Update product with data
201+
*
202+
* @param array $sku
203+
* @param array $data
204+
* @return void
205+
*/
206+
private function updateProduct(string $sku, array $data): void
207+
{
208+
$storeManager = $this->objectManager->get(StoreManagerInterface::class);
209+
$currentStore = $storeManager->getStore();
210+
try {
211+
$storeManager->setCurrentStore(Store::DEFAULT_STORE_ID);
212+
$product = $this->productRepository->get($sku);
213+
$product->addData($data);
214+
$this->productRepository->save($product);
215+
} finally {
216+
$storeManager->setCurrentStore($currentStore);
217+
}
218+
}
219+
220+
/**
221+
* Check attribute options
222+
*
223+
* @param array $actualAttributeDataData
224+
* @param array $actualOptionData
225+
* @param array $expectedData
226+
* @return void
227+
*/
228+
private function assertConfig(array $actualAttributeDataData, array $actualOptionData, array $expectedData): void
229+
{
230+
$this->assertCount(count($expectedData), $actualOptionData, 'Redundant options were loaded');
231+
foreach ($expectedData as $expectedOption) {
232+
$expectedId = $this->productResource->getIdBySku($expectedOption['product']);
233+
$itemToCheck = $actualOptionData[$expectedId] ?? null;
234+
$this->assertNotNull($itemToCheck);
235+
foreach ($actualAttributeDataData['options'] as $actualAttributeDataItem) {
236+
if ($actualAttributeDataItem['id'] === reset($itemToCheck)) {
237+
$this->assertEquals($expectedOption['label'], $actualAttributeDataItem['label']);
238+
}
239+
}
240+
}
241+
}
242+
243+
/**
244+
* Render stock block
245+
*
246+
* @param ProductInterface $configurableProduct
247+
* @return string
248+
*/
249+
private function renderStockBlock(ProductInterface $configurableProduct): string
250+
{
251+
$this->block->setProduct($configurableProduct);
252+
$this->block->setTemplate(self::STOCK_DISPLAY_TEMPLATE);
253+
254+
return $this->block->toHtml();
255+
}
256+
257+
/**
258+
* Perform test asserts
259+
*
260+
* @param string $result
261+
* @param array $expectedData
262+
* @return void
263+
*/
264+
private function performAsserts(string $result, array $expectedData): void
265+
{
266+
$this->assertEquals((string)__($expectedData['stock_status']), trim(strip_tags($result)));
267+
$config = $this->json->unserialize($this->block->getJsonConfig());
268+
$this->assertConfig(reset($config['attributes']), $config['index'], $expectedData['options']);
269+
}
270+
271+
/**
272+
* Prepare configurable product with children to test
273+
*
274+
* @param string $sku
275+
* @param array $data
276+
* @return ProductInterface
277+
*/
278+
private function prepareProductToTest(string $sku, array $data): ProductInterface
279+
{
280+
$this->updateProduct($sku, $data);
281+
$configurableProduct = $this->productRepository->get('configurable', false, null, true);
282+
283+
return $configurableProduct;
284+
}
285+
}

0 commit comments

Comments
 (0)