Completed
Pull Request — 3.x (#6171)
by Vincent
03:04
created

AdminHelperTest::testAddInstance()   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\Bridge\PhpUnit\ExpectDeprecationTrait;
24
use Symfony\Component\DependencyInjection\Container;
25
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
26
use Symfony\Component\Form\DataMapperInterface;
27
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
28
use Symfony\Component\Form\FormBuilder;
29
use Symfony\Component\Form\FormFactoryInterface;
30
use Symfony\Component\Form\FormView;
31
use Symfony\Component\HttpFoundation\ParameterBag;
32
use Symfony\Component\HttpFoundation\Request;
33
use Symfony\Component\PropertyAccess\PropertyAccessorBuilder;
34
35
class AdminHelperTest extends TestCase
36
{
37
    /**
38
     * @var AdminHelper
39
     */
40
    protected $helper;
41
42
    protected function setUp(): void
43
    {
44
        $container = new Container();
45
46
        $pool = new Pool($container, 'title', 'logo.png');
47
        $this->helper = new AdminHelper($pool);
48
    }
49
50
    public function testGetChildFormBuilder(): void
51
    {
52
        $formFactory = $this->createMock(FormFactoryInterface::class);
53
        $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
54
55
        $formBuilder = new FormBuilder('test', \stdClass::class, $eventDispatcher, $formFactory);
56
57
        $childFormBuilder = new FormBuilder('elementId', \stdClass::class, $eventDispatcher, $formFactory);
58
        $formBuilder->add($childFormBuilder);
59
60
        $this->assertNull($this->helper->getChildFormBuilder($formBuilder, 'foo'));
61
        $this->assertInstanceOf(FormBuilder::class, $this->helper->getChildFormBuilder($formBuilder, 'test_elementId'));
62
    }
63
64
    public function testGetChildFormView(): void
65
    {
66
        $formView = new FormView();
67
        $formView->vars['id'] = 'test';
68
        $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...
69
        $formView->children[] = $child;
70
        $child->vars['id'] = 'test_elementId';
71
72
        $this->assertNull($this->helper->getChildFormView($formView, 'foo'));
73
        $this->assertInstanceOf(FormView::class, $this->helper->getChildFormView($formView, 'test_elementId'));
74
    }
75
76
    /**
77
     * @group legacy
78
     *
79
     * @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.
80
     */
81
    public function testAddNewInstance(): void
82
    {
83
        $admin = $this->createMock(AdminInterface::class);
84
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
85
86
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
87
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
88
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
89
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
90
91
        $object = $this->getMockBuilder(\stdClass::class)
92
            ->setMethods(['addFooBar'])
93
            ->getMock();
94
        $object->expects($this->once())->method('addFooBar');
95
96
        $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...
97
    }
98
99
    /**
100
     * @group legacy
101
     *
102
     * @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.
103
     */
104
    public function testAddNewInstanceWithParentAssociation(): void
105
    {
106
        $admin = $this->createMock(AdminInterface::class);
107
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
108
109
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
110
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
111
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
112
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([['fieldName' => 'parent']]);
113
114
        $object2 = $this->getMockBuilder(\stdClass::class)
115
            ->setMethods(['addFooBar'])
116
            ->getMock();
117
        $object2->expects($this->once())->method('addFooBar');
118
119
        $object1 = $this->getMockBuilder(\stdClass::class)
120
            ->setMethods(['getParent'])
121
            ->getMock();
122
        $object1->expects($this->once())->method('getParent')->willReturn($object2);
123
124
        $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...
125
    }
126
127
    /**
128
     * @group legacy
129
     *
130
     * @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.
131
     */
132
    public function testAddNewInstancePlural(): void
133
    {
134
        $admin = $this->createMock(AdminInterface::class);
135
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
136
137
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
138
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
139
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBars']);
140
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
141
142
        $object = $this->getMockBuilder(\stdClass::class)
143
            ->setMethods(['addFooBar'])
144
            ->getMock();
145
        $object->expects($this->once())->method('addFooBar');
146
147
        $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...
148
    }
149
150
    /**
151
     * @group legacy
152
     *
153
     * @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.
154
     */
155
    public function testAddNewInstanceInflector(): void
156
    {
157
        $admin = $this->createMock(AdminInterface::class);
158
        $admin->expects($this->once())->method('getNewInstance')->willReturn(new \stdClass());
159
160
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
161
        $fieldDescription->expects($this->once())->method('getAssociationAdmin')->willReturn($admin);
162
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'entries']);
163
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
164
165
        $object = $this->getMockBuilder(\stdClass::class)
166
            ->setMethods(['addEntry'])
167
            ->getMock();
168
        $object->expects($this->once())->method('addEntry');
169
170
        $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...
171
    }
172
173
    public function testAddInstance(): void
174
    {
175
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
176
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
177
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
178
179
        $object = $this->getMockBuilder(\stdClass::class)
180
            ->setMethods(['addFooBar'])
181
            ->getMock();
182
        $object->expects($this->once())->method('addFooBar');
183
184
        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...
185
    }
