[BUGFIX] Notice free FormEngine testing
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / TcaCheckboxItemsTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Prophecy\Argument;
18 use Prophecy\Prophecy\ObjectProphecy;
19 use TYPO3\CMS\Backend\Form\FormDataProvider\TcaCheckboxItems;
20 use TYPO3\CMS\Core\Localization\LanguageService;
21 use TYPO3\CMS\Core\Messaging\FlashMessage;
22 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
23 use TYPO3\CMS\Core\Messaging\FlashMessageService;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
26
27 /**
28 * Test case
29 */
30 class TcaCheckboxItemsTest extends UnitTestCase
31 {
32 /**
33 * @var array A backup of registered singleton instances
34 */
35 protected $singletonInstances = [];
36
37 protected function setUp()
38 {
39 $this->singletonInstances = GeneralUtility::getSingletonInstances();
40 }
41
42 protected function tearDown()
43 {
44 GeneralUtility::purgeInstances();
45 GeneralUtility::resetSingletonInstances($this->singletonInstances);
46 parent::tearDown();
47 }
48
49 public function checkboxConfigurationDataProvider()
50 {
51 return [
52 'simpleCheckboxConfig' => [
53 'input' => [
54 'tableName' => 'foo',
55 'processedTca' => [
56 'columns' => [
57 'aField' => [
58 'config' => [
59 'type' => 'check',
60 'items' => [
61 0 => [
62 'foo',
63 'bar',
64 ],
65 ],
66 ],
67 ],
68 ],
69 ],
70 ],
71 'expected' => [
72 'tableName' => 'foo',
73 'processedTca' => [
74 'columns' => [
75 'aField' => [
76 'config' => [
77 'type' => 'check',
78 'items' => [
79 0 => [
80 'foo', //@todo a followup patch should refactor towards 'label' => 'foo'
81 'bar', //@todo a followup patch should remove this numeric key altogether
82 'invertStateDisplay' => false
83 ],
84 ],
85 ],
86 ],
87 ],
88 ],
89 ]
90 ],
91 'brokenSimpleCheckboxConfig' => [
92 'input' => [
93 'tableName' => 'foo',
94 'processedTca' => [
95 'columns' => [
96 'aField' => [
97 'config' => [
98 'type' => 'check',
99 'items' => [
100 0 => [
101 'foo',
102 'bar',
103 'baz'
104 ],
105 ],
106 ],
107 ],
108 ],
109 ],
110 ],
111 'expected' => [
112 'tableName' => 'foo',
113 'processedTca' => [
114 'columns' => [
115 'aField' => [
116 'config' => [
117 'type' => 'check',
118 'items' => [
119 0 => [
120 'foo',
121 'bar',
122 'invertStateDisplay' => false
123 ],
124 ],
125 ],
126 ],
127 ],
128 ],
129 ]
130 ],
131 'toggleCheckboxConfig' => [
132 'input' => [
133 'tableName' => 'foo',
134 'processedTca' => [
135 'columns' => [
136 'aField' => [
137 'config' => [
138 'type' => 'check',
139 'renderType' => 'checkboxToggle',
140 'items' => [
141 0 => [
142 'foo',
143 'bar',
144 'labelChecked' => 'Enabled',
145 'labelUnchecked' => 'Disabled'
146 ],
147 ],
148 ],
149 ],
150 ],
151 ],
152 ],
153 'expected' => [
154 'tableName' => 'foo',
155 'processedTca' => [
156 'columns' => [
157 'aField' => [
158 'config' => [
159 'type' => 'check',
160 'renderType' => 'checkboxToggle',
161 'items' => [
162 0 => [
163 'foo',
164 'bar',
165 'invertStateDisplay' => false
166 ],
167 ],
168 ],
169 ],
170 ],
171 ],
172 ]
173 ],
174 'inverted_toggleCheckboxConfig' => [
175 'input' => [
176 'tableName' => 'foo',
177 'processedTca' => [
178 'columns' => [
179 'aField' => [
180 'config' => [
181 'type' => 'check',
182 'renderType' => 'checkboxToggle',
183 'items' => [
184 0 => [
185 'foo',
186 'bar',
187 'labelChecked' => 'Enabled',
188 'labelUnchecked' => 'Disabled',
189 'invertStateDisplay' => true
190 ],
191 ],
192 ],
193 ],
194 ],
195 ],
196 ],
197 'expected' => [
198 'tableName' => 'foo',
199 'processedTca' => [
200 'columns' => [
201 'aField' => [
202 'config' => [
203 'type' => 'check',
204 'renderType' => 'checkboxToggle',
205 'items' => [
206 0 => [
207 'foo',
208 'bar',
209 'invertStateDisplay' => true
210 ],
211 ],
212 ],
213 ],
214 ],
215 ],
216 ]
217 ],
218 'labeledCheckboxConfig' => [
219 'input' => [
220 'tableName' => 'foo',
221 'processedTca' => [
222 'columns' => [
223 'aField' => [
224 'config' => [
225 'type' => 'check',
226 'renderType' => 'checkboxLabeledToggle',
227 'items' => [
228 0 => [
229 'foo',
230 'bar',
231 'labelChecked' => 'Enabled',
232 'labelUnchecked' => 'Disabled'
233 ],
234 ],
235 ],
236 ],
237 ],
238 ],
239 ],
240 'expected' => [
241 'tableName' => 'foo',
242 'processedTca' => [
243 'columns' => [
244 'aField' => [
245 'config' => [
246 'type' => 'check',
247 'renderType' => 'checkboxLabeledToggle',
248 'items' => [
249 0 => [
250 'foo',
251 'bar',
252 'labelChecked' => 'Enabled',
253 'labelUnchecked' => 'Disabled',
254 'invertStateDisplay' => false
255 ],
256 ],
257 ],
258 ],
259 ],
260 ],
261 ]
262 ],
263 'inverted_labeledCheckboxConfig' => [
264 'input' => [
265 'tableName' => 'foo',
266 'processedTca' => [
267 'columns' => [
268 'aField' => [
269 'config' => [
270 'type' => 'check',
271 'renderType' => 'checkboxLabeledToggle',
272 'items' => [
273 0 => [
274 'foo',
275 'bar',
276 'labelChecked' => 'Enabled',
277 'labelUnchecked' => 'Disabled',
278 'invertStateDisplay' => true
279 ],
280 ],
281 ],
282 ],
283 ],
284 ],
285 ],
286 'expected' => [
287 'tableName' => 'foo',
288 'processedTca' => [
289 'columns' => [
290 'aField' => [
291 'config' => [
292 'type' => 'check',
293 'renderType' => 'checkboxLabeledToggle',
294 'items' => [
295 0 => [
296 'foo',
297 'bar',
298 'labelChecked' => 'Enabled',
299 'labelUnchecked' => 'Disabled',
300 'invertStateDisplay' => true
301 ],
302 ],
303 ],
304 ],
305 ],
306 ],
307 ]
308 ],
309 'iconCheckboxConfig' => [
310 'input' => [
311 'tableName' => 'foo',
312 'processedTca' => [
313 'columns' => [
314 'aField' => [
315 'config' => [
316 'type' => 'check',
317 'renderType' => 'checkboxIconToggle',
318 'items' => [
319 0 => [
320 'foo',
321 'bar',
322 'labelChecked' => 'Enabled',
323 'labelUnchecked' => 'Disabled',
324 'iconIdentifierChecked' => 'styleguide-icon-toggle-checked',
325 'iconIdentifierUnchecked' => 'styleguide-icon-toggle-checked',
326 ],
327 ],
328 ],
329 ],
330 ],
331 ],
332 ],
333 'expected' => [
334 'tableName' => 'foo',
335 'processedTca' => [
336 'columns' => [
337 'aField' => [
338 'config' => [
339 'type' => 'check',
340 'renderType' => 'checkboxIconToggle',
341 'items' => [
342 0 => [
343 'foo',
344 'bar',
345 'iconIdentifierChecked' => 'styleguide-icon-toggle-checked',
346 'iconIdentifierUnchecked' => 'styleguide-icon-toggle-checked',
347 'invertStateDisplay' => false
348 ],
349 ],
350 ],
351 ],
352 ],
353 ],
354 ]
355 ],
356 'inverted_iconCheckboxConfig' => [
357 'input' => [
358 'tableName' => 'foo',
359 'processedTca' => [
360 'columns' => [
361 'aField' => [
362 'config' => [
363 'type' => 'check',
364 'renderType' => 'checkboxIconToggle',
365 'items' => [
366 0 => [
367 'foo',
368 'bar',
369 'labelChecked' => 'Enabled',
370 'labelUnchecked' => 'Disabled',
371 'iconIdentifierChecked' => 'styleguide-icon-toggle-checked',
372 'iconIdentifierUnchecked' => 'styleguide-icon-toggle-checked',
373 'invertStateDisplay' => true
374 ],
375 ],
376 ],
377 ],
378 ],
379 ],
380 ],
381 'expected' => [
382 'tableName' => 'foo',
383 'processedTca' => [
384 'columns' => [
385 'aField' => [
386 'config' => [
387 'type' => 'check',
388 'renderType' => 'checkboxIconToggle',
389 'items' => [
390 0 => [
391 'foo',
392 'bar',
393 'iconIdentifierChecked' => 'styleguide-icon-toggle-checked',
394 'iconIdentifierUnchecked' => 'styleguide-icon-toggle-checked',
395 'invertStateDisplay' => true
396 ],
397 ],
398 ],
399 ],
400 ],
401 ],
402 ]
403 ],
404 ];
405 }
406
407 /**
408 * @test
409 * @dataProvider checkboxConfigurationDataProvider
410 */
411 public function addDataKeepExistingItems($input, $expectedResult)
412 {
413 $languageService = $this->prophesize(LanguageService::class);
414 $GLOBALS['LANG'] = $languageService->reveal();
415 $languageService->sL(Argument::cetera())->willReturnArgument(0);
416
417 $this->assertSame($expectedResult, (new TcaCheckboxItems)->addData($input));
418 }
419
420 /**
421 * @test
422 */
423 public function addDataThrowsExceptionIfItemsAreNoArray()
424 {
425 $input = [
426 'processedTca' => [
427 'columns' => [
428 'aField' => [
429 'config' => [
430 'type' => 'check',
431 'items' => [
432 0 => 'aoeu',
433 ],
434 ],
435 ],
436 ],
437 ],
438 'tableName' => 'foo'
439 ];
440
441 $languageService = $this->prophesize(LanguageService::class);
442 $GLOBALS['LANG'] = $languageService->reveal();
443 $languageService->sL(Argument::cetera())->willReturnArgument(0);
444
445 $this->expectException(\UnexpectedValueException::class);
446 $this->expectExceptionCode(1440499337);
447 (new TcaCheckboxItems)->addData($input);
448 }
449
450 /**
451 * @test
452 */
453 public function addDataThrowsExceptionIfItemLabelIsNotSet()
454 {
455 $input = [
456 'processedTca' => [
457 'columns' => [
458 'aField' => [
459 'config' => [
460 'type' => 'check',
461 'items' => [
462 0 => [
463 'funnyKey' => 'funnyValue',
464 ],
465 ],
466 ],
467 ],
468 ],
469 ],
470 'tableName' => 'foo'
471 ];
472
473 $languageService = $this->prophesize(LanguageService::class);
474 $GLOBALS['LANG'] = $languageService->reveal();
475 $languageService->sL(Argument::cetera())->willReturnArgument(0);
476
477 $this->expectException(\UnexpectedValueException::class);
478 $this->expectExceptionCode(1440499338);
479 (new TcaCheckboxItems)->addData($input);
480 }
481
482 /**
483 * @test
484 */
485 public function addDataTranslatesItemLabels()
486 {
487 $input = [
488 'processedTca' => [
489 'columns' => [
490 'aField' => [
491 'config' => [
492 'type' => 'check',
493 'items' => [
494 0 => [
495 0 => 'aLabel',
496 1 => 'aValue',
497 ],
498 ],
499 ],
500 ],
501 ],
502 ],
503 'tableName' => 'foo'
504 ];
505
506 /** @var LanguageService|ObjectProphecy $languageService */
507 $languageService = $this->prophesize(LanguageService::class);
508 $GLOBALS['LANG'] = $languageService->reveal();
509
510 $languageService->sL('aLabel')->shouldBeCalled()->willReturn('translated');
511
512 $expected = $input;
513 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'translated';
514 $expected['processedTca']['columns']['aField']['config']['items'][0]['invertStateDisplay'] = false;
515
516 $this->assertSame($expected, (new TcaCheckboxItems)->addData($input));
517 }
518
519 /**
520 * @test
521 */
522 public function addDataCallsItemsProcFunc()
523 {
524 $input = [
525 'tableName' => 'aTable',
526 'databaseRow' => [],
527 'processedTca' => [
528 'columns' => [
529 'aField' => [
530 'config' => [
531 'type' => 'check',
532 'items' => [],
533 'itemsProcFunc' => function (array $parameters, $pObj) {
534 $parameters['items'] = [
535 'foo' => 'bar',
536 ];
537 },
538 ],
539 ],
540 ],
541 ],
542 ];
543
544 $languageService = $this->prophesize(LanguageService::class);
545 $GLOBALS['LANG'] = $languageService->reveal();
546 $languageService->sL(Argument::cetera())->willReturnArgument(0);
547
548 $expected = $input;
549 $expected['processedTca']['columns']['aField']['config'] = [
550 'type' => 'check',
551 'items' => [
552 'foo' => 'bar',
553 ],
554 ];
555 $this->assertSame($expected, (new TcaCheckboxItems)->addData($input));
556 }
557
558 /**
559 * @test
560 */
561 public function addDataItemsProcFuncReceivesParameters()
562 {
563 $input = [
564 'tableName' => 'aTable',
565 'databaseRow' => [
566 'aField' => 'aValue',
567 ],
568 'pageTsConfig' => [
569 'TCEFORM.' => [
570 'aTable.' => [
571 'aField.' => [
572 'itemsProcFunc.' => [
573 'itemParamKey' => 'itemParamValue',
574 ],
575 ]
576 ],
577 ],
578 ],
579 'flexParentDatabaseRow' => [
580 'aParentDatabaseRowFieldName' => 'aParentDatabaseRowFieldValue',
581 ],
582 'processedTca' => [
583 'columns' => [
584 'aField' => [
585 'config' => [
586 'type' => 'check',
587 'aKey' => 'aValue',
588 'items' => [
589 0 => [
590 0 => 'foo',
591 1 => 'bar',
592 'invertedStateDisplay' => false
593 ],
594 ],
595 'itemsProcFunc' => function (array $parameters, $pObj) {
596 if (
597 $parameters['items'] !== [ 0 => [0=>'foo', 1 =>'bar', 'invertStateDisplay' => false]]
598 || $parameters['config']['aKey'] !== 'aValue'
599 || $parameters['TSconfig'] !== [ 'itemParamKey' => 'itemParamValue' ]
600 || $parameters['table'] !== 'aTable'
601 || $parameters['row'] !== [ 'aField' => 'aValue' ]
602 || $parameters['field'] !== 'aField'
603 || $parameters['flexParentDatabaseRow']['aParentDatabaseRowFieldName'] !== 'aParentDatabaseRowFieldValue'
604 ) {
605 throw new \UnexpectedValueException('broken', 1476109402);
606 }
607 },
608 ],
609 ],
610 ],
611 ],
612 ];
613
614 $languageService = $this->prophesize(LanguageService::class);
615 $GLOBALS['LANG'] = $languageService->reveal();
616 $languageService->sL(Argument::cetera())->willReturnArgument(0);
617 /** @var FlashMessage|ObjectProphecy $flashMessage */
618 $flashMessage = $this->prophesize(FlashMessage::class);
619 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
620 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
621 $flashMessageService = $this->prophesize(FlashMessageService::class);
622 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
623 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
624 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
625 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
626
627 // itemsProcFunc must NOT have raised an exception
628 $flashMessageQueue->enqueue($flashMessage)->shouldNotBeCalled();
629
630 (new TcaCheckboxItems)->addData($input);
631 }
632
633 /**
634 * @test
635 */
636 public function addDataItemsProcFuncEnqueuesFlashMessageOnException()
637 {
638 $input = [
639 'tableName' => 'aTable',
640 'databaseRow' => [
641 'aField' => 'aValue',
642 ],
643 'pageTsConfig' => [
644 'TCEFORM.' => [
645 'aTable.' => [
646 'aField.' => [
647 'itemsProcFunc.' => [
648 'itemParamKey' => 'itemParamValue',
649 ],
650 ]
651 ],
652 ],
653 ],
654 'processedTca' => [
655 'columns' => [
656 'aField' => [
657 'config' => [
658 'type' => 'check',
659 'aKey' => 'aValue',
660 'items' => [
661 0 => [
662 'foo',
663 'bar',
664 ],
665 ],
666 'itemsProcFunc' => function (array $parameters, $pObj) {
667 throw new \UnexpectedValueException('anException', 1438604329);
668 },
669 ],
670 ],
671 ],
672 ],
673 ];
674
675 $languageService = $this->prophesize(LanguageService::class);
676 $GLOBALS['LANG'] = $languageService->reveal();
677 /** @var FlashMessage|ObjectProphecy $flashMessage */
678 $flashMessage = $this->prophesize(FlashMessage::class);
679 GeneralUtility::addInstance(FlashMessage::class, $flashMessage->reveal());
680 /** @var FlashMessageService|ObjectProphecy $flashMessageService */
681 $flashMessageService = $this->prophesize(FlashMessageService::class);
682 GeneralUtility::setSingletonInstance(FlashMessageService::class, $flashMessageService->reveal());
683 /** @var FlashMessageQueue|ObjectProphecy $flashMessageQueue */
684 $flashMessageQueue = $this->prophesize(FlashMessageQueue::class);
685 $flashMessageService->getMessageQueueByIdentifier(Argument::cetera())->willReturn($flashMessageQueue->reveal());
686
687 $flashMessageQueue->enqueue($flashMessage)->shouldBeCalled();
688
689 (new TcaCheckboxItems)->addData($input);
690 }
691
692 /**
693 * @test
694 */
695 public function addDataTranslatesItemLabelsFromPageTsConfig()
696 {
697 $input = [
698 'tableName' => 'aTable',
699 'processedTca' => [
700 'columns' => [
701 'aField' => [
702 'config' => [
703 'type' => 'check',
704 'items' => [
705 0 => [
706 0 => 'aLabel',
707 1 => 'aValue',
708 ],
709 ],
710 ],
711 ],
712 ],
713 ],
714 'pageTsConfig' => [
715 'TCEFORM.' => [
716 'aTable.' => [
717 'aField.' => [
718 'altLabels.' => [
719 0 => 'labelOverride',
720 ],
721 ]
722 ],
723 ],
724 ],
725 ];
726
727 /** @var LanguageService|ObjectProphecy $languageService */
728 $languageService = $this->prophesize(LanguageService::class);
729 $GLOBALS['LANG'] = $languageService->reveal();
730 $languageService->sL('aLabel')->willReturnArgument(0);
731
732 $languageService->sL('labelOverride')->shouldBeCalled()->willReturnArgument(0);
733
734 $expected = $input;
735 $expected['processedTca']['columns']['aField']['config']['items'][0][0] = 'labelOverride';
736 $expected['processedTca']['columns']['aField']['config']['items'][0]['invertStateDisplay'] = false;
737
738 $this->assertSame($expected, (new TcaCheckboxItems)->addData($input));
739 }
740 }