[!!!][TASK] Extract testing framework for TYPO3
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / TcaRecordTitleTest.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\TcaRecordTitle;
20 use TYPO3\CMS\Lang\LanguageService;
21
22 /**
23 * Test case
24 */
25 class TcaRecordTitleTest extends \TYPO3\CMS\Components\TestingFramework\Core\UnitTestCase
26 {
27 /**
28 * @var TcaRecordTitle
29 */
30 protected $subject;
31
32 /**
33 * @var string
34 */
35 protected $timeZone;
36
37 public function setUp()
38 {
39 $this->subject = new TcaRecordTitle();
40 $this->timeZone = date_default_timezone_get();
41 date_default_timezone_set('UTC');
42 }
43
44 protected function tearDown()
45 {
46 date_default_timezone_set($this->timeZone);
47 }
48
49 /**
50 * @test
51 */
52 public function addDataThrowsExceptionWithMissingLabel()
53 {
54 $input = [
55 'tableName' => 'aTable',
56 'databaseRew' => [],
57 'processedTca' => [
58 'ctrl' => [],
59 ],
60 ];
61 $this->expectException(\UnexpectedValueException::class);
62 $this->expectExceptionCode(1443706103);
63 $this->subject->addData($input);
64 }
65
66 /**
67 * @test
68 */
69 public function addDataReturnsRecordTitleForLabelUserFunction()
70 {
71 $input = [
72 'tableName' => 'aTable',
73 'databaseRow' => [],
74 'processedTca' => [
75 'ctrl' => [
76 'label' => 'uid',
77 'label_userFunc' => function (&$parameters) {
78 $parameters['title'] = 'Test';
79 }
80 ],
81 'columns' => [],
82 ],
83 ];
84
85 $expected = $input;
86 $expected['recordTitle'] = 'Test';
87
88 $this->assertSame($expected, $this->subject->addData($input));
89 }
90
91 /**
92 * @test
93 */
94 public function addDataReturnsRecordTitleForFormattedLabelUserFunction()
95 {
96 $input = [
97 'tableName' => 'aTable',
98 'databaseRow' => [],
99 'isInlineChild' => true,
100 'processedTca' => [
101 'ctrl' => [
102 'label' => 'uid',
103 'formattedLabel_userFunc' => function (&$parameters) {
104 $parameters['title'] = 'Test';
105 }
106 ],
107 'columns' => [],
108 ],
109 ];
110
111 $expected = $input;
112 $expected['recordTitle'] = 'Test';
113
114 $this->assertSame($expected, $this->subject->addData($input));
115 }
116
117 /**
118 * @test
119 */
120 public function addDataReturnsRecordTitleForInlineChildWithForeignLabel()
121 {
122 $input = [
123 'tableName' => 'aTable',
124 'databaseRow' => [
125 'aField' => 'aValue',
126 ],
127 'processedTca' => [
128 'ctrl' => [
129 'label' => 'foo',
130 'label_userFunc' => function (&$parameters) {
131 $parameters['title'] = 'Value that MUST NOT be used, otherwise the code is broken.';
132 }
133 ],
134 'columns' => [
135 'aField' => [
136 'config' => [
137 'type' => 'input',
138 ],
139 ],
140 ],
141 ],
142 'isInlineChild' => true,
143 'inlineParentConfig' => [
144 'foreign_label' => 'aField',
145 ],
146 ];
147 $expected = $input;
148 $expected['recordTitle'] = 'aValue';
149 $this->assertSame($expected, $this->subject->addData($input));
150 }
151
152 /**
153 * @test
154 */
155 public function addDataOverridesRecordTitleWithFormattedLabelUserFuncForInlineChildWithForeignLabel()
156 {
157 $input = [
158 'tableName' => 'aTable',
159 'databaseRow' => [
160 'aField' => 'aValue',
161 ],
162 'processedTca' => [
163 'ctrl' => [
164 'label' => 'foo',
165 'formattedLabel_userFunc' => function (&$parameters) {
166 $parameters['title'] = 'aFormattedLabel';
167 },
168 ],
169 'columns' => [
170 'aField' => [
171 'config' => [
172 'type' => 'input',
173 ],
174 ],
175 ],
176 ],
177 'isInlineChild' => true,
178 'inlineParentConfig' => [
179 'foreign_label' => 'aField',
180 ],
181 ];
182 $expected = $input;
183 $expected['recordTitle'] = 'aFormattedLabel';
184 $this->assertSame($expected, $this->subject->addData($input));
185 }
186
187 /**
188 * @test
189 */
190 public function addDataReturnsRecordTitleForInlineChildWithSymmetricLabel()
191 {
192 $input = [
193 'tableName' => 'aTable',
194 'databaseRow' => [
195 'aField' => 'aValue',
196 ],
197 'processedTca' => [
198 'ctrl' => [
199 'label' => 'foo',
200 ],
201 'columns' => [
202 'aField' => [
203 'config' => [
204 'type' => 'input',
205 ],
206 ],
207 ],
208 ],
209 'isInlineChild' => true,
210 'inlineParentConfig' => [
211 'symmetric_label' => 'aField',
212 ],
213 'isOnSymmetricSide' => true,
214 ];
215 $expected = $input;
216 $expected['recordTitle'] = 'aValue';
217 $this->assertSame($expected, $this->subject->addData($input));
218 }
219
220 /**
221 * @test
222 */
223 public function addDataReturnsRecordTitleForUid()
224 {
225 $input = [
226 'tableName' => 'aTable',
227 'databaseRow' => [
228 'uid' => 'NEW56017ee37d10e587251374',
229 ],
230 'processedTca' => [
231 'ctrl' => [
232 'label' => 'uid'
233 ],
234 'columns' => [],
235 ]
236 ];
237
238 /** @var LanguageService|ObjectProphecy $languageService */
239 $languageService = $this->prophesize(LanguageService::class);
240 $GLOBALS['LANG'] = $languageService->reveal();
241 $languageService->sL(Argument::cetera())->willReturnArgument(0);
242
243 $expected = $input;
244 $expected['recordTitle'] = 'NEW56017ee37d10e587251374';
245 $this->assertSame($expected, $this->subject->addData($input));
246 }
247
248 /**
249 * Data provider for addDataReturnsRecordTitleForInputType
250 * Each data set is an array with the following elements:
251 * - TCA field ['config'] section
252 * - Database value for field
253 * - expected title to be generated
254 *
255 * @returns array
256 */
257 public function addDataReturnsRecordTitleForInputTypeDataProvider()
258 {
259 return [
260 'new record' => [
261 [
262 'type' => 'input',
263 ],
264 '',
265 '',
266 ],
267 'plain text input' => [
268 [
269 'type' => 'input',
270 ],
271 'aValue',
272 'aValue',
273 ],
274 'date input' => [
275 [
276 'type' => 'input',
277 'eval' => 'date'
278 ],
279 '978307261',
280 '01-01-01 (-7 days)',
281 ],
282 'date input (dbType: date)' => [
283 [
284 'type' => 'input',
285 'eval' => 'date',
286 'dbType' => 'date'
287 ],
288 '2001-01-01',
289 '01-01-01 (-7 days)',
290 ],
291 'date input (disableAgeDisplay: TRUE)' => [
292 [
293 'type' => 'input',
294 'eval' => 'date',
295 'disableAgeDisplay' => true
296 ],
297 '978307261',
298 '01-01-01',
299 ],
300 'time input' => [
301 [
302 'type' => 'input',
303 'eval' => 'time',
304 ],
305 '44100',
306 '12:15',
307 ],
308 'timesec input' => [
309 [
310 'type' => 'input',
311 'eval' => 'timesec',
312 ],
313 '44130',
314 '12:15:30',
315 ],
316 'datetime input' => [
317 [
318 'type' => 'input',
319 'eval' => 'datetime',
320 'dbType' => 'date'
321 ],
322 '978307261',
323 '01-01-01 00:01',
324 ],
325 'datetime input (dbType: datetime)' => [
326 [
327 'type' => 'input',
328 'eval' => 'datetime',
329 'dbType' => 'datetime'
330 ],
331 '2014-12-31 23:59:59',
332 '31-12-14 23:59',
333 ],
334 ];
335 }
336
337 /**
338 * @test
339 * @dataProvider addDataReturnsRecordTitleForInputTypeDataProvider
340 *
341 * @param array $fieldConfig
342 * @param string $fieldValue
343 * @param string $expectedTitle
344 */
345 public function addDataReturnsRecordTitleForInputType($fieldConfig, $fieldValue, $expectedTitle)
346 {
347 $input = [
348 'tableName' => 'aTable',
349 'databaseRow' => [
350 'uid' => '1',
351 'aField' => $fieldValue,
352 ],
353 'processedTca' => [
354 'ctrl' => [
355 'label' => 'aField'
356 ],
357 'columns' => [
358 'aField' => [
359 'config' => $fieldConfig,
360 ]
361 ],
362 ]
363 ];
364
365 /** @var LanguageService|ObjectProphecy $languageService */
366 $languageService = $this->prophesize(LanguageService::class);
367 $GLOBALS['LANG'] = $languageService->reveal();
368 $languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears')
369 ->willReturn(' min| hrs| days| yrs| min| hour| day| year');
370 $languageService->sL(Argument::cetera())->willReturnArgument(0);
371 $GLOBALS['EXEC_TIME'] = 978912061;
372
373 $expected = $input;
374 $expected['recordTitle'] = $expectedTitle;
375 $this->assertSame($expected, $this->subject->addData($input));
376 }
377
378 /**
379 * @test
380 */
381 public function addDataReturnsRecordTitleWithAlternativeLabel()
382 {
383 $input = [
384 'tableName' => 'aTable',
385 'databaseRow' => [
386 'uid' => '1',
387 'aField' => '',
388 'anotherField' => 'anotherValue',
389 ],
390 'processedTca' => [
391 'ctrl' => [
392 'label' => 'aField',
393 'label_alt' => 'anotherField',
394 ],
395 'columns' => [
396 'aField' => [
397 'config' => [
398 'type' => 'input'
399 ]
400 ],
401 'anotherField' => [
402 'config' => [
403 'type' => 'input'
404 ]
405 ]
406 ],
407 ]
408 ];
409
410 $expected = $input;
411 $expected['recordTitle'] = 'anotherValue';
412 $this->assertSame($expected, $this->subject->addData($input));
413 }
414
415 /**
416 * @test
417 */
418 public function addDataReturnsRecordTitleWithMultipleAlternativeLabels()
419 {
420 $input = [
421 'tableName' => 'aTable',
422 'databaseRow' => [
423 'uid' => '1',
424 'aField' => '',
425 'anotherField' => '',
426 'additionalField' => 'additionalValue'
427 ],
428 'processedTca' => [
429 'ctrl' => [
430 'label' => 'aField',
431 'label_alt' => 'anotherField,additionalField',
432 ],
433 'columns' => [
434 'aField' => [
435 'config' => [
436 'type' => 'input'
437 ]
438 ],
439 'anotherField' => [
440 'config' => [
441 'type' => 'input'
442 ]
443 ],
444 'additionalField' => [
445 'config' => [
446 'type' => 'input'
447 ]
448 ],
449 ],
450 ]
451 ];
452
453 $expected = $input;
454 $expected['recordTitle'] = 'additionalValue';
455 $this->assertSame($expected, $this->subject->addData($input));
456 }
457
458 /**
459 * @test
460 */
461 public function addDataReturnsRecordTitleWithForcedAlternativeLabel()
462 {
463 $input = [
464 'tableName' => 'aTable',
465 'databaseRow' => [
466 'uid' => '1',
467 'aField' => 'aField',
468 'anotherField' => 'anotherField'
469 ],
470 'processedTca' => [
471 'ctrl' => [
472 'label' => 'aField',
473 'label_alt' => 'anotherField',
474 'label_alt_force' => true,
475 ],
476 'columns' => [
477 'aField' => [
478 'config' => [
479 'type' => 'input'
480 ]
481 ],
482 'anotherField' => [
483 'config' => [
484 'type' => 'input'
485 ]
486 ],
487 ],
488 ]
489 ];
490
491 $expected = $input;
492 $expected['recordTitle'] = 'aField, anotherField';
493 $this->assertSame($expected, $this->subject->addData($input));
494 }
495
496 /**
497 * @test
498 */
499 public function addDataReturnsRecordTitleWithMultipleForcedAlternativeLabels()
500 {
501 $input = [
502 'tableName' => 'aTable',
503 'databaseRow' => [
504 'uid' => '1',
505 'aField' => 'aField',
506 'anotherField' => 'anotherField',
507 'additionalField' => 'additionalValue'
508 ],
509 'processedTca' => [
510 'ctrl' => [
511 'label' => 'aField',
512 'label_alt' => 'anotherField,additionalField',
513 'label_alt_force' => true,
514 ],
515 'columns' => [
516 'aField' => [
517 'config' => [
518 'type' => 'input'
519 ]
520 ],
521 'anotherField' => [
522 'config' => [
523 'type' => 'input'
524 ]
525 ],
526 'additionalField' => [
527 'config' => [
528 'type' => 'input'
529 ]
530 ],
531 ],
532 ]
533 ];
534
535 $expected = $input;
536 $expected['recordTitle'] = 'aField, anotherField, additionalValue';
537 $this->assertSame($expected, $this->subject->addData($input));
538 }
539
540 /**
541 * @test
542 */
543 public function addDataReturnsRecordTitleIgnoresEmptyAlternativeLabels()
544 {
545 $input = [
546 'tableName' => 'aTable',
547 'databaseRow' => [
548 'uid' => '1',
549 'aField' => 'aField',
550 'anotherField' => '',
551 'additionalField' => 'additionalValue'
552 ],
553 'processedTca' => [
554 'ctrl' => [
555 'label' => 'aField',
556 'label_alt' => 'anotherField,additionalField',
557 'label_alt_force' => true,
558 ],
559 'columns' => [
560 'aField' => [
561 'config' => [
562 'type' => 'input'
563 ]
564 ],
565 'anotherField' => [
566 'config' => [
567 'type' => 'input'
568 ]
569 ],
570 'additionalField' => [
571 'config' => [
572 'type' => 'input'
573 ]
574 ],
575 ],
576 ]
577 ];
578
579 $expected = $input;
580 $expected['recordTitle'] = 'aField, additionalValue';
581 $this->assertSame($expected, $this->subject->addData($input));
582 }
583
584 /**
585 * @test
586 */
587 public function addDataReturnsRecordTitleForRadioType()
588 {
589 $input = [
590 'tableName' => 'aTable',
591 'databaseRow' => [
592 'uid' => '1',
593 'aField' => '2',
594 ],
595 'processedTca' => [
596 'ctrl' => [
597 'label' => 'aField'
598 ],
599 'columns' => [
600 'aField' => [
601 'config' => [
602 'type' => 'radio',
603 'items' => [
604 ['foo', 1],
605 ['bar', 2],
606 ['baz', 3],
607 ]
608 ]
609 ]
610 ],
611 ]
612 ];
613
614 $expected = $input;
615 $expected['recordTitle'] = 'bar';
616 $this->assertSame($expected, $this->subject->addData($input));
617 }
618
619 /**
620 * Data provider for addDataReturnsRecordTitleForGroupType
621 * Each data set is an array with the following elements:
622 * - TCA field configuration (merged with base config)
623 * - Database value for field
624 * - expected title to be generated
625 *
626 * @returns array
627 */
628 public function addDataReturnsRecordTitleForGroupTypeDataProvider()
629 {
630 return [
631 'new record' => [
632 [
633 'internal_type' => 'db',
634 ],
635 '',
636 ''
637 ],
638 'internal_type: file' => [
639 [
640 'internal_type' => 'file',
641 ],
642 'somePath/aFile.jpg,someOtherPath/anotherFile.png',
643 'somePath/aFile.jpg, someOtherPath/anotherFile.png',
644 ],
645 'internal_type: db, single table, single record' => [
646 [
647 'internal_type' => 'db',
648 'allowed' => 'aTable'
649 ],
650 '1|aValue',
651 'aValue',
652 ],
653 'internal_type: db, single table, multiple records' => [
654 [
655 'internal_type' => 'db',
656 'allowed' => 'aTable'
657 ],
658 '1|aValue,3|anotherValue',
659 'aValue, anotherValue',
660 ],
661 'internal_type: db, multiple tables, single record' => [
662 [
663 'internal_type' => 'db',
664 'allowed' => 'aTable,anotherTable'
665 ],
666 'anotherTable_1|anotherValue',
667 'anotherValue',
668 ],
669 'internal_type: db, multiple tables, multiple records' => [
670 [
671 'internal_type' => 'db',
672 'allowed' => 'aTable,anotherTable'
673 ],
674 'anotherTable_1|anotherValue,aTable_1|aValue',
675 'aValue, anotherValue',
676 ],
677 ];
678 }
679
680 /**
681 * @test
682 * @dataProvider addDataReturnsRecordTitleForGroupTypeDataProvider
683 *
684 * @param array $fieldConfig
685 * @param string $fieldValue
686 * @param string $expectedTitle
687 */
688 public function addDataReturnsRecordTitleForGroupType($fieldConfig, $fieldValue, $expectedTitle)
689 {
690 $input = [
691 'tableName' => 'aTable',
692 'databaseRow' => [
693 'uid' => '1',
694 'aField' => $fieldValue,
695 ],
696 'processedTca' => [
697 'ctrl' => [
698 'label' => 'aField'
699 ],
700 'columns' => [
701 'aField' => [
702 'config' => array_merge(
703 [
704 'type' => 'group',
705 ],
706 $fieldConfig
707 ),
708 ]
709 ],
710 ]
711 ];
712
713 /** @var LanguageService|ObjectProphecy $languageService */
714 $languageService = $this->prophesize(LanguageService::class);
715 $GLOBALS['LANG'] = $languageService->reveal();
716 $languageService->sL(Argument::cetera())->willReturnArgument(0);
717
718 $expected = $input;
719 $expected['recordTitle'] = $expectedTitle;
720 $this->assertSame($expected, $this->subject->addData($input));
721 }
722
723 /**
724 * @test
725 */
726 public function addDataReturnsRecordTitleForGroupTypeWithInternalTypeDb()
727 {
728 $input = [
729 'tableName' => 'aTable',
730 'databaseRow' => [
731 'uid' => '1',
732 'aField' => 'aTable_1|aValue,anotherTable_2|anotherValue',
733 ],
734 'processedTca' => [
735 'ctrl' => [
736 'label' => 'aField'
737 ],
738 'columns' => [
739 'aField' => [
740 'config' => [
741 'type' => 'group',
742 'internal_type' => 'db',
743 'allowed' => 'aTable,anotherTable',
744 ]
745 ]
746 ],
747 ]
748 ];
749
750 $expected = $input;
751 $expected['recordTitle'] = 'aValue, anotherValue';
752 $this->assertSame($expected, $this->subject->addData($input));
753 }
754
755 /**
756 * @test
757 */
758 public function addDataReturnsRecordTitleForSingleCheckboxType()
759 {
760 $input = [
761 'tableName' => 'aTable',
762 'databaseRow' => [
763 'aField' => 1,
764 ],
765 'processedTca' => [
766 'ctrl' => [
767 'label' => 'aField'
768 ],
769 'columns' => [
770 'aField' => [
771 'config' => [
772 'type' => 'check',
773 ]
774 ]
775 ],
776 ]
777 ];
778
779 /** @var LanguageService|ObjectProphecy $languageService */
780 $languageService = $this->prophesize(LanguageService::class);
781 $GLOBALS['LANG'] = $languageService->reveal();
782 $languageService->sL(Argument::cetera())->willReturnArgument(0)->shouldBeCalled();
783
784 $expected = $input;
785 $expected['recordTitle'] = 'LLL:EXT:lang/Resources/Private/Language/locallang_common.xlf:yes';
786 $this->assertSame($expected, $this->subject->addData($input));
787 }
788
789 /**
790 * @test
791 */
792 public function addDataReturnsRecordTitleForArrayCheckboxType()
793 {
794 $input = [
795 'tableName' => 'aTable',
796 'databaseRow' => [
797 'aField' => '5'
798 ],
799 'processedTca' => [
800 'ctrl' => [
801 'label' => 'aField'
802 ],
803 'columns' => [
804 'aField' => [
805 'config' => [
806 'type' => 'check',
807 'items' => [
808 ['foo', ''],
809 ['bar', ''],
810 ['baz', ''],
811 ]
812 ]
813 ]
814 ],
815 ]
816 ];
817
818 $expected = $input;
819 $expected['recordTitle'] = 'foo, baz';
820 $this->assertSame($expected, $this->subject->addData($input));
821 }
822
823 /**
824 * @test
825 */
826 public function addDataReturnsEmptyRecordTitleForFlexType()
827 {
828 $input = [
829 'tableName' => 'aTable',
830 'databaseRow' => [
831 'aField' => [
832 'data' => [
833 'sDEF' => [
834 'lDEF' => [
835 'aFlexField' => [
836 'vDEF' => 'aFlexValue',
837 ]
838 ]
839 ]
840 ]
841 ]
842 ],
843 'processedTca' => [
844 'ctrl' => [
845 'label' => 'aField'
846 ],
847 'columns' => [
848 'aField' => [
849 'config' => [
850 'type' => 'flex',
851 'ds' => [
852 'sheets' => [
853 'sDEF' => [
854 'ROOT' => [
855 'type' => 'array',
856 'el' => [
857 'aFlexField' => [
858 'label' => 'Some input field',
859 'config' => [
860 'type' => 'input',
861 ],
862 ],
863 ],
864 ],
865 ],
866 ],
867 ]
868
869 ]
870 ]
871 ],
872 ]
873 ];
874
875 $expected = $input;
876 $expected['recordTitle'] = '';
877 $this->assertSame($expected, $this->subject->addData($input));
878 }
879
880 /**
881 * @test
882 */
883 public function addDataReturnsRecordTitleForSelectType()
884 {
885 $input = [
886 'tableName' => 'aTable',
887 'databaseRow' => [
888 'aField' => [
889 '1',
890 '2'
891 ]
892 ],
893 'processedTca' => [
894 'ctrl' => [
895 'label' => 'aField'
896 ],
897 'columns' => [
898 'aField' => [
899 'config' => [
900 'type' => 'select',
901 'items' => [
902 ['foo', 1, null, null],
903 ['bar', 2, null, null],
904 ['baz', 4, null, null],
905 ]
906 ]
907 ]
908 ],
909 ]
910 ];
911
912 $expected = $input;
913 $expected['recordTitle'] = 'foo, bar';
914 $this->assertSame($expected, $this->subject->addData($input));
915 }
916
917 /**
918 * @test
919 */
920 public function addDataReturnsStrippedAndTrimmedValueForTextType()
921 {
922 $input = [
923 'tableName' => 'aTable',
924 'databaseRow' => [
925 'aField' => '<p> text </p>',
926 ],
927 'processedTca' => [
928 'ctrl' => [
929 'label' => 'aField',
930 ],
931 'columns' => [
932 'aField' => [
933 'config' => [
934 'type' => 'text',
935 ],
936 ],
937 ],
938 ],
939 ];
940
941 $expected = $input;
942 $expected['recordTitle'] = 'text';
943 $this->assertSame($expected, $this->subject->addData($input));
944 }
945 }