Completed
Pull Request — 3.x (#6171)
by Vincent
02:58
created

AdminHelperTest::testAddInstancePlural()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.8333
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\Admin;
15
16
use PHPUnit\Framework\TestCase;
17
use Sonata\AdminBundle\Admin\AdminHelper;
18
use Sonata\AdminBundle\Admin\AdminInterface;
19
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
20
use Sonata\AdminBundle\Admin\Pool;
21
use Sonata\AdminBundle\Tests\Fixtures\Entity\Bar;
22
use Sonata\AdminBundle\Tests\Fixtures\Entity\Foo;
23
use Symfony\Component\DependencyInjection\Container;
24
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
25
use Symfony\Component\Form\DataMapperInterface;
26
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
27
use Symfony\Component\Form\FormBuilder;
28
use Symfony\Component\Form\FormFactoryInterface;
29
use Symfony\Component\Form\FormView;
30
use Symfony\Component\HttpFoundation\ParameterBag;
31
use Symfony\Component\HttpFoundation\Request;
32
use Symfony\Component\PropertyAccess\PropertyAccessorBuilder;
33
34
class AdminHelperTest extends TestCase
35
{
36
    /**
37
     * @var AdminHelper
38
     */
39
    protected $helper;
40
41
    protected function setUp(): void
42
    {
43
        $container = new Container();
44
45
        $pool = new Pool($container, 'title', 'logo.png');
46
        $this->helper = new AdminHelper($pool);
47
    }
48
49
    public function testGetChildFormBuilder(): void
50
    {
51
        $formFactory = $this->createMock(FormFactoryInterface::class);
52
        $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
53
54
        $formBuilder = new FormBuilder('test', \stdClass::class, $eventDispatcher, $formFactory);
55
56
        $childFormBuilder = new FormBuilder('elementId', \stdClass::class, $eventDispatcher, $formFactory);
57
        $formBuilder->add($childFormBuilder);
58
59
        $this->assertNull($this->helper->getChildFormBuilder($formBuilder, 'foo'));
60
        $this->assertInstanceOf(FormBuilder::class, $this->helper->getChildFormBuilder($formBuilder, 'test_elementId'));
61
    }
62
63
    public function testGetChildFormView(): void
64
    {
65
        $formView = new FormView();
66
        $formView->vars['id'] = 'test';
67
        $child = new FormView($formView);
0 ignored issues
show
Documentation introduced by
$formView is of type object<Symfony\Component\Form\FormView>, but the function expects a null|object<self>.

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...
68
        $formView->children[] = $child;
69
        $child->vars['id'] = 'test_elementId';
70
71
        $this->assertNull($this->helper->getChildFormView($formView, 'foo'));
72
        $this->assertInstanceOf(FormView::class, $this->helper->getChildFormView($formView, 'test_elementId'));
73
    }
74
75
    /**
76
     * @group legacy
77
     *
78
     * @expectedDeprecation Method Sonata\AdminBundle\Admin\AdminHelper::addNewInstance() is deprecated since sonata-project/admin-bundle 3.x. It will be removed in version 4.0. Use Sonata\AdminBundle\Admin\AdminHelper::addInstance() instead.'
79
     */
80
    public function testAddNewInstance(): void
81
    {
82
        $admin = $this->createMock(AdminInterface::class);
83
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
84
85
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
86
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
87
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
88
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
89
90
        $object = $this->getMockBuilder(\stdClass::class)
91
            ->setMethods(['addFooBar'])
92
            ->getMock();
93
        $object->expects($this->once())->method('addFooBar');
94
95
        $this->helper->addNewInstance($object, $fieldDescription);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...elper::addNewInstance() has been deprecated with message: since sonata-project/admin-bundle 3.x, use to be removed with 4.0. Add a new instance to the related FieldDescriptionInterface value.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
96
    }
97
98
    /**
99
     * @group legacy
100
     *
101
     * @expectedDeprecation Method Sonata\AdminBundle\Admin\AdminHelper::addNewInstance() is deprecated since sonata-project/admin-bundle 3.x. It will be removed in version 4.0. Use Sonata\AdminBundle\Admin\AdminHelper::addInstance() instead.'
102
     */
103
    public function testAddNewInstanceWithParentAssociation(): void
104
    {
105
        $admin = $this->createMock(AdminInterface::class);
106
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
107
108
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
109
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
110
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
111
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([['fieldName' => 'parent']]);
112
113
        $object2 = $this->getMockBuilder(\stdClass::class)
114
            ->setMethods(['addFooBar'])
115
            ->getMock();
116
        $object2->expects($this->once())->method('addFooBar');
117
118
        $object1 = $this->getMockBuilder(\stdClass::class)
119
            ->setMethods(['getParent'])
120
            ->getMock();
121
        $object1->expects($this->once())->method('getParent')->willReturn($object2);
122
123
        $this->helper->addNewInstance($object1, $fieldDescription);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...elper::addNewInstance() has been deprecated with message: since sonata-project/admin-bundle 3.x, use to be removed with 4.0. Add a new instance to the related FieldDescriptionInterface value.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
124
    }
125
126
    /**
127
     * @group legacy
128
     *
129
     * @expectedDeprecation Method Sonata\AdminBundle\Admin\AdminHelper::addNewInstance() is deprecated since sonata-project/admin-bundle 3.x. It will be removed in version 4.0. Use Sonata\AdminBundle\Admin\AdminHelper::addInstance() instead.'
130
     */
131
    public function testAddNewInstancePlural(): void
132
    {
133
        $admin = $this->createMock(AdminInterface::class);
134
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
135
136
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
137
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
138
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBars']);
139
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
140
141
        $object = $this->getMockBuilder(\stdClass::class)
142
            ->setMethods(['addFooBar'])
143
            ->getMock();
144
        $object->expects($this->once())->method('addFooBar');
145
146
        $this->helper->addNewInstance($object, $fieldDescription);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...elper::addNewInstance() has been deprecated with message: since sonata-project/admin-bundle 3.x, use to be removed with 4.0. Add a new instance to the related FieldDescriptionInterface value.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
147
    }
148
149
    /**
150
     * @group legacy
151
     *
152
     * @expectedDeprecation Method Sonata\AdminBundle\Admin\AdminHelper::addNewInstance() is deprecated since sonata-project/admin-bundle 3.x. It will be removed in version 4.0. Use Sonata\AdminBundle\Admin\AdminHelper::addInstance() instead.'
153
     */
154
    public function testAddNewInstanceInflector(): void
155
    {
156
        $admin = $this->createMock(AdminInterface::class);
157
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
158
159
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
160
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
161
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'entries']);
162
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
163
164
        $object = $this->getMockBuilder(\stdClass::class)
165
            ->setMethods(['addEntry'])
166
            ->getMock();
167
        $object->expects($this->once())->method('addEntry');
168
169
        $this->helper->addNewInstance($object, $fieldDescription);
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Admin...elper::addNewInstance() has been deprecated with message: since sonata-project/admin-bundle 3.x, use to be removed with 4.0. Add a new instance to the related FieldDescriptionInterface value.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
170
    }
