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