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