171
172
    public function testAddInstance(): void
173
    {
174
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
175
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
176
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
177
178
        $object = $this->getMockBuilder(\stdClass::class)
179
            ->setMethods(['addFooBar'])
180
            ->getMock();
181
        $object->expects($this->once())->method('addFooBar');
182
183
        AdminHelper::addInstance($object, $fieldDescription, new \stdClass());
0 ignored issues
show
Documentation introduced by
new \stdClass() is of type object<stdClass>, but the function expects a object<Sonata\AdminBundle\Admin\object>.

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...
184
    }
185
186
    public function testAddInstanceWithParentAssociation(): void
187
    {
188
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
189
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
190
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([['fieldName' => 'parent']]);
191
192
        $object2 = $this->getMockBuilder(\stdClass::class)
193
            ->setMethods(['addFooBar'])
194
            ->getMock();
195
        $object2->expects($this->once())->method('addFooBar');
196
197
        $object1 = $this->getMockBuilder(\stdClass::class)
198
            ->setMethods(['getParent'])
199
            ->getMock();
200
        $object1->expects($this->once())->method('getParent')->willReturn($object2);
201
202
        AdminHelper::addInstance($object1, $fieldDescription, new \stdClass());
0 ignored issues
show
Documentation introduced by
new \stdClass() is of type object<stdClass>, but the function expects a object<Sonata\AdminBundle\Admin\object>.

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...
203
    }
204
205
    public function testAddInstancePlural(): void
206
    {
207
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
208
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBars']);
209
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
210
211
        $object = $this->getMockBuilder(\stdClass::class)
212
            ->setMethods(['addFooBar'])
213
            ->getMock();
214
        $object->expects($this->once())->method('addFooBar');
215
216
        AdminHelper::addInstance($object, $fieldDescription, new \stdClass());
0 ignored issues
show
Documentation introduced by
new \stdClass() is of type object<stdClass>, but the function expects a object<Sonata\AdminBundle\Admin\object>.

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...
217
    }
218
219
    public function testAddInstanceInflector(): void
