Skip to content

Commit b40c1ba

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-86295' into 2.2-develop-pr33
2 parents 5dfe914 + a9dc43c commit b40c1ba

File tree

8 files changed

+412
-2
lines changed

8 files changed

+412
-2
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
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\Test\Unit\Ui\Component\Product;
9+
10+
use Magento\Catalog\Ui\Component\Product\MassAction;
11+
use Magento\Framework\AuthorizationInterface;
12+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
13+
use Magento\Framework\View\Element\UiComponent\ContextInterface;
14+
15+
/**
16+
* Test for Magento\Catalog\Ui\Component\Product\MassAction class.
17+
*/
18+
class MassActionTest extends \PHPUnit\Framework\TestCase
19+
{
20+
/**
21+
* @var ContextInterface|\PHPUnit_Framework_MockObject_MockObject
22+
*/
23+
private $contextMock;
24+
25+
/**
26+
* @var ObjectManager
27+
*/
28+
private $objectManager;
29+
30+
/**
31+
* @var AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject
32+
*/
33+
private $authorizationMock;
34+
35+
/**
36+
* @var MassAction
37+
*/
38+
private $massAction;
39+
40+
/**
41+
* @inheritdoc
42+
*/
43+
protected function setUp()
44+
{
45+
$this->objectManager = new ObjectManager($this);
46+
47+
$this->contextMock = $this->getMockBuilder(ContextInterface::class)
48+
->getMockForAbstractClass();
49+
$this->authorizationMock = $this->getMockBuilder(AuthorizationInterface::class)
50+
->getMockForAbstractClass();
51+
52+
$this->massAction = $this->objectManager->getObject(
53+
MassAction::class,
54+
[
55+
'authorization' => $this->authorizationMock,
56+
'context' => $this->contextMock,
57+
'data' => [],
58+
]
59+
);
60+
}
61+
62+
/**
63+
* @return void
64+
*/
65+
public function testGetComponentName()
66+
{
67+
$this->assertTrue($this->massAction->getComponentName() === MassAction::NAME);
68+
}
69+
70+
/**
71+
* @param string $componentName
72+
* @param array $componentData
73+
* @param bool $isAllowed
74+
* @param bool $expectActionConfig
75+
* @return void
76+
* @dataProvider getPrepareDataProvider
77+
*/
78+
public function testPrepare(
79+
string $componentName,
80+
array $componentData,
81+
bool $isAllowed = true,
82+
bool $expectActionConfig = true
83+
) {
84+
$processor = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponent\Processor::class)
85+
->disableOriginalConstructor()
86+
->getMock();
87+
$this->contextMock->expects($this->atLeastOnce())->method('getProcessor')->willReturn($processor);
88+
/** @var \Magento\Ui\Component\MassAction $action */
89+
$action = $this->objectManager->getObject(
90+
\Magento\Ui\Component\MassAction::class,
91+
[
92+
'context' => $this->contextMock,
93+
'data' => [
94+
'name' => $componentName,
95+
'config' => $componentData,
96+
]
97+
]
98+
);
99+
$this->authorizationMock->method('isAllowed')
100+
->willReturn($isAllowed);
101+
$this->massAction->addComponent('action', $action);
102+
$this->massAction->prepare();
103+
$expected = $expectActionConfig ? ['actions' => [$action->getConfiguration()]] : [];
104+
$this->assertEquals($expected, $this->massAction->getConfiguration());
105+
}
106+
107+
/**
108+
* @return array
109+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
110+
*/
111+
public function getPrepareDataProvider() : array
112+
{
113+
return [
114+
[
115+
'test_component1',
116+
[
117+
'type' => 'first_action',
118+
'label' => 'First Action',
119+
'url' => '/module/controller/firstAction',
120+
],
121+
],
122+
[
123+
'test_component2',
124+
[
125+
'type' => 'second_action',
126+
'label' => 'Second Action',
127+
'actions' => [
128+
[
129+
'type' => 'second_sub_action1',
130+
'label' => 'Second Sub Action 1',
131+
'url' => '/module/controller/secondSubAction1',
132+
],
133+
[
134+
'type' => 'second_sub_action2',
135+
'label' => 'Second Sub Action 2',
136+
'url' => '/module/controller/secondSubAction2',
137+
],
138+
],
139+
],
140+
],
141+
[
142+
'status_component',
143+
[
144+
'type' => 'status',
145+
'label' => 'Status',
146+
'actions' => [
147+
[
148+
'type' => 'enable',
149+
'label' => 'Second Sub Action 1',
150+
'url' => '/module/controller/enable',
151+
],
152+
[
153+
'type' => 'disable',
154+
'label' => 'Second Sub Action 2',
155+
'url' => '/module/controller/disable',
156+
],
157+
],
158+
],
159+
],
160+
[
161+
'status_component_not_allowed',
162+
[
163+
'type' => 'status',
164+
'label' => 'Status',
165+
'actions' => [
166+
[
167+
'type' => 'enable',
168+
'label' => 'Second Sub Action 1',
169+
'url' => '/module/controller/enable',
170+
],
171+
[
172+
'type' => 'disable',
173+
'label' => 'Second Sub Action 2',
174+
'url' => '/module/controller/disable',
175+
],
176+
],
177+
],
178+
false,
179+
false,
180+
],
181+
[
182+
'delete_component',
183+
[
184+
'type' => 'delete',
185+
'label' => 'First Action',
186+
'url' => '/module/controller/delete',
187+
],
188+
],
189+
[
190+
'delete_component_not_allowed',
191+
[
192+
'type' => 'delete',
193+
'label' => 'First Action',
194+
'url' => '/module/controller/delete',
195+
],
196+
false,
197+
false,
198+
],
199+
[
200+
'attributes_component',
201+
[
202+
'type' => 'delete',
203+
'label' => 'First Action',
204+
'url' => '/module/controller/attributes',
205+
],
206+
],
207+
[
208+
'attributes_component_not_allowed',
209+
[
210+
'type' => 'delete',
211+
'label' => 'First Action',
212+
'url' => '/module/controller/attributes',
213+
],
214+
false,
215+
false,
216+
],
217+
];
218+
}
219+
220+
/**
221+
* @param bool $expected
222+
* @param string $actionType
223+
* @param int $callNum
224+
* @param string $resource
225+
* @param bool $isAllowed
226+
* @return void
227+
* @dataProvider isActionAllowedDataProvider
228+
*/
229+
public function testIsActionAllowed(
230+
bool $expected,
231+
string $actionType,
232+
int $callNum,
233+
string $resource = '',
234+
bool $isAllowed = true
235+
) {
236+
$this->authorizationMock->expects($this->exactly($callNum))
237+
->method('isAllowed')
238+
->with($resource)
239+
->willReturn($isAllowed);
240+
241+
$this->assertEquals($expected, $this->massAction->isActionAllowed($actionType));
242+
}
243+
244+
/**
245+
* @return array
246+
*/
247+
public function isActionAllowedDataProvider(): array
248+
{
249+
return [
250+
'other' => [true, 'other', 0,],
251+
'delete-allowed' => [true, 'delete', 1, 'Magento_Catalog::products'],
252+
'delete-not-allowed' => [false, 'delete', 1, 'Magento_Catalog::products', false],
253+
'status-allowed' => [true, 'status', 1, 'Magento_Catalog::products'],
254+
'status-not-allowed' => [false, 'status', 1, 'Magento_Catalog::products', false],
255+
'attributes-allowed' => [true, 'attributes', 1, 'Magento_Catalog::update_attributes'],
256+
'attributes-not-allowed' => [false, 'attributes', 1, 'Magento_Catalog::update_attributes', false],
257+
];
258+
}
259+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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\Ui\Component\Product;
9+
10+
use Magento\Framework\AuthorizationInterface;
11+
use Magento\Framework\View\Element\UiComponentInterface;
12+
use Magento\Framework\View\Element\UiComponent\ContextInterface;
13+
use Magento\Ui\Component\AbstractComponent;
14+
15+
/**
16+
* Provide validation of allowed massaction for user.
17+
*/
18+
class MassAction extends AbstractComponent
19+
{
20+
const NAME = 'massaction';
21+
22+
/**
23+
* @var AuthorizationInterface
24+
*/
25+
private $authorization;
26+
27+
/**
28+
* @param AuthorizationInterface $authorization
29+
* @param ContextInterface $context
30+
* @param UiComponentInterface[] $components
31+
* @param array $data
32+
*/
33+
public function __construct(
34+
AuthorizationInterface $authorization,
35+
ContextInterface $context,
36+
array $components = [],
37+
array $data = []
38+
) {
39+
$this->authorization = $authorization;
40+
parent::__construct($context, $components, $data);
41+
}
42+
43+
/**
44+
* @inheritdoc
45+
*/
46+
public function prepare()
47+
{
48+
$config = $this->getConfiguration();
49+
50+
foreach ($this->getChildComponents() as $actionComponent) {
51+
$actionType = $actionComponent->getConfiguration()['type'];
52+
if ($this->isActionAllowed($actionType)) {
53+
$config['actions'][] = $actionComponent->getConfiguration();
54+
}
55+
}
56+
$origConfig = $this->getConfiguration();
57+
if ($origConfig !== $config) {
58+
$config = array_replace_recursive($config, $origConfig);
59+
}
60+
61+
$this->setData('config', $config);
62+
$this->components = [];
63+
64+
parent::prepare();
65+
}
66+
67+
/**
68+
* @inheritdoc
69+
*/
70+
public function getComponentName(): string
71+
{
72+
return static::NAME;
73+
}
74+
75+
/**
76+
* Check if the given type of action is allowed.
77+
*
78+
* @param string $actionType
79+
* @return bool
80+
*/
81+
public function isActionAllowed(string $actionType): bool
82+
{
83+
$isAllowed = true;
84+
switch ($actionType) {
85+
case 'delete':
86+
case 'status':
87+
$isAllowed = $this->authorization->isAllowed('Magento_Catalog::products');
88+
break;
89+
case 'attributes':
90+
$isAllowed = $this->authorization->isAllowed('Magento_Catalog::update_attributes');
91+
break;
92+
default:
93+
break;
94+
}
95+
96+
return $isAllowed;
97+
}
98+
}