186
187
    public function testAddInstanceWithParentAssociation(): void
188
    {
189
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
190
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBar']);
191
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([['fieldName' => 'parent']]);
192
193
        $object2 = $this->getMockBuilder(\stdClass::class)
194
            ->setMethods(['addFooBar'])
195
            ->getMock();
196
        $object2->expects($this->once())->method('addFooBar');
197
198
        $object1 = $this->getMockBuilder(\stdClass::class)
199
            ->setMethods(['getParent'])
200
            ->getMock();
201
        $object1->expects($this->once())->method('getParent')->willReturn($object2);
202
203
        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...
204
    }
205
206
    public function testAddInstancePlural(): void
207
    {
208
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
209
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'fooBars']);
210
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
211
212
        $object = $this->getMockBuilder(\stdClass::class)
213
            ->setMethods(['addFooBar'])
214
            ->getMock();
215
        $object->expects($this->once())->method('addFooBar');
216
217
        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...
218
    }
219
220
    public function testAddInstanceInflector(): void
221
    {
222
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
223
        $fieldDescription->expects($this->once())->method('getAssociationMapping')->willReturn(['fieldName' => 'entries']);
224
        $fieldDescription->expects($this->once())->method('getParentAssociationMappings')->willReturn([]);
225
226
        $object = $this->getMockBuilder(\stdClass::class)
227
            ->setMethods(['addEntry'])
228
            ->getMock();
229
        $object->expects($this->once())->method('addEntry');
230
231
        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...
232
    }
233
234
    public function testGetElementAccessPath(): void
235
    {
236
        $object = $this->getMockBuilder(\stdClass::class)
237
            ->setMethods(['getPathToObject'])
238
            ->getMock();
239
        $subObject = $this->getMockBuilder(\stdClass::class)
240
            ->setMethods(['getAnother'])
241
            ->getMock();
242
        $sub2Object = $this->getMockBuilder(\stdClass::class)
243
            ->setMethods(['getMoreThings'])
244
            ->getMock();
245
246
        $object->expects($this->atLeastOnce())->method('getPathToObject')->willReturn([$subObject]);
247
        $subObject->expects($this->atLeastOnce())->method('getAnother')->willReturn($sub2Object);
248
        $sub2Object->expects($this->atLeastOnce())->method('getMoreThings')->willReturn('Value');
249
250
        $path = $this->helper->getElementAccessPath('uniquePartOfId_path_to_object_0_another_more_things', $object);
251
252
        $this->assertSame('path_to_object[0].another.more_things', $path);
253
    }
254
255
    public function testItThrowsExceptionWhenDoesNotFindTheFullPath(): void
256
    {
257
        $path = 'uniquePartOfId_path_to_object_0_more_calls';
258
        $object = $this->getMockBuilder(\stdClass::class)
259
            ->setMethods(['getPathToObject'])
260
            ->getMock();
261
        $subObject = $this->getMockBuilder(\stdClass::class)
262
            ->setMethods(['getMore'])
263
            ->getMock();
264
265
        $object->expects($this->atLeastOnce())->method('getPathToObject')->willReturn([$subObject]);
266
        $subObject->expects($this->atLeastOnce())->method('getMore')->willReturn('Value');
267
268
        $this->expectException(\Exception::class);
269
        $this->expectExceptionMessage('Could not get element id from '.$path.' Failing part: calls');
270
271
        $this->helper->getElementAccessPath($path, $object);
272
    }
273
274
    public function testAppendFormFieldElement(): void
