[CLEANUP] Remove unused TCA option canNotCollapse
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Form / FormDataProvider / EvaluateDisplayConditionsTest.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\Prophecy\ObjectProphecy;
18 use TYPO3\CMS\Backend\Form\FormDataProvider\EvaluateDisplayConditions;
19 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Tests\UnitTestCase;
21
22 /**
23 * Test case
24 */
25 class EvaluateDisplayConditionsTest extends UnitTestCase
26 {
27 /**
28 * @var EvaluateDisplayConditions
29 */
30 protected $subject;
31
32 protected function setUp()
33 {
34 $this->subject = new EvaluateDisplayConditions();
35 }
36
37 /**
38 * @test
39 */
40 public function addDataRemovesTcaColumnsHiddenByDisplayCondition()
41 {
42 $input = [
43 'databaseRow' => [
44 'aField' => 'aField',
45 'bField' => 'bField',
46 'cField' => 1,
47 ],
48 'recordTypeValue' => 'aType',
49 'processedTca' => [
50 'types' => [
51 'aType' => [
52 'showitem' => '--palette--;aPalette;2,bField,cField'
53 ],
54 ],
55 'palettes' => [
56 '2' => array(
57 'showitem' => 'aField',
58 ),
59 ],
60 'columns' => [
61 'aField' => [
62 'displayCond' => 'FIELD:cField:=:0',
63 'config' => [
64 'type' => 'input',
65 ]
66 ],
67 'bField' => [
68 'displayCond' => 'FIELD:cField:=:1',
69 'config' => [
70 'type' => 'input',
71 ]
72 ],
73 'cField' => [
74 'config' => [
75 'type' => 'input',
76 ]
77 ]
78 ]
79 ]
80 ];
81
82 $expected = $input;
83 unset($expected['processedTca']['columns']['aField']);
84
85 $this->assertSame($expected, $this->subject->addData($input));
86 }
87
88 /**
89 * @test
90 */
91 public function addDataRemovesFlexformSheetsHiddenByDisplayCondition()
92 {
93 $input = [
94 'databaseRow' => [
95 'aField' => [
96 'data' => [
97 'sGeneral' => [
98 'lDEF' => [
99 'mmType' => [
100 'vDEF' => [
101 0 => 'video',
102 ],
103 ],
104 'mmUseHTML5' => [
105 'vDEF' => '0',
106 ],
107 ],
108 ],
109 'sVideo' => [
110 'lDEF' => [],
111 ],
112 'sAudio' => [
113 'lDEF' => []
114 ],
115 ]
116 ]
117 ],
118 'processedTca' => [
119 'columns' => [
120 'aField' => [
121 'config' => [
122 'type' => 'flex',
123 'ds' => [
124 'meta' => [],
125 'sheets' => [
126 'sGeneral' => [
127 'ROOT' => [
128 'type' => 'array',
129 'el' => [
130 'mmType' => [
131 'config' => [
132 'type' => 'select',
133 'items' => [],
134 ],
135 ],
136 'mmUseHTML5' => [
137 'displayCond' => 'FIELD:mmType:!=:audio',
138 'config' => [
139 'type' => 'check',
140 'default' => '0',
141 'items' => [],
142 ],
143 ],
144 ],
145 'sheetTitle' => 'sGeneral',
146 ],
147 ],
148 'sVideo' => [
149 'ROOT' => [
150 'type' => 'array',
151 'el' => [],
152 'sheetTitle' => 'sVideo',
153 'displayCond' => 'FIELD:sGeneral.mmType:!=:audio',
154 ],
155 ],
156 'sAudio' => [
157 'ROOT' => [
158 'type' => 'array',
159 'el' => [],
160 'sheetTitle' => 'sAudio',
161 'displayCond' => 'FIELD:sGeneral.mmType:=:audio',
162 ],
163 ],
164 ],
165 ],
166 ],
167 ],
168 ],
169 ],
170 ];
171
172 $expected = $input;
173 unset($expected['processedTca']['columns']['aField']['config']['ds']['sheets']['sAudio']);
174 $this->assertSame($expected, $this->subject->addData($input));
175 }
176
177 /**
178 * @test
179 */
180 public function addDataRemovesFlexformFieldsHiddenByDisplayCondition()
181 {
182 $input = [
183 'databaseRow' => [
184 'aField' => [
185 'data' => [
186 'sGeneral' => [
187 'lDEF' => [
188 'mmType' => [
189 'vDEF' => [
190 0 => 'audio',
191 ],
192 ],
193 'mmUseHTML5' => [
194 'vDEF' => '0',
195 ],
196 ],
197 ],
198 ]
199 ]
200 ],
201 'processedTca' => [
202 'columns' => [
203 'aField' => [
204 'config' => [
205 'type' => 'flex',
206 'ds_pointerField' => 'list_type,CType',
207 'ds' => [
208 'meta' => [],
209 'sheets' => [
210 'sGeneral' => [
211 'ROOT' => [
212 'type' => 'array',
213 'el' => [
214 'mmType' => [
215 'config' => [
216 'type' => 'select',
217 'items' => [],
218 ],
219 ],
220 'mmUseHTML5' => [
221 'displayCond' => 'FIELD:mmType:!=:audio',
222 'config' => [
223 'type' => 'check',
224 'default' => '0',
225 'items' => [],
226 ],
227 ],
228 'mmUseCurl' => [
229 'displayCond' => 'FIELD:mmType:=:audio',
230 'config' => [
231 'type' => 'check',
232 'default' => '0',
233 'items' => [],
234 ],
235 ],
236 ],
237 'sheetTitle' => 'aTitle',
238 ],
239 ],
240 'secondSheet' => [
241 'ROOT' => [
242 'type' => 'array',
243 'el' => [
244 'foo' => [
245 'config' => [
246 'type' => 'select',
247 'items' => [],
248 ],
249 ],
250 ],
251 'sheetTitle' => 'bTitle',
252 ],
253 ],
254 ],
255 ],
256 ],
257 ],
258 ],
259 ],
260 ];
261
262 $expected = $input;
263 unset($expected['processedTca']['columns']['aField']['config']['ds']['sheets']['sGeneral']['ROOT']['el']['mmUseHTML5']);
264
265 $this->assertSame($expected, $this->subject->addData($input));
266 }
267
268 /**
269 * @test
270 */
271 public function matchHideForNonAdminsReturnsTrueIfBackendUserIsAdmin()
272 {
273 $input = [
274 'databaseRow' => [],
275 'processedTca' => [
276 'columns' => [
277 'aField' => [
278 'displayCond' => 'HIDE_FOR_NON_ADMINS',
279 'config' => [
280 'type' => 'input',
281 ]
282 ],
283 ]
284 ]
285 ];
286
287 /** @var BackendUserAuthentication|ObjectProphecy backendUserProphecy */
288 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
289 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
290 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(true);
291
292 $expected = $input;
293
294 $this->assertSame($expected, $this->subject->addData($input));
295 }
296
297 /**
298 * @test
299 */
300 public function matchHideForNonAdminsReturnsFalseIfBackendUserIsNotAdmin()
301 {
302 $input = [
303 'databaseRow' => [],
304 'processedTca' => [
305 'columns' => [
306 'aField' => [
307 'displayCond' => 'HIDE_FOR_NON_ADMINS',
308 'config' => [
309 'type' => 'input',
310 ]
311 ],
312 ]
313 ]
314 ];
315
316 /** @var BackendUserAuthentication|ObjectProphecy backendUserProphecy */
317 $backendUserProphecy = $this->prophesize(BackendUserAuthentication::class);
318 $GLOBALS['BE_USER'] = $backendUserProphecy->reveal();
319 $backendUserProphecy->isAdmin()->shouldBeCalled()->willReturn(false);
320
321 $expected = $input;
322 unset($expected['processedTca']['columns']['aField']);
323 $this->assertSame($expected, $this->subject->addData($input));
324 }
325
326 /**
327 * The HIDE_L10N_SIBLINGS condition is deprecated, this test only ensures that it can be successfully parsed
328 *
329 * @test
330 */
331 public function matchHideL10NSiblingsReturnsTrue()
332 {
333 $input = [
334 'databaseRow' => [],
335 'processedTca' => [
336 'columns' => [
337 'aField' => [
338 'displayCond' => 'HIDE_L10N_SIBLINGS',
339 'config' => [
340 'type' => 'input',
341 ]
342 ],
343 ]
344 ]
345 ];
346
347 $expected = $input;
348
349 $this->assertSame($expected, $this->subject->addData($input));
350 }
351
352 /**
353 * @test
354 */
355 public function matchHideL10NSiblingsExceptAdminReturnsTrue()
356 {
357 $input = [
358 'databaseRow' => [],
359 'processedTca' => [
360 'columns' => [
361 'aField' => [
362 'displayCond' => 'HIDE_L10N_SIBLINGS:except_admin',
363 'config' => [
364 'type' => 'input',
365 ]
366 ],
367 ]
368 ]
369 ];
370
371 $expected = $input;
372
373 $this->assertSame($expected, $this->subject->addData($input));
374 }
375
376 /**
377 * Returns data sets for the test matchConditionStrings
378 * Each data set is an array with the following elements:
379 * - the condition string
380 * - the current record
381 * - the expected result
382 *
383 * @return array
384 */
385 public function conditionStringDataProvider()
386 {
387 return [
388 'Invalid condition string' => [
389 'xINVALIDx:',
390 [],
391 false,
392 ],
393 'Not loaded extension compares to loaded as FALSE' => [
394 'EXT:neverloadedext:LOADED:TRUE',
395 [],
396 false,
397 ],
398 'Not loaded extension compares to not loaded as TRUE' => [
399 'EXT:neverloadedext:LOADED:FALSE',
400 [],
401 true,
402 ],
403 'Loaded extension compares to TRUE' => [
404 'EXT:backend:LOADED:TRUE',
405 [],
406 true,
407 ],
408 'Loaded extension compares to FALSE' => [
409 'EXT:backend:LOADED:FALSE',
410 [],
411 false,
412 ],
413 'Field is not greater zero if not given' => [
414 'FIELD:uid:>:0',
415 [],
416 false,
417 ],
418 'Field is not equal 0 if not given' => [
419 'FIELD:uid:=:0',
420 [],
421 false,
422 ],
423 'Field value string comparison' => [
424 'FIELD:foo:=:bar',
425 ['foo' => 'bar'],
426 true,
427 ],
428 'Field value string comparison against list' => [
429 'FIELD:foo:IN:bar,baz',
430 ['foo' => 'baz'],
431 true,
432 ],
433 'Field value comparison of 1 against multi-value field of 5 returns true' => [
434 'FIELD:content:BIT:1',
435 ['content' => '5'],
436 true
437 ],
438 'Field value comparison of 2 against multi-value field of 5 returns false' => [
439 'FIELD:content:BIT:2',
440 ['content' => '5'],
441 false
442 ],
443 'Field value of 5 negated comparison against multi-value field of 5 returns false' => [
444 'FIELD:content:!BIT:5',
445 ['content' => '5'],
446 false
447 ],
448 'Field value comparison for required value is false for different value' => [
449 'FIELD:foo:REQ:FALSE',
450 ['foo' => 'bar'],
451 false,
452 ],
453 'Field value string not equal comparison' => [
454 'FIELD:foo:!=:baz',
455 ['foo' => 'bar'],
456 true,
457 ],
458 'Field value string not equal comparison against list' => [
459 'FIELD:foo:!IN:bar,baz',
460 ['foo' => 'foo'],
461 true,
462 ],
463 'Field value in range' => [
464 'FIELD:uid:-:3-42',
465 ['uid' => '23'],
466 true,
467 ],
468 'Field value greater than' => [
469 'FIELD:uid:>=:42',
470 ['uid' => '23'],
471 false,
472 ],
473 'Field is value for default language without flexform' => [
474 'HIDE_L10N_SIBLINGS',
475 [],
476 true,
477 ],
478 'New is TRUE for new comparison with TRUE' => [
479 'REC:NEW:TRUE',
480 ['uid' => null],
481 true,
482 ],
483 'New is FALSE for new comparison with FALSE' => [
484 'REC:NEW:FALSE',
485 ['uid' => null],
486 false,
487 ],
488 'New is FALSE for not new element' => [
489 'REC:NEW:TRUE',
490 ['uid' => 42],
491 false,
492 ],
493 'New is TRUE for not new element compared to FALSE' => [
494 'REC:NEW:FALSE',
495 ['uid' => 42],
496 true,
497 ],
498 'Version is TRUE for versioned row' => [
499 'VERSION:IS:TRUE',
500 [
501 'uid' => 42,
502 'pid' => -1
503 ],
504 true,
505 ],
506 'Version is TRUE for not versioned row compared with FALSE' => [
507 'VERSION:IS:FALSE',
508 [
509 'uid' => 42,
510 'pid' => 1
511 ],
512 true,
513 ],
514 'Version is TRUE for NULL row compared with TRUE' => [
515 'VERSION:IS:TRUE',
516 [
517 'uid' => null,
518 'pid' => null,
519 ],
520 false,
521 ],
522 'Multiple conditions with AND compare to TRUE if all are OK' => [
523 [
524 'AND' => [
525 'FIELD:testField:>:9',
526 'FIELD:testField:<:11',
527 ],
528 ],
529 [
530 'testField' => 10
531 ],
532 true,
533 ],
534 'Multiple conditions with AND compare to FALSE if one fails' => [
535 [
536 'AND' => [
537 'FIELD:testField:>:9',
538 'FIELD:testField:<:11',
539 ]
540 ],
541 [
542 'testField' => 99
543 ],
544 false,
545 ],
546 'Multiple conditions with OR compare to TRUE if one is OK' => [
547 [
548 'OR' => [
549 'FIELD:testField:<:9',
550 'FIELD:testField:<:11',
551 ],
552 ],
553 [
554 'testField' => 10
555 ],
556 true,
557 ],
558 'Multiple conditions with OR compare to FALSE is all fail' => [
559 [
560 'OR' => [
561 'FIELD:testField:<:9',
562 'FIELD:testField:<:11',
563 ],
564 ],
565 [
566 'testField' => 99
567 ],
568 false,
569 ],
570 'Multiple conditions without operator due to misconfiguration compare to TRUE' => [
571 [
572 '' => [
573 'FIELD:testField:<:9',
574 'FIELD:testField:>:11',
575 ]
576 ],
577 [
578 'testField' => 99
579 ],
580 true,
581 ],
582 'Multiple nested conditions evaluate to TRUE' => [
583 [
584 'AND' => [
585 'FIELD:testField:>:9',
586 'OR' => [
587 'FIELD:testField:<:100',
588 'FIELD:testField:>:-100',
589 ],
590 ],
591 ],
592 [
593 'testField' => 10
594 ],
595 true,
596 ],
597 'Multiple nested conditions evaluate to FALSE' => [
598 [
599 'AND' => [
600 'FIELD:testField:>:9',
601 'OR' => [
602 'FIELD:testField:<:100',
603 'FIELD:testField:>:-100',
604 ],
605 ],
606 ],
607 [
608 'testField' => -999
609 ],
610 false,
611 ],
612 ];
613 }
614
615 /**
616 * @param string $condition
617 * @param array $record
618 * @param string $expectedResult
619 * @dataProvider conditionStringDataProvider
620 * @test
621 */
622 public function matchConditionStrings($condition, array $record, $expectedResult)
623 {
624 $input = [
625 'databaseRow' => $record,
626 'processedTca' => [
627 'columns' => [
628 'testField' => [
629 'displayCond' => $condition,
630 'config' => [
631 'type' => 'input',
632 ]
633 ],
634 ]
635 ]
636 ];
637
638 $expected = $input;
639 if (!$expectedResult) {
640 unset($expected['processedTca']['columns']['testField']);
641 }
642 $this->assertSame($expected, $this->subject->addData($input));
643 }
644
645 /**
646 * @param string $condition
647 * @param array $record
648 * @param string $expectedResult
649 *
650 * @dataProvider conditionStringDataProvider
651 * @test
652 */
653 public function matchConditionStringsWithRecordTestFieldBeingArray($condition, array $record, $expectedResult)
654 {
655 $input = [
656 'processedTca' => [
657 'columns' => [
658 'testField' => [
659 'displayCond' => $condition,
660 'config' => [
661 'type' => 'input',
662 ],
663 ],
664 ],
665 ],
666 ];
667 $input['databaseRow'] = $record ?: ['testField' => ['key' => $record['testField']]];
668
669 $expected = $input;
670 if (!$expectedResult) {
671 unset($expected['processedTca']['columns']['testField']);
672 }
673 $this->assertSame($expected, $this->subject->addData($input));
674 }
675
676 /**
677 * Returns data sets for the test matchConditionStrings
678 * Each data set is an array with the following elements:
679 * - the condition string
680 * - the current record
681 * - the expected result
682 *
683 * @return array
684 */
685 public function flexformConditionStringDataProvider()
686 {
687 return [
688 'Flexform value invalid comparison' => [
689 'FIELD:foo:=:bar',
690 [
691 'foo' => 'bar',
692 'testField' => [
693 'data' => [
694 'sDEF' => [
695 'lDEF' => [],
696 ],
697 ],
698 ],
699 ],
700 false,
701 ],
702 'Flexform value valid comparison' => [
703 'FIELD:parentRec.foo:=:bar',
704 [
705 'foo' => 'bar',
706 'testField' => [
707 'data' => [
708 'sDEF' => [
709 'lDEF' => [],
710 ],
711 ],
712 ],
713 ],
714 true,
715 ],
716 ];
717 }
718
719 /**
720 * @param string $condition
721 * @param array $record
722 * @param string $expectedResult
723 * @dataProvider flexformConditionStringDataProvider
724 * @test
725 */
726 public function matchFlexformConditionStrings($condition, array $record, $expectedResult)
727 {
728 $input = [
729 'databaseRow' => $record,
730 'processedTca' => [
731 'columns' => [
732 'testField' => [
733 'config' => [
734 'type' => 'flex',
735 'ds' => [
736 'meta' => [],
737 'sheets' => [
738 'sDEF' => [
739 'ROOT' => [
740 'type' => 'array',
741 'el' => [
742 'flexTestField' => [
743 'displayCond' => $condition,
744 'config' => [
745 'type' => 'input',
746 ],
747 ],
748 ],
749 ],
750 ],
751 ]
752 ],
753 ],
754 ],
755 ],
756 ],
757 ];
758
759 $expected = $input;
760 if (!$expectedResult) {
761 unset($expected['processedTca']['columns']['testField']['config']['ds']['sheets']['sDEF']['ROOT']['el']['flexTestField']);
762 }
763 $this->assertSame($expected, $this->subject->addData($input));
764 }
765
766 /**
767 * @test
768 */
769 public function matchFlexformSheetConditionStringsForFieldsWithDotInName()
770 {
771 $input = [
772 'databaseRow' => [
773 'foo' => 'bar',
774 'testField' => [
775 'data' => [
776 'sDEF' => [
777 'lDEF' => [
778 'flex.TestField' => [
779 'vDEF' => [
780 0 => 0,
781 ],
782 ],
783 ],
784 ],
785 ],
786 ],
787 ],
788 'processedTca' => [
789 'columns' => [
790 'testField' => [
791 'config' => [
792 'type' => 'flex',
793 'ds' => [
794 'meta' => [],
795 'sheets' => [
796 'sDEF' => [
797 'ROOT' => [
798 'type' => 'array',
799 'el' => [
800 'flex.TestField' => [
801 'config' => [
802 'type' => 'input',
803 ],
804 ],
805 ],
806 ],
807 ],
808 'sTest' => [
809 'ROOT' => [
810 'type' => 'array',
811 'el' => [],
812 'sheetTitle' => 'sVideo',
813 'displayCond' => 'FIELD:sDEF.flex.TestField:!=:0',
814 ],
815 ],
816 ]
817 ],
818 ],
819 ],
820 ],
821 ],
822 ];
823
824 $expected = $input;
825 unset($expected['processedTca']['columns']['testField']['config']['ds']['sheets']['sTest']);
826 $this->assertSame($expected, $this->subject->addData($input));
827 }
828 }