Skip to content

Commit 99924e5

Browse files
author
Hwashiang Yu
committed
MC-30175: UI component rendering config
- Added changes from MC-21979
1 parent 550ce14 commit 99924e5

File tree

9 files changed

+700
-18
lines changed

9 files changed

+700
-18
lines changed

app/code/Magento/Ui/Component/Form/Element/AbstractOptionsField.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use Magento\Framework\Data\OptionSourceInterface;
99
use Magento\Framework\View\Element\UiComponent\ContextInterface;
10+
use Magento\Framework\View\Element\UiComponent\DataProvider\Sanitizer;
1011

1112
/**
1213
* Base abstract form element.
@@ -23,21 +24,29 @@ abstract class AbstractOptionsField extends AbstractElement
2324
*/
2425
protected $options;
2526

27+
/**
28+
* @var Sanitizer
29+
*/
30+
private $sanitizer;
31+
2632
/**
2733
* Constructor
2834
*
2935
* @param ContextInterface $context
3036
* @param array|OptionSourceInterface|null $options
3137
* @param array $components
3238
* @param array $data
39+
* @param Sanitizer|null $sanitizer
3340
*/
3441
public function __construct(
3542
ContextInterface $context,
3643
$options = null,
3744
array $components = [],
38-
array $data = []
45+
array $data = [],
46+
?Sanitizer $sanitizer = null
3947
) {
4048
$this->options = $options;
49+
$this->sanitizer = $sanitizer ?? \Magento\Framework\App\ObjectManager::getInstance()->get(Sanitizer::class);
4150
parent::__construct($context, $components, $data);
4251
}
4352

@@ -62,13 +71,10 @@ public function prepare()
6271
if (empty($config['rawOptions'])) {
6372
$options = $this->convertOptionsValueToString($options);
6473
}
65-
66-
array_walk(
67-
$options,
68-
function (&$item) {
69-
$item['__disableTmpl'] = true;
70-
}
71-
);
74+
foreach ($options as &$option) {
75+
//Options contain static or dynamic entity data that is not supposed to contain templates.
76+
$option = $this->sanitizer->sanitize($option);
77+
}
7278

