Completed
Push — master ( be024c...a4094f )
by Vincent
16s queued 11s
created

ListMapperTest::testAutoSortOnAssociatedProperty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 40
rs 9.28
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Tests\Datagrid;
15
16
use PHPUnit\Framework\TestCase;
17
use Sonata\AdminBundle\Admin\AbstractAdmin;
18
use Sonata\AdminBundle\Admin\AdminInterface;
19
use Sonata\AdminBundle\Admin\BaseFieldDescription;
20
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
21
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
22
use Sonata\AdminBundle\Builder\ListBuilderInterface;
23
use Sonata\AdminBundle\Datagrid\ListMapper;
24
use Sonata\AdminBundle\Model\ModelManagerInterface;
25
use Sonata\AdminBundle\Translator\NoopLabelTranslatorStrategy;
26
27
/**
28
 * @author Andrej Hudec <[email protected]>
29
 */
30
class ListMapperTest extends TestCase
31
{
32
    private const DEFAULT_GRANTED_ROLE = 'ROLE_ADMIN_BAZ';
33
34
    /**
35
     * @var ListMapper
36
     */
37
    private $listMapper;
38
39
    /**
40
     * @var FieldDescriptionCollection
41
     */
42
    private $fieldDescriptionCollection;
43
44
    /**
45
     * @var AdminInterface
46
     */
47
    private $admin;
48
49
    protected function setUp(): void
50
    {
51
        $listBuilder = $this->createMock(ListBuilderInterface::class);
52
        $this->fieldDescriptionCollection = new FieldDescriptionCollection();
53
        $this->admin = $this->createMock(AbstractAdmin::class);
54
55
        $listBuilder
56
            ->method('addField')
57
            ->willReturnCallback(static function (
58
                FieldDescriptionCollection $list,
59
                ?string $type,
60
                BaseFieldDescription $fieldDescription,
61
                AbstractAdmin $admin
0 ignored issues
show
Unused Code introduced by
The parameter $admin is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
62
            ): void {
63
                $list->add($fieldDescription);
64
            });
65
66
        $modelManager = $this->createMock(ModelManagerInterface::class);
67
68
        $modelManager
69
            ->method('getNewFieldDescriptionInstance')
70
            ->willReturnCallback(function (?string $class, string $name, array $options = []): BaseFieldDescription {
71
                $fieldDescription = $this->getFieldDescriptionMock();
72
                $fieldDescription->setName($name);
73
                $fieldDescription->setOptions($options);
74
75
                return $fieldDescription;
76
            });
77
78
        $this->admin
79
            ->method('getModelManager')
80
            ->willReturn($modelManager);
81
82
        $labelTranslatorStrategy = new NoopLabelTranslatorStrategy();
83
84
        $this->admin
85
            ->method('getLabelTranslatorStrategy')
86
            ->willReturn($labelTranslatorStrategy);
87
88
        $this->admin
89
            ->method('isGranted')
90
            ->willReturnCallback(static function (string $name, object $object = null): bool {
0 ignored issues
show
Unused Code introduced by
The parameter $object is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
91
                return self::DEFAULT_GRANTED_ROLE === $name;
92
            });
93
94
        $this->listMapper = new ListMapper($listBuilder, $this->fieldDescriptionCollection, $this->admin);
95
    }
96
97
    public function testFluidInterface(): void
98
    {
99
        $fieldDescription = $this->getFieldDescriptionMock('fooName', 'fooLabel');
100
101
        $this->assertSame($this->listMapper, $this->listMapper->add($fieldDescription));
102
        $this->assertSame($this->listMapper, $this->listMapper->remove('fooName'));
103
        $this->assertSame($this->listMapper, $this->listMapper->reorder([]));
104
    }
105
106
    public function testGet(): void
107
    {
108
        $this->assertFalse($this->listMapper->has('fooName'));
109
110
        $fieldDescription = $this->getFieldDescriptionMock('fooName', 'fooLabel');
111
112
        $this->listMapper->add($fieldDescription);
113
        $this->assertSame($fieldDescription, $this->listMapper->get('fooName'));
114
    }
115
116
    public function testAddIdentifier(): void
117
    {
118
        $this->assertFalse($this->listMapper->has('fooName'));
119
120
        $fieldDescription = $this->getFieldDescriptionMock('fooName', 'fooLabel');
121
122
        $this->listMapper->addIdentifier($fieldDescription);
0 ignored issues
show
Documentation introduced by
$fieldDescription is of type object<Sonata\AdminBundl...n\BaseFieldDescription>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
123
        $this->assertTrue($this->listMapper->has('fooName'));
124
125
        $fieldDescription = $this->listMapper->get('fooName');
126
        $this->assertTrue($fieldDescription->getOption('identifier'));
127
    }
128
129
    public function testAddOptionIdentifier(): void
130
    {
131
        $this->assertFalse($this->listMapper->has('fooName'));
132
        $this->assertFalse($this->listMapper->has('barName'));
133
        $this->assertFalse($this->listMapper->has('bazName'));
134
135
        $this->listMapper->add('barName');
136
        $this->assertNull($this->listMapper->get('barName')->getOption('identifier'));
137
        $this->listMapper->add('fooName', null, ['identifier' => true]);
138
        $this->assertTrue($this->listMapper->has('fooName'));
139
        $this->assertTrue($this->listMapper->get('fooName')->getOption('identifier'));
140
        $this->listMapper->add('bazName', null, ['identifier' => false]);
141
        $this->assertTrue($this->listMapper->has('bazName'));
142
        $this->assertFalse($this->listMapper->get('bazName')->getOption('identifier'));
143
    }
144
145
    /**
146
     * @dataProvider getWrongIdentifierOptions
147
     */
148
    public function testAddOptionIdentifierWithWrongValue(bool $expected, $value): void
149
    {
150
        $this->assertFalse($this->listMapper->has('fooName'));
151
152
        $this->expectException(\InvalidArgumentException::class);
153
        $this->expectExceptionMessageMatches('/^Value for "identifier" option must be boolean, .+ given.$/');
154
155
        $this->listMapper->add('fooName', null, ['identifier' => $value]);
156
    }
157
158
    public function getWrongIdentifierOptions(): iterable
159
    {
160
        return [
161
            [true, 1],
162
            [true, 'string'],
163
            [true, new \stdClass()],
164
            [true, [null]],
165
            [false, 0],
166
            [false, null],
167
            [false, ''],
168
            [false, '0'],
169
            [false, []],
170
        ];
171
    }
172
173
    public function testAdd(): void
174
    {
175
        $this->listMapper->add('fooName');
176
        $this->listMapper->add('fooNameLabelBar', null, ['label' => 'Foo Bar']);
177
        $this->listMapper->add('fooNameLabelFalse', null, ['label' => false]);
178
179
        $this->assertTrue($this->listMapper->has('fooName'));
180
181
        $fieldDescription = $this->listMapper->get('fooName');
182
        $fieldLabelBar = $this->listMapper->get('fooNameLabelBar');
183
        $fieldLabelFalse = $this->listMapper->get('fooNameLabelFalse');
184
185
        $this->assertInstanceOf(FieldDescriptionInterface::class, $fieldDescription);
186
        $this->assertSame('fooName', $fieldDescription->getName());
187
        $this->assertSame('fooName', $fieldDescription->getOption('label'));
188
        $this->assertSame('Foo Bar', $fieldLabelBar->getOption('label'));
189
        $this->assertFalse($fieldLabelFalse->getOption('label'));
190
    }
191
192
    public function testAddViewInlineAction(): void
193
    {
194
        $this->assertFalse($this->listMapper->has('_action'));
195
        $this->listMapper->add('_action', 'actions', ['actions' => ['show' => []]]);
196
197
        $this->assertTrue($this->listMapper->has('_action'));
198
199
        $fieldDescription = $this->listMapper->get('_action');
200
201
        $this->assertInstanceOf(FieldDescriptionInterface::class, $fieldDescription);
202
        $this->assertSame('_action', $fieldDescription->getName());
203
        $this->assertCount(1, $fieldDescription->getOption('actions'));
204
        $this->assertSame(['show' => []], $fieldDescription->getOption('actions'));
205
    }
206
207
    public function testAddRemove(): void
208
    {
209
        $this->assertFalse($this->listMapper->has('fooName'));
210
211
        $fieldDescription = $this->getFieldDescriptionMock('fooName', 'fooLabel');
212
213
        $this->listMapper->add($fieldDescription);
214
        $this->assertTrue($this->listMapper->has('fooName'));
215
216
        $this->listMapper->remove('fooName');
217
        $this->assertFalse($this->listMapper->has('fooName'));
218
    }
219
220
    public function testAddDuplicateNameException(): void
221
    {
222
        $tmpNames = [];
223
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
224
            ->method('hasListFieldDescription')
225
            ->willReturnCallback(static function (string $name) use (&$tmpNames): bool {
226
                if (isset($tmpNames[$name])) {
227
                    return true;
228
                }
229
                $tmpNames[$name] = $name;
230
231
                return false;
232
            });
233
234
        $this->expectException(\LogicException::class);
235
        $this->expectExceptionMessage('Duplicate field name "fooName" in list mapper. Names should be unique.');
236
237
        $this->listMapper->add('fooName');
238
        $this->listMapper->add('fooName');
239
    }
240
241
    public function testAddWrongTypeException(): void
242
    {
243
        $this->expectException(\TypeError::class);
244
        $this->expectExceptionMessage(
245
            'Unknown field name in list mapper. Field name should be either of FieldDescriptionInterface interface or string.'
246
        );
247
248
        $this->listMapper->add(12345);
249
    }
250
251
    public function testAutoAddVirtualOption(): void
252
    {
253
        foreach (['actions', 'batch', 'select'] as $type) {
254
            $this->listMapper->add('_'.$type, $type);
255
        }
256
257
        foreach ($this->fieldDescriptionCollection->getElements() as $field) {
258
            $this->assertTrue(
259
                $field->isVirtual(),
260
                'Failed asserting that FieldDescription with type "'.$field->getType().'" is tagged with virtual flag.'
261
            );
262
        }
263
    }
264
265
    public function testAutoSortOnAssociatedProperty(): void
266
    {
267
        $this->listMapper->add('fooName');
268
        $this->listMapper->add(
269
            'fooNameAutoSort',
270
            null,
271
            [
272
                'associated_property' => 'fooAssociatedProperty',
273
            ]
274
        );
275
        $this->listMapper->add(
276
            'fooNameManualSort',
277
            null,
278
            [
279
                'associated_property' => 'fooAssociatedProperty',
280
                'sortable' => false,
281
                'sort_parent_association_mappings' => 'fooSortParentAssociationMapping',
282
                'sort_field_mapping' => 'fooSortFieldMapping',
283
            ]
284
        );
285
286
        $field = $this->listMapper->get('fooName');
287
        $fieldAutoSort = $this->listMapper->get('fooNameAutoSort');
288
        $fieldManualSort = $this->listMapper->get('fooNameManualSort');
289
290
        $this->assertNull($field->getOption('associated_property'));
291
        $this->assertNull($field->getOption('sortable'));
292
        $this->assertNull($field->getOption('sort_parent_association_mappings'));
293
        $this->assertNull($field->getOption('sort_field_mapping'));
294
295
        $this->assertSame('fooAssociatedProperty', $fieldAutoSort->getOption('associated_property'));
296
        $this->assertTrue($fieldAutoSort->getOption('sortable'));
297
        $this->assertSame([['fieldName' => $fieldAutoSort->getName()]], $fieldAutoSort->getOption('sort_parent_association_mappings'));
298
        $this->assertSame(['fieldName' => $fieldAutoSort->getOption('associated_property')], $fieldAutoSort->getOption('sort_field_mapping'));
299
300
        $this->assertSame('fooAssociatedProperty', $fieldManualSort->getOption('associated_property'));
301
        $this->assertFalse($fieldManualSort->getOption('sortable'));
302
        $this->assertSame('fooSortParentAssociationMapping', $fieldManualSort->getOption('sort_parent_association_mappings'));
303
        $this->assertSame('fooSortFieldMapping', $fieldManualSort->getOption('sort_field_mapping'));
304
    }
305
306
    public function testKeys(): void
307
    {
308
        $fieldDescription1 = $this->getFieldDescriptionMock('fooName1', 'fooLabel1');
309
        $fieldDescription2 = $this->getFieldDescriptionMock('fooName2', 'fooLabel2');
310
311
        $this->listMapper->add($fieldDescription1);
312
        $this->listMapper->add($fieldDescription2);
313
314
        $this->assertSame(['fooName1', 'fooName2'], $this->listMapper->keys());
315
    }
316
317
    public function testReorder(): void
318
    {
319
        $fieldDescription1 = $this->getFieldDescriptionMock('fooName1', 'fooLabel1');
320
        $fieldDescription2 = $this->getFieldDescriptionMock('fooName2', 'fooLabel2');
321
        $fieldDescription3 = $this->getFieldDescriptionMock('fooName3', 'fooLabel3');
322
        $fieldDescription4 = $this->getFieldDescriptionMock('fooName4', 'fooLabel4');
323
324
        $this->listMapper->add($fieldDescription1);
325
        $this->listMapper->add($fieldDescription2);
326
        $this->listMapper->add($fieldDescription3);
327
        $this->listMapper->add($fieldDescription4);
328
329
        $this->assertSame([
330
            'fooName1' => $fieldDescription1,
331
            'fooName2' => $fieldDescription2,
332
            'fooName3' => $fieldDescription3,
333
            'fooName4' => $fieldDescription4,
334
        ], $this->fieldDescriptionCollection->getElements());
335
336
        $this->listMapper->reorder(['fooName3', 'fooName2', 'fooName1', 'fooName4']);
337
338
        // print_r is used to compare order of items in associative arrays
339
        $this->assertSame(print_r([
340
            'fooName3' => $fieldDescription3,
341
            'fooName2' => $fieldDescription2,
342
            'fooName1' => $fieldDescription1,
343
            'fooName4' => $fieldDescription4,
344
        ], true), print_r($this->fieldDescriptionCollection->getElements(), true));
345
    }
346
347
    public function testAddOptionRole(): void
348
    {
349
        $this->listMapper->add('bar', 'bar');
350
351
        $this->assertTrue($this->listMapper->has('bar'));
352
353
        $this->listMapper->add('quux', 'bar', ['role' => 'ROLE_QUX']);
354
355
        $this->assertTrue($this->listMapper->has('bar'));
356
        $this->assertFalse($this->listMapper->has('quux'));
357
358
        $this->listMapper
359
            ->add('foobar', 'bar', ['role' => self::DEFAULT_GRANTED_ROLE])
360
            ->add('foo', 'bar', ['role' => 'ROLE_QUX'])
361
            ->add('baz', 'bar');
362
363
        $this->assertTrue($this->listMapper->has('foobar'));
364
        $this->assertFalse($this->listMapper->has('foo'));
365
        $this->assertTrue($this->listMapper->has('baz'));
366
    }
367
368
    private function getFieldDescriptionMock(?string $name = null, ?string $label = null): BaseFieldDescription
369
    {
370
        $fieldDescription = $this->getMockForAbstractClass(BaseFieldDescription::class);
371
372
        if (null !== $name) {
373
            $fieldDescription->setName($name);
374
        }
375
376
        if (null !== $label) {
377
            $fieldDescription->setOption('label', $label);
378
        }
379
380
        return $fieldDescription;
381
    }
382
}
383