275
    {
276
        $container = new Container();
277
278
        $propertyAccessorBuilder = new PropertyAccessorBuilder();
279
        $propertyAccessor = $propertyAccessorBuilder->getPropertyAccessor();
280
        $pool = new Pool($container, 'title', 'logo.png', [], $propertyAccessor);
281
        $helper = new AdminHelper($pool);
282
283
        $admin = $this->createMock(AdminInterface::class);
284
        $admin
285
            ->method('getClass')
286
            ->willReturn(Foo::class);
287
288
        $associationAdmin = $this->createMock(AdminInterface::class);
289
        $associationAdmin
290
            ->method('getClass')
291
            ->willReturn(Bar::class);
292
293
        $associationMapping = [
294
            'fieldName' => 'bar',
295
            'targetEntity' => Foo::class,
296
            'sourceEntity' => Foo::class,
297
            'isOwningSide' => false,
298
        ];
299
300
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
301
        $fieldDescription->method('getAssociationAdmin')->willReturn($associationAdmin);
302
        $fieldDescription->method('getAssociationMapping')->willReturn($associationMapping);
303
        $fieldDescription->method('getParentAssociationMappings')->willReturn([]);
304
305
        $admin
306
            ->method('getFormFieldDescription')
307
            ->willReturn($fieldDescription);
308
309
        $associationAdmin
310
            ->method('getFormFieldDescriptions')
311
            ->willReturn([
312
                'bar' => $fieldDescription,
313
            ]);
314
315
        $request = $this->createMock(Request::class);
316
        $request
317
            ->method('get')
318
            ->willReturn([
319
                'bar' => [
320
                    [
321
                        'baz' => [
322
                            'baz' => true,
323
                        ],
324
                    ],
325
                    ['_delete' => true],
326
                ],
327
            ]);
328
329
        $request->request = new ParameterBag();
330
331
        $admin
332
            ->method('getRequest')
333
            ->will($this->onConsecutiveCalls($request, $request, $request, null, $request, $request, $request, $request, null, $request));
334
335
        $foo = $this->createMock(Foo::class);
336
        $admin
337
            ->method('hasSubject')
338
            ->willReturn(true);
339
        $admin
340
            ->method('getSubject')
341
            ->willReturn($foo);
342
343
        $bar = new \stdClass();
344
        $associationAdmin
345
            ->expects($this->atLeastOnce())
346
            ->method('getNewInstance')
347
            ->willReturn($bar);
348
349
        $foo->expects($this->atLeastOnce())->method('addBar')->with($bar);
350
351
        $dataMapper = $this->createMock(DataMapperInterface::class);
352
        $formFactory = $this->createMock(FormFactoryInterface::class);
353
        $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
354
        $formBuilder = new FormBuilder('test', \get_class($foo), $eventDispatcher, $formFactory);
355
        $childFormBuilder = new FormBuilder('bar', \stdClass::class, $eventDispatcher, $formFactory);
356
        $childFormBuilder->setCompound(true);
357
        $childFormBuilder->setDataMapper($dataMapper);
358
        $subChildFormBuilder = new FormBuilder('baz', \stdClass::class, $eventDispatcher, $formFactory);
359
        $subChildFormBuilder->setCompound(true);
360
        $subChildFormBuilder->setDataMapper($dataMapper);
361
        $childFormBuilder->add($subChildFormBuilder);
362
363
        $formBuilder->setCompound(true);
364
        $formBuilder->setDataMapper($dataMapper);
365
        $formBuilder->add($childFormBuilder);
366
367
        $associationAdmin->expects($this->atLeastOnce())->method('setSubject')->with($bar);
368
        $admin->method('getFormBuilder')->willReturn($formBuilder);
369
370
        $finalForm = $helper->appendFormFieldElement($admin, $foo, 'test_bar')[1];
371
372
        foreach ($finalForm->get($childFormBuilder->getName()) as $childField) {
373
            $this->assertFalse($childField->has('_delete'));
374
        }
375
376
        $deleteFormBuilder = new FormBuilder('_delete', null, $eventDispatcher, $formFactory);
377
        $subChildFormBuilder->add($deleteFormBuilder, CheckboxType::class, ['delete' => false]);
378
379
        $finalForm = $helper->appendFormFieldElement($admin, $foo, 'test_bar')[1];
380
381
        foreach ($finalForm->get($childFormBuilder->getName()) as $childField) {
382
            $this->assertTrue($childField->has('_delete'));
383
            $this->assertSame('', $childField->get('_delete')->getData());
384
        }
385
    }
386
387
    public function testAppendFormFieldElementNested(): void
388
    {
389
        $admin = $this->createMock(AdminInterface::class);
390
        $object = $this->getMockBuilder(\stdClass::class)
391
            ->setMethods(['getSubObject'])
392
            ->getMock();
393
394
        $subObject = $this->getMockBuilder(\stdClass::class)
395
            ->setMethods(['getAnd'])
396
            ->getMock();
397
        $sub2Object = $this->getMockBuilder(\stdClass::class)
398
            ->setMethods(['getMore'])
399
            ->getMock();
400
        $sub3Object = $this->getMockBuilder(\stdClass::class)
401
            ->setMethods(['getFinalData'])
402
            ->getMock();
403
        $dataMapper = $this->createMock(DataMapperInterface::class);
404
        $formFactory = $this->createMock(FormFactoryInterface::class);
405
        $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
406
        $formBuilder = new FormBuilder('test', \get_class($object), $eventDispatcher, $formFactory);
407
        $childFormBuilder = new FormBuilder('subObject', \get_class($subObject), $eventDispatcher, $formFactory);
408
409
        $object->expects($this->atLeastOnce())->method('getSubObject')->willReturn([$subObject]);
410
        $subObject->expects($this->atLeastOnce())->method('getAnd')->willReturn($sub2Object);
411
        $sub2Object->expects($this->atLeastOnce())->method('getMore')->willReturn([$sub3Object]);
412
        $sub3Object->expects($this->atLeastOnce())->method('getFinalData')->willReturn('value');
413
414
        $formBuilder->setCompound(true);
415
        $formBuilder->setDataMapper($dataMapper);
416
        $formBuilder->add($childFormBuilder);
417
418
        $admin->method('hasSubject')->willReturn(true);
419
        $admin->method('getSubject')->willReturn($object);
420
        $admin->expects($this->once())->method('getFormBuilder')->willReturn($formBuilder);
421
422
        $this->expectException(\Exception::class);
423
        $this->expectExceptionMessage('unknown collection class');
424
425
        $this->helper->appendFormFieldElement($admin, $object, 'uniquePartOfId_sub_object_0_and_more_0_final_data');
426
    }
427
}
428