7379
$config['options'] = array_values(array_replace_recursive($config['options'], $options));
7480
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\View\Element\UiComponent;
10+
11+
use Magento\Framework\Api\Search\SearchCriteria;
12+
use Magento\Framework\App\RequestInterface;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use PHPUnit\Framework\TestCase;
16+
use Magento\Ui\Component\Form;
17+
use Magento\Ui\Component\FormFactory;
18+
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface;
19+
20+
/**
21+
* Test UI component context.
22+
*/
23+
class ContextTest extends TestCase
24+
{
25+
/**
26+
* @var RequestInterface
27+
*/
28+
private $request;
29+
30+
/**
31+
* @var ContextFactory
32+
*/
33+
private $contextFactory;
34+
35+
/**
36+
* @var FormFactory
37+
*/
38+
private $componentFactory;
39+
40+
/**
41+
* @inheritDoc
42+
*/
43+
protected function setUp()
44+
{
45+
$this->request = Bootstrap::getObjectManager()->get(RequestInterface::class);
46+
$this->contextFactory = Bootstrap::getObjectManager()->get(ContextFactory::class);
47+
$this->componentFactory = Bootstrap::getObjectManager()->get(FormFactory::class);
48+
}
49+
50+
/**
51+
* Generate provider for the test.
52+
*
53+
* @return DataProviderInterface
54+
*/
55+
private function generateMockProvider(): DataProviderInterface
56+
{
57+
/** @var DataProviderInterface|MockObject $mock */
58+
$mock = $this->getMockForAbstractClass(DataProviderInterface::class);
59+
$mock->method('getName')->willReturn('test');
60+
$mock->method('getPrimaryFieldName')->willReturn('id');
61+
$mock->method('getRequestFieldName')->willReturn('id');
62+
$mock->method('getData')->willReturn(['id' => ['some_field' => '${\'some_value\'}']]);
63+
$mock->method('getConfigData')->willReturn([]);
64+
$mock->method('getFieldMetaInfo')->willReturn([]);
65+
$mock->method('getFieldSetMetaInfo')->willReturn('id');
66+
$mock->method('getFieldsMetaInfo')->willReturn('id');
67+
$mock->method('getSearchCriteria')->willReturn(new SearchCriteria());
68+
$mock->method('getSearchResult')->willReturn([]);
69+
70+
return $mock;
71+
}
72+
73+
/**
74+
* Check processed provider data.
75+
*
76+
* @return void
77+
*/
78+
public function testGetDataSourceData(): void
79+
{
80+
$dataProvider = $this->generateMockProvider();
81+
$context = $this->contextFactory->create(['dataProvider' => $dataProvider]);
82+
/** @var Form $component */
83+
$component = $this->componentFactory->create(['context' => $context]);
84+
$this->request->setParams(['id' => 'id']);
85+
86+
$data = $context->getDataSourceData($component);
87+
$this->assertEquals(
88+
[
89+
'test' => [
90+
'type' => 'dataSource',
91+
'name' => 'test',
92+
'dataScope' => null,
93+
'config' => [
94+
'data' => ['some_field' => '${\'some_value\'}', '__disableTmpl' => ['some_field' => true]],
95+
'params' => ['namespace' => null]]
96+
]
97+
],
98+
$data
99+
);
100+
}
101+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\View\Element;
10+
11+
use Magento\Framework\Api\Search\SearchCriteria;
12+
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use PHPUnit\Framework\TestCase;
16+
use Magento\Framework\Config\DataInterface as ConfigData;
17+
use Magento\Framework\Config\DataInterfaceFactory as ConfigDataFactory;
18+
19+
/**
20+
* Test the component factory.
21+
*/
22+
class UiComponentFactoryTest extends TestCase
23+
{
24+
/**
25+
* @var UiComponentFactoryFactory
26+
*/
27+
private $factory;
28+
29+
/**
30+
* @inheritDoc
31+
*/
32+
protected function setUp()
33+
{
34+
$this->factory = Bootstrap::getObjectManager()->get(UiComponentFactoryFactory::class);
35+
}
36+
37+
/**
38+
* Create factory with mock config provided.
39+
*
40+
* @param array $mockConfig
41+
* @return UiComponentFactory
42+
*/
43+
private function createFactory(array $mockConfig): UiComponentFactory
44+
{
45+
$dataMock = $this->getMockForAbstractClass(ConfigData::class);
46+
$dataMock->method('get')->willReturnCallback(
47+
function (string $id) use ($mockConfig) : array {
48+
return $mockConfig[$id];
49+
}
50+
);
51+
$dataFactoryMock = $this->getMockBuilder(ConfigDataFactory::class)
52+
->disableOriginalConstructor()
53+
->getMock();
54+
$dataFactoryMock->method('create')->willReturn($dataMock);
55+
56+
return $this->factory->create(['configFactory' => $dataFactoryMock]);
57+
}
58+
59+
/**
60+
* Test creating a component.
61+
*
62+
* @return void
63+
* @magentoAppArea adminhtml
64+
*/
65+
public function testCreate(): void
66+
{
67+
//Mocking component config.
68+
$factory = $this->createFactory([
69+
'test' => [
70+
'arguments' => ['data' => ['config' => ['component' => 'uiComponent']]],
71+
'attributes' => [
72+
'name' => 'test',
73+
'sorting' => true,
74+
'class' => 'Magento\Ui\Component\Listing',
75+
'component' => 'uiComponent'
76+
],
77+
'children' => [
78+
'test_child' => [
79+
'arguments' => [
80+
'data' => ['config' => ['component' => 'uiComponent']],
81+
'dataProvider' => $this->generateMockProvider()
82+
],
83+
'attributes' => [
84+
'name' => 'test_child',
85+
'sorting' => true,
86+
'class' => 'Magento\Ui\Component\Listing',
87+
'component' => 'uiComponent'
88+
],
89+
'children' => []
90+
]
91+
]
92+
],
93+
'test_child_child' => [
94+
'arguments' => [
95+
'data' => [
96+
'config' => [
97+
'component' => 'uiComponent',
98+
'label' => '${\'Label\'}',
99+
'componentType' => 'component'
100+
]
101+
],
102+
],
103+
'attributes' => [
104+
'name' => 'test_child_child',
105+
'sorting' => true,
106+
'class' => 'Magento\Ui\Component\Listing',
107+
'component' => 'uiComponent'
108+
],
109+
'children' => []
110+
]
111+
]);
112+
$component = $factory->create('test', null, ['data' => ['label' => '${\'Label\'}']]);
113+
114+
$componentData = $component->getData();
115+
//Arguments passed must be sanitized
116+
$this->assertArrayHasKey('__disableTmpl', $componentData);
117+
$this->assertEquals(['label' => true], $componentData['__disableTmpl']);
118+
//Metadata provided by the dataProvider must be sanitized as well.
119+
$this->assertArrayHasKey('test_child_child', $childData = $component->getChildComponents());
120+
$childData = $component->getChildComponents()['test_child_child']->getData()['config'];
121+
$this->assertArrayHasKey('__disableTmpl', $childData);
122+
$this->assertEquals(['label' => true], $childData['__disableTmpl']);
123+
}
124+
125+
/**
126+
* Generate provider for the test.
127+
*
128+
* @return DataProviderInterface
129+
*/
130+
private function generateMockProvider(): DataProviderInterface
131+
{
132+
/** @var DataProviderInterface|MockObject $mock */
133+
$mock = $this->getMockForAbstractClass(DataProviderInterface::class);
134+
$mock->method('getName')->willReturn('test');
135+
$mock->method('getPrimaryFieldName')->willReturn('id');
136+
$mock->method('getRequestFieldName')->willReturn('id');
137+
$mock->method('getData')->willReturn([]);
138+
$mock->method('getConfigData')->willReturn([]);
139+
$mock->method('getFieldMetaInfo')->willReturn([]);
140+
$mock->method('getFieldSetMetaInfo')->willReturn('id');
141+
$mock->method('getFieldsMetaInfo')->willReturn('id');
142+
$mock->method('getSearchCriteria')->willReturn(new SearchCriteria());
143+
$mock->method('getSearchResult')->willReturn([]);
144+
$mock->method('getMeta')->willReturn(
145+
[
146+
'test_child_child' => [
147+
'arguments' => [
148+
'data' => [
149+
'config' => [
150+
'component' => 'uiComponent',
151+
'label' => '${\'Label\'}',
152+
'componentType' => 'component'
153+
]
154+
],
155+
],
156+
'attributes' => [
157+
'name' => 'test_child_child',
158+
'sorting' => true,
159+
'class' => 'Magento\Ui\Component\Listing',
160+
'component' => 'uiComponent'
161+
]
162+
]
163+
]
164+
);
165+
166+
return $mock;
167+
}
168+
}

dev/tests/integration/testsuite/Magento/Setup/Console/Command/_files/root/lib/internal/Magento/Framework/Test/Unit/View/Element/UiComponentFactoryTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\Framework\Test\Unit\View\Element;
77

88
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
9+
use Magento\Framework\View\Element\UiComponent\DataProvider\Sanitizer;
910
use PHPUnit\Framework\MockObject\MockObject;
1011

1112
class UiComponentFactoryTest extends \PHPUnit\Framework\TestCase
@@ -59,6 +60,9 @@ protected function setUp()
5960
->disableOriginalConstructor()
6061
->getMock();
6162
$this->dataMock = $this->createMock(\Magento\Framework\Config\DataInterface::class);
63+
$sanitizerMock = $this->createMock(Sanitizer::class);
64+
$sanitizerMock->method('sanitize')->willReturnArgument(0);
65+
$sanitizerMock->method('sanitizeComponentMetadata')->willReturnArgument(0);
6266
$this->objectManagerHelper = new ObjectManagerHelper($this);
6367
$this->model = $this->objectManagerHelper->getObject(
6468
\Magento\Framework\View\Element\UiComponentFactory::class,
@@ -69,7 +73,8 @@ protected function setUp()
6973
'configFactory' => $this->dataInterfaceFactoryMock,
7074
'data' => [],
7175
'componentChildFactories' => [],
72-
'definitionData' => $this->dataMock
76+
'definitionData' => $this->dataMock,
77+
'sanitizer' => $sanitizerMock
7378
]
7479
);
7580
}

0 commit comments

Comments
 (0)