app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
</settings>
4949
</filterSelect>
5050
</filters>
51-
<massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions">
51+
<massaction name="listing_massaction"
52+
component="Magento_Ui/js/grid/tree-massactions"
53+
class="\Magento\Catalog\Ui\Component\Product\MassAction">
5254
<action name="delete">
5355
<settings>
5456
<confirm>

app/code/Magento/Ui/DataProvider/AbstractDataProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,6 @@ public function setConfigData($config)
292292
*/
293293
public function getAllIds()
294294
{
295-
return $this->collection->getAllIds();
295+
return $this->getCollection()->getAllIds();
296296
}
297297
}

dev/tests/acceptance/tests/functional/Magento/FunctionalTest/Catalog/Section/AdminProductGridSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@
2222
<element name="productGridXRowYColumnButton" type="input" selector="table.data-grid tr.data-row:nth-child({{row}}) td:nth-child({{column}})" parameterized="true" timeout="30"/>
2323
<element name="productNameInNameColumn" type="input" selector="//td[4]/div[@class='data-grid-cell-content']"/>
2424
<element name="checkbox" type="checkbox" selector="//div[contains(text(),'{{product}}')]/ancestor::tr[@class='data-row']//input[@class='admin__control-checkbox']" parameterized="true" />
25+
<element name="bulkActionOption" type="button" selector="//div[contains(@class,'admin__data-grid-header-row') and contains(@class, 'row')]//div[contains(@class, 'action-select-wrap')]//ul/li/span[text() = '{{label}}']" parameterized="true"/>
2526
</section>
2627
</sections>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Page/etc/PageObject.xsd">
9+
<page name="AdminRolesPage" url="admin/user_role/" module="Magento_User" area="admin">
10+
<section name="AdminRoleGridSection"/>
11+
</page>
12+
</pages>

0 commit comments

Comments
 (0)