220
    {
221
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
222
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'entries']);
223
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
224
225
        $object = $this->getMockBuilder(\stdClass::class)
226
            ->setMethods(['addEntry'])
227
            ->getMock();
228
        $object->expects($this->once())->method('addEntry');
229
230
        AdminHelper::addInstance($object, $fieldDescription, new \stdClass());
0 ignored issues
show
Documentation introduced by
new \stdClass() is of type object<stdClass>, but the function expects a object<Sonata\AdminBundle\Admin\object>.

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...
231
    }
232
233
    public function testGetElementAccessPath(): void
234
    {
235
        $object = $this->getMockBuilder(\stdClass::class)
236
            ->setMethods(['getPathToObject'])
237
            ->getMock();
238
        $subObject = $this->getMockBuilder(\stdClass::class)
239
            ->setMethods(['getAnother'])
240
            ->getMock();
241
        $sub2Object = $this->getMockBuilder(\stdClass::class)
242
            ->setMethods(['getMoreThings'])
243
            ->getMock();
244
245
        $object->expects($this->atLeastOnce())->method('getPathToObject')->willReturn([$subObject]);
246
        $subObject->expects($this->atLeastOnce())->method('getAnother')->willReturn($sub2Object);
247
        $sub2Object->expects($this->atLeastOnce())->method('getMoreThings')->willReturn('Value');
248
249
        $path = $this->helper->getElementAccessPath('uniquePartOfId_path_to_object_0_another_more_things', $object);
250
251
        $this->assertSame('path_to_object[0].another.more_things', $path);
252
    }
253
254
    public function testItThrowsExceptionWhenDoesNotFindTheFullPath(): void
255
    {
256
        $path = 'uniquePartOfId_path_to_object_0_more_calls';
257
        $object = $this->getMockBuilder(\stdClass::class)
258
            ->setMethods(['getPathToObject'])
259
            ->getMock();
260
        $subObject = $this->getMockBuilder(\stdClass::class)
261
            ->setMethods(['getMore'])
262
            ->getMock();
263
264
        $object->expects($this->atLeastOnce())->method('getPathToObject')->willReturn([$subObject]);
265
        $subObject->expects($this->atLeastOnce())->method('getMore')->willReturn('Value');
266
267
        $this->expectException(\Exception::class);
268
        $this->expectExceptionMessage('Could not get element id from '.$path.' Failing part: calls');
269
270
        $this->helper->getElementAccessPath($path, $object);
271
    }
272
273
    public function testAppendFormFieldElement(): void
