Skip to content

Commit d6ffc1e

Browse files
committed
MC-25243: Storefront: Simple product with custom attribute per multiple websites/storeviews, countries/states
1 parent c644992 commit d6ffc1e

17 files changed

+1346
-1
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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\Catalog\Block\Product\View\Attribute;
9+
10+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
13+
use Magento\Catalog\Api\ProductRepositoryInterface;
14+
use Magento\Catalog\Block\Product\View\Attributes;
15+
use Magento\Catalog\Helper\Output;
16+
use Magento\Framework\ObjectManagerInterface;
17+
use Magento\Framework\Registry;
18+
use Magento\Framework\View\LayoutInterface;
19+
use Magento\TestFramework\Helper\Bootstrap;
20+
use PHPUnit\Framework\TestCase;
21+
22+
/**
23+
* Class consist of base logic for custom attributes view tests
24+
*/
25+
abstract class AbstractAttributeTest extends TestCase
26+
{
27+
/** @var ObjectManagerInterface */
28+
protected $objectManager;
29+
30+
/** @var LayoutInterface */
31+
private $layout;
32+
33+
/** @var ProductRepositoryInterface */
34+
private $productRepository;
35+
36+
/** @var ProductAttributeRepositoryInterface */
37+
private $attributeRepository;
38+
39+
/** @var Registry */
40+
private $registry;
41+
42+
/** @var Attributes */
43+
private $block;
44+
45+
/** @var ProductAttributeInterface */
46+
private $attribute;
47+
48+
/** @var Output */
49+
private $outputHelper;
50+
51+
/**
52+
* @inheritdoc
53+
*/
54+
protected function setUp()
55+
{
56+
parent::setUp();
57+
58+
$this->objectManager = Bootstrap::getObjectManager();
59+
$this->layout = $this->objectManager->get(LayoutInterface::class);
60+
$this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class);
61+
$this->attributeRepository = $this->objectManager->create(ProductAttributeRepositoryInterface::class);
62+
$this->registry = $this->objectManager->get(Registry::class);
63+
$this->block = $this->layout->createBlock(Attributes::class);
64+
$this->outputHelper = $this->objectManager->create(Output::class);
65+
}
66+
67+
/**
68+
* Process custom attribute view
69+
*
70+
* @param string $sku
71+
* @param string $attributeValue
72+
* @param string $expectedAttributeValue
73+
* @return void
74+
*/
75+
protected function processAttributeView(
76+
string $sku,
77+
string $attributeValue,
78+
string $expectedAttributeValue
79+
): void {
80+
$this->updateAttribute(['is_visible_on_front' => true]);
81+
$product = $this->updateProduct($sku, $attributeValue);
82+
$this->registerProduct($product);
83+
$data = $this->block->getAdditionalData();
84+
$this->assertEquals($this->prepareExpectedData($expectedAttributeValue), $data);
85+
}
86+
87+
/**
88+
* Process custom attribute with default value view when new value set
89+
*
90+
* @param string $sku
91+
* @param string $attributeValue
92+
* @param string $expectedAttributeValue
93+
* @return void
94+
*/
95+
protected function processNonDefaultAttributeValueView(
96+
string $sku,
97+
string $attributeValue,
98+
string $expectedAttributeValue
99+
): void {
100+
$this->updateAttribute(['is_visible_on_front' => true, 'default_value' => $this->getDefaultAttributeValue()]);
101+
$product = $this->updateProduct($sku, $attributeValue);
102+
$this->registerProduct($product);
103+
$data = $this->block->getAdditionalData();
104+
$this->assertEquals($this->prepareExpectedData($expectedAttributeValue), $data);
105+
}
106+
107+
/**
108+
* Procces custom attribute view with default value
109+
*
110+
* @param string $sku
111+
* @param string $expectedAttributeValue
112+
* @return void
113+
*/
114+
protected function processDefaultValueAttributeView(string $sku, string $expectedAttributeValue): void
115+
{
116+
$this->updateAttribute(['is_visible_on_front' => true, 'default_value' => $this->getDefaultAttributeValue()]);
117+
$product = $this->productRepository->save($this->productRepository->get($sku));
118+
$this->registerProduct($product);
119+
$data = $this->block->getAdditionalData();
120+
$this->assertEquals($this->prepareExpectedData($expectedAttributeValue), $data);
121+
}
122+
123+
/**
124+
* Procces attribute value view with html tags
125+
*
126+
* @param string $sku
127+
* @param bool $allowHtmlTags
128+
* @param string $attributeValue
129+
* @param string $expectedAttributeValue
130+
* @return void
131+
*/
132+
protected function processAttributeHtmlOutput(
133+
string $sku,
134+
bool $allowHtmlTags,
135+
string $attributeValue,
136+
string $expectedAttributeValue
137+
): void {
138+
$this->updateAttribute(['is_visible_on_front' => true, 'is_html_allowed_on_front' => $allowHtmlTags]);
139+
$product = $this->updateProduct($sku, $attributeValue);
140+
$this->registerProduct($product);
141+
$data = $this->block->getAdditionalData();
142+
$dataItem = $data[$this->getAttributeCode()] ?? null;
143+
$this->assertNotNull($dataItem);
144+
$output = $this->outputHelper->productAttribute($product, $dataItem['value'], $dataItem['code']);
145+
$this->assertEquals($expectedAttributeValue, $output);
146+
}
147+
148+
/**
149+
* Get attribute
150+
*
151+
* @return ProductAttributeInterface
152+
*/
153+
protected function getAttribute(): ProductAttributeInterface
154+
{
155+
if ($this->attribute === null) {
156+
$this->attribute = $this->attributeRepository->get($this->getAttributeCode());
157+
}
158+
159+
return $this->attribute;
160+
}
161+
162+
/**
163+
* Prepare expected data
164+
*
165+
* @param string $expectedValue
166+
* @return array
167+
*/
168+
private function prepareExpectedData(string $expectedValue): array
169+
{
170+
return [
171+
$this->getAttributeCode() => [
172+
'label' => $this->getAttribute()->getStoreLabel(),
173+
'value' => $expectedValue,
174+
'code' => $this->getAttributeCode(),
175+
],
176+
];
177+
}
178+
179+
/**
180+
* Update product
181+
*
182+
* @param string $productSku
183+
* @param string $attributeValue
184+
* @return ProductInterface
185+
*/
186+
private function updateProduct(string $productSku, string $attributeValue): ProductInterface
187+
{
188+
$product = $this->productRepository->get($productSku);
189+
$product->addData([$this->getAttributeCode() => $attributeValue]);
190+
191+
return $this->productRepository->save($product);
192+
}
193+
194+
/**
195+
* Register product
196+
*
197+
* @param ProductInterface $product
198+
* @return void
199+
*/
200+
private function registerProduct(ProductInterface $product): void
201+
{
202+
$this->registry->unregister('product');
203+
$this->registry->register('product', $product);
204+
}
205+
206+
/**
207+
* Update attribute
208+
*
209+
* @param array $data
210+
* @return void
211+
*/
212+
private function updateAttribute(array $data): void
213+
{
214+
$attribute = $this->getAttribute();
215+
$attribute->addData($data);
216+
217+
$this->attributeRepository->save($attribute);
218+
}
219+
220+
/**
221+
* Get attribute code for current test
222+
*
223+
* @return string
224+
*/
225+
abstract protected function getAttributeCode(): string;
226+
227+
/**
228+
* Get default value for current attribute
229+
*
230+
* @return string
231+
*/
232+
abstract protected function getDefaultAttributeValue(): string;
233+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Catalog\Block\Product\View\Attribute;
9+
10+
/**
11+
* Class checks date attribute displaying on frontend
12+
*
13+
* @magentoDbIsolation disabled
14+
* @magentoDataFixture Magento/Catalog/_files/product_date_attribute.php
15+
* @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
16+
*/
17+
class DateAttributeTest extends AbstractAttributeTest
18+
{
19+
/**
20+
* @magentoConfigFixture default_store general/locale/timezone UTC
21+
* @return void
22+
*/
23+
public function testAttributeView(): void
24+
{
25+
$attributeValue = $this->getAttribute()->getBackend()->formatDate('12/20/19');
26+
$this->processAttributeView('simple2', $attributeValue, 'Dec 20, 2019');
27+
}
28+
29+
/**
30+
* @magentoConfigFixture default_store general/locale/timezone UTC
31+
* @return void
32+
*/
33+
public function testAttributeWithNonDefaultValueView(): void
34+
{
35+
$attributeValue = $this->getAttribute()->getBackend()->formatDate('12/20/19');
36+
$this->processNonDefaultAttributeValueView('simple2', $attributeValue, 'Dec 20, 2019');
37+
}
38+
39+
/**
40+
* @magentoConfigFixture default_store general/locale/timezone UTC
41+
* @return void
42+
*/
43+
public function testAttributeWithDefaultValueView(): void
44+
{
45+
$this->markTestSkipped('Test is blocked by issue MC-28950');
46+
$this->processDefaultValueAttributeView('simple2', $this->getDefaultAttributeValue());
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
protected function getAttributeCode(): string
53+
{
54+
return 'date_attribute';
55+
}
56+
57+
/**
58+
* @inheritdoc
59+
*/
60+
protected function getDefaultAttributeValue(): string
61+
{
62+
return $this->getAttribute()->getBackend()->formatDate('11/20/19');
63+
}
64+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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\Catalog\Block\Product\View\Attribute;
9+
10+
/**
11+
* Class checks dropdown attribute displaying on frontend
12+
*
13+
* @magentoDbIsolation enabled
14+
* @magentoAppIsolation enabled
15+
* @magentoDataFixture Magento/Catalog/_files/dropdown_attribute.php
16+
* @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
17+
*/
18+
class DropdownAttributeTest extends AbstractAttributeTest
19+
{
20+
/** @var string */
21+
private $attributeCode;
22+
23+
/**
24+
* @return void
25+
*/
26+
public function testAttributeView(): void
27+
{
28+
$attributeValue = $this->getAttribute()->getSource()->getOptionId('Option 2');
29+
$this->processAttributeView('simple2', $attributeValue, 'Option 2');
30+
}
31+
32+
/**
33+
* @return void
34+
*/
35+
public function testAttributeWithNonDefaultValueView(): void
36+
{
37+
$attributeValue = $this->getAttribute()->getSource()->getOptionId('Option 2');
38+
$this->processNonDefaultAttributeValueView('simple2', $attributeValue, 'Option 2');
39+
}
40+
41+
/**
42+
* @dataProvider attributeWithTagsProvider
43+
* @magentoAppArea frontend
44+
* @magentoDataFixture Magento/Catalog/_files/dropdown_attribute_with_html.php
45+
* @magentoDataFixture Magento/Catalog/_files/second_product_simple.php
46+
* @param bool $allowHtmlTags
47+
* @param string $attributeValue
48+
* @param string $expectedAttributeValue
49+
* @return void
50+
*/
51+
public function testAttributeWithHtmlTags(
52+
bool $allowHtmlTags,
53+
string $attributeValue,
54+
string $expectedAttributeValue
55+
): void {
56+
$this->attributeCode = 'dropdown_attribute_with_html';
57+
$attributeValue = $this->getAttribute()->getSource()->getOptionId($attributeValue);
58+
$this->processAttributeHtmlOutput('simple2', $allowHtmlTags, $attributeValue, $expectedAttributeValue);
59+
}
60+
61+
/**
62+
* @return array
63+
*/
64+
public function attributeWithTagsProvider(): array
65+
{
66+
return [
67+
'allow_html_tags' => [
68+
'allow_html_tags' => true,
69+
'attribute_value' => '<h2>Option 2</h2>',
70+
'expected_attribute_value' => '<h2>Option 2</h2>',
71+
],
72+
'disallow_html_tags' => [
73+
'allow_html_tags' => false,
74+
'attribute_value' => '<h2>Option 2</h2>',
75+
'expected_attribute_value' => '&lt;h2&gt;Option 2&lt;/h2&gt;',
76+
],
77+
];
78+
}
79+
80+
/**
81+
* @inheritdoc
82+
*/
83+
protected function getAttributeCode(): string
84+
{
85+
return $this->attributeCode ?? 'dropdown_attribute';
86+
}
87+
88+
/**
89+
* @inheritdoc
90+
*/
91+
protected function getDefaultAttributeValue(): string
92+
{
93+
return $this->getAttribute()->getSource()->getOptionId('Option 1');
94+
}
95+
}

0 commit comments

Comments
 (0)