274
    {
275
        $container = new Container();
276
277
        $propertyAccessorBuilder = new PropertyAccessorBuilder();
278
        $propertyAccessor = $propertyAccessorBuilder->getPropertyAccessor();
279
        $pool = new Pool($container, 'title', 'logo.png', [], $propertyAccessor);
280
        $helper = new AdminHelper($pool);
281
282
        $admin = $this->createMock(AdminInterface::class);
283
        $admin
284
            ->method('getClass')
285
            ->willReturn(Foo::class);
286
287
        $associationAdmin = $this->createMock(AdminInterface::class);
288
        $associationAdmin
289
            ->method('getClass')
290
            ->willReturn(Bar::class);
291
292
        $associationMapping = [
293
            'fieldName' => 'bar',
294
            'targetEntity' => Foo::class,
295
            'sourceEntity' => Foo::class,
296
            'isOwningSide' => false,
297
        ];
298
299
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
300
        $fieldDescription->method('getAssociationAdmin')->willReturn($associationAdmin);
301
        $fieldDescription->method('getAssociationMapping')->willReturn($associationMapping);
302
        $fieldDescription->method('getParentAssociationMappings')->willReturn([]);
303
304
        $admin
305
            ->method('getFormFieldDescription')
306
            ->willReturn($fieldDescription);
307
308
        $associationAdmin
309
            ->method('getFormFieldDescriptions')
310
            ->willReturn([
311
                'bar' => $fieldDescription,
312
            ]);
313
314
        $request = $this->createMock(Request::class);
315
        $request
316
            ->method('get')
317
            ->willReturn([
318
                'bar' => [
319
                    [
320
                        'baz' => [
321
                            'baz' => true,
322
                        ],
323
                    ],
324
                    ['_delete' => true],
325
                ],
326
            ]);
327
328
        $request->request = new ParameterBag();
329
330
        $admin
331
            ->method('getRequest')
332
            ->will($this->onConsecutiveCalls($request, $request, $request, null, $request, $request, $request, $request, null, $request));
333
334
        $foo = $this->createMock(Foo::class);
335
        $admin
336
            ->method('hasSubject')
337
            ->willReturn(true);
338
        $admin
339
            ->method('getSubject')
340
            ->willReturn($foo);
341
342
        $bar = new \stdClass();
343
        $associationAdmin
344
            ->expects($this->atLeastOnce())
345
            ->method('getNewInstance')
346
            ->willReturn($bar);
347
348
        $foo->expects($this->atLeastOnce())->method('addBar')->with($bar);
349
350
        $dataMapper = $this->createMock(DataMapperInterface::class);
351
        $formFactory = $this->createMock(FormFactoryInterface::class);
352
        $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
353
        $formBuilder = new FormBuilder('test', \get_class($foo), $eventDispatcher, $formFactory);
354
        $childFormBuilder = new FormBuilder('bar', \stdClass::class, $eventDispatcher, $formFactory);
355
        $childFormBuilder->setCompound(true);
356
        $childFormBuilder->setDataMapper($dataMapper);
357
        $subChildFormBuilder = new FormBuilder('baz', \stdClass::class, $eventDispatcher, $formFactory);
358
        $subChildFormBuilder->setCompound(true);
359
        $subChildFormBuilder->setDataMapper($dataMapper);
360
        $childFormBuilder->add($subChildFormBuilder);
361
362
        $formBuilder->setCompound(true);
363
        $formBuilder->setDataMapper($dataMapper);
364
        $formBuilder->add($childFormBuilder);
365
366
        $associationAdmin->expects($this->atLeastOnce())->method('setSubject')->with($bar);
367
        $admin->method('getFormBuilder')->willReturn($formBuilder);
368
369
        $finalForm = $helper->appendFormFieldElement($admin, $foo, 'test_bar')[1];
370
371
        foreach ($finalForm->get($childFormBuilder->getName()) as $childField) {
372
            $this->assertFalse($childField->has('_delete'));
373
        }
374
375
        $deleteFormBuilder = new FormBuilder('_delete', null, $eventDispatcher, $formFactory);
376
        $subChildFormBuilder->add($deleteFormBuilder, CheckboxType::class, ['delete' => false]);
377
378
        $finalForm = $helper->appendFormFieldElement($admin, $foo, 'test_bar')[1];
379
380
        foreach ($finalForm->get($childFormBuilder->getName()) as $childField) {
381
            $this->assertTrue($childField->has('_delete'));
382
            $this->assertSame('', $childField->get('_delete')->getData());
383
        }
384
    }
385
386
    public function testAppendFormFieldElementNested(): void
387
    {
388
        $admin = $this->createMock(AdminInterface::class);
389
        $object = $this->getMockBuilder(\stdClass::class)
390
            ->setMethods(['getSubObject'])
391
            ->getMock();
392
393
        $subObject = $this->getMockBuilder(\stdClass::class)
394
            ->setMethods(['getAnd'])
395
            ->getMock();
396
        $sub2Object = $this->getMockBuilder(\stdClass::class)
397
            ->setMethods(['getMore'])
398
            ->getMock();
399
        $sub3Object = $this->getMockBuilder(\stdClass::class)
400
            ->setMethods(['getFinalData'])
401
            ->getMock();
402
        $dataMapper = $this->createMock(DataMapperInterface::class);
403
        $formFactory = $this->createMock(FormFactoryInterface::class);
404
        $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
405
        $formBuilder = new FormBuilder('test', \get_class($object), $eventDispatcher, $formFactory);
406
        $childFormBuilder = new FormBuilder('subObject', \get_class($subObject), $eventDispatcher, $formFactory);
407
408
        $object->expects($this->atLeastOnce())->method('getSubObject')->willReturn([$subObject]);
409
        $subObject->expects($this->atLeastOnce())->method('getAnd')->willReturn($sub2Object);
410
        $sub2Object->expects($this->atLeastOnce())->method('getMore')->willReturn([$sub3Object]);
411
        $sub3Object->expects($this->atLeastOnce())->method('getFinalData')->willReturn('value');
412
413
        $formBuilder->setCompound(true);
414
        $formBuilder->setDataMapper($dataMapper);
415
        $formBuilder->add($childFormBuilder);
416
417
        $admin->method('hasSubject')->willReturn(true);
418
        $admin->method('getSubject')->willReturn($object);
419
        $admin->expects($this->once())->method('getFormBuilder')->willReturn($formBuilder);
420
421
        $this->expectException(\Exception::class);
422
        $this->expectExceptionMessage('unknown collection class');
423
424
        $this->helper->appendFormFieldElement($admin, $object, 'uniquePartOfId_sub_object_0_and_more_0_final_data');
425
    }
426
}
427