e3e34cc8d0cbcbb3b4a81342b05367e3ee8dd753
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Tests / Unit / Utility / BackendUtilityTest.php
1 <?php
2 namespace TYPO3\CMS\Backend\Tests\Unit\Utility;
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\Configuration\TsConfigParser;
20 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\LabelFromItemListMergedReturnsCorrectFieldsFixture;
21 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ProcessedValueForGroupWithMultipleAllowedTablesFixture;
22 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ProcessedValueForGroupWithOneAllowedTableFixture;
23 use TYPO3\CMS\Backend\Tests\Unit\Utility\Fixtures\ProcessedValueForSelectWithMMRelationFixture;
24 use TYPO3\CMS\Backend\Utility\BackendUtility;
25 use TYPO3\CMS\Core\Cache\CacheManager;
26 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
27 use TYPO3\CMS\Core\Database\Connection;
28 use TYPO3\CMS\Core\Database\ConnectionPool;
29 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
30 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
31 use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
32 use TYPO3\CMS\Core\Database\RelationHandler;
33 use TYPO3\CMS\Core\Localization\LanguageService;
34 use TYPO3\CMS\Core\Utility\GeneralUtility;
35 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
36
37 /**
38 * Test case
39 */
40 class BackendUtilityTest extends UnitTestCase
41 {
42 /**
43 * @var bool
44 */
45 protected $resetSingletonInstances = true;
46
47 ///////////////////////////////////////
48 // Tests concerning calcAge
49 ///////////////////////////////////////
50 /**
51 * Data provider for calcAge function
52 *
53 * @return array
54 */
55 public function calcAgeDataProvider()
56 {
57 return [
58 'Single year' => [
59 'seconds' => 60 * 60 * 24 * 365,
60 'expectedLabel' => '1 year'
61 ],
62 'Plural years' => [
63 'seconds' => 60 * 60 * 24 * 365 * 2,
64 'expectedLabel' => '2 yrs'
65 ],
66 'Single negative year' => [
67 'seconds' => 60 * 60 * 24 * 365 * -1,
68 'expectedLabel' => '-1 year'
69 ],
70 'Plural negative years' => [
71 'seconds' => 60 * 60 * 24 * 365 * 2 * -1,
72 'expectedLabel' => '-2 yrs'
73 ],
74 'Single day' => [
75 'seconds' => 60 * 60 * 24,
76 'expectedLabel' => '1 day'
77 ],
78 'Plural days' => [
79 'seconds' => 60 * 60 * 24 * 2,
80 'expectedLabel' => '2 days'
81 ],
82 'Single negative day' => [
83 'seconds' => 60 * 60 * 24 * -1,
84 'expectedLabel' => '-1 day'
85 ],
86 'Plural negative days' => [
87 'seconds' => 60 * 60 * 24 * 2 * -1,
88 'expectedLabel' => '-2 days'
89 ],
90 'Single hour' => [
91 'seconds' => 60 * 60,
92 'expectedLabel' => '1 hour'
93 ],
94 'Plural hours' => [
95 'seconds' => 60 * 60 * 2,
96 'expectedLabel' => '2 hrs'
97 ],
98 'Single negative hour' => [
99 'seconds' => 60 * 60 * -1,
100 'expectedLabel' => '-1 hour'
101 ],
102 'Plural negative hours' => [
103 'seconds' => 60 * 60 * 2 * -1,
104 'expectedLabel' => '-2 hrs'
105 ],
106 'Single minute' => [
107 'seconds' => 60,
108 'expectedLabel' => '1 min'
109 ],
110 'Plural minutes' => [
111 'seconds' => 60 * 2,
112 'expectedLabel' => '2 min'
113 ],
114 'Single negative minute' => [
115 'seconds' => 60 * -1,
116 'expectedLabel' => '-1 min'
117 ],
118 'Plural negative minutes' => [
119 'seconds' => 60 * 2 * -1,
120 'expectedLabel' => '-2 min'
121 ],
122 'Zero seconds' => [
123 'seconds' => 0,
124 'expectedLabel' => '0 min'
125 ]
126 ];
127 }
128
129 /**
130 * @test
131 * @dataProvider calcAgeDataProvider
132 *
133 * @param int $seconds
134 * @param string $expectedLabel
135 */
136 public function calcAgeReturnsExpectedValues($seconds, $expectedLabel)
137 {
138 $this->assertSame($expectedLabel, BackendUtility::calcAge($seconds));
139 }
140
141 ///////////////////////////////////////
142 // Tests concerning getProcessedValue
143 ///////////////////////////////////////
144 /**
145 * @test
146 * @see http://forge.typo3.org/issues/20994
147 */
148 public function getProcessedValueForZeroStringIsZero()
149 {
150 $GLOBALS['TCA'] = [
151 'tt_content' => [
152 'columns' => [
153 'header' => [
154 'config' => [
155 'type' => 'input',
156 ],
157 ],
158 ],
159 ],
160 ];
161 $GLOBALS['LANG'] = [];
162 $this->assertEquals('0', BackendUtility::getProcessedValue('tt_content', 'header', '0'));
163 }
164
165 /**
166 * @test
167 */
168 public function getProcessedValueForGroup()
169 {
170 $GLOBALS['TCA'] = [
171 'tt_content' => [
172 'columns' => [
173 'multimedia' => [
174 'config' => [
175 'type' => 'group',
176 ],
177 ],
178 ],
179 ],
180 ];
181 $GLOBALS['LANG'] = [];
182 $this->assertSame('1, 2', BackendUtility::getProcessedValue('tt_content', 'multimedia', '1,2'));
183 }
184
185 /**
186 * @test
187 */
188 public function getProcessedValueForGroupWithOneAllowedTable()
189 {
190 $GLOBALS['TCA'] = [
191 'tt_content' => [
192 'columns' => [
193 'pages' => [
194 'config' => [
195 'type' => 'group',
196 'allowed' => 'pages',
197 'internal_type' => 'db',
198 'maxitems' => 22,
199 'minitems' => 0,
200 'size' => 3,
201 ],
202 ],
203 ],
204 ],
205 ];
206 $GLOBALS['LANG'] = [];
207 $this->assertSame('Page 1, Page 2', ProcessedValueForGroupWithOneAllowedTableFixture::getProcessedValue('tt_content', 'pages', '1,2'));
208 }
209
210 /**
211 * @test
212 */
213 public function getProcessedValueForGroupWithMultipleAllowedTables()
214 {
215 $GLOBALS['TCA'] = [
216 'index_config' => [
217 'columns' => [
218 'indexcfgs' => [
219 'config' => [
220 'type' => 'group',
221 'internal_type' => 'db',
222 'allowed' => 'index_config,pages',
223 'size' => 5,
224 ],
225 ],
226 ],
227 ],
228 ];
229 $GLOBALS['LANG'] = [];
230 $this->assertSame('Page 1, Configuration 2', ProcessedValueForGroupWithMultipleAllowedTablesFixture::getProcessedValue('index_config', 'indexcfgs', 'pages_1,index_config_2'));
231 }
232
233 /**
234 * Prepare a mock database setup for a Doctrine connection
235 * and return an array of all prophets to set expectations upon.
236 *
237 * @param string $tableName
238 * @return array
239 */
240 protected function mockDatabaseConnection($tableName = 'sys_category')
241 {
242 $connectionProphet = $this->prophesize(Connection::class);
243 $connectionProphet->quote(Argument::cetera())->will(function ($arguments) {
244 return "'" . $arguments[0] . "'";
245 });
246 $connectionProphet->quoteIdentifier(Argument::cetera())->will(function ($arguments) {
247 return '`' . $arguments[0] . '`';
248 });
249
250 $restrictionProphet = $this->prophesize(DefaultRestrictionContainer::class);
251 $restrictionProphet->removeAll()->willReturn($restrictionProphet->reveal());
252 $restrictionProphet->add(Argument::cetera())->willReturn($restrictionProphet->reveal());
253
254 $queryBuilderProphet = $this->prophesize(QueryBuilder::class);
255 $queryBuilderProphet->expr()->willReturn(
256 GeneralUtility::makeInstance(ExpressionBuilder::class, $connectionProphet->reveal())
257 );
258 $queryBuilderProphet->getRestrictions()->willReturn($restrictionProphet->reveal());
259 $queryBuilderProphet->quoteIdentifier(Argument::cetera())->will(function ($arguments) {
260 return '`' . $arguments[0] . '`';
261 });
262
263 $connectionPoolProphet = $this->prophesize(ConnectionPool::class);
264 $connectionPoolProphet->getConnectionForTable($tableName)
265 ->willReturn($connectionProphet->reveal());
266 $connectionPoolProphet->getQueryBuilderForTable($tableName)
267 ->shouldBeCalled()
268 ->willReturn($queryBuilderProphet->reveal());
269
270 return [$queryBuilderProphet, $connectionPoolProphet, $connectionProphet, $restrictionProphet];
271 }
272
273 /**
274 * @test
275 */
276 public function getProcessedValueForSelectWithMMRelation()
277 {
278 /** @var RelationHandler|ObjectProphecy $relationHandlerProphet */
279 $relationHandlerProphet = $this->prophesize(RelationHandler::class);
280 $relationHandlerProphet->start(Argument::cetera())->shouldBeCalled();
281
282 $relationHandlerInstance = $relationHandlerProphet->reveal();
283 $relationHandlerInstance->tableArray['sys_category'] = [1, 2];
284
285 list($queryBuilderProphet, $connectionPoolProphet) = $this->mockDatabaseConnection('sys_category');
286 $statementProphet = $this->prophesize(\Doctrine\DBAL\Driver\Statement::class);
287 $statementProphet->fetch()->shouldBeCalled()->willReturn(
288 [
289 'uid' => 1,
290 'title' => 'Category 1',
291 ],
292 [
293 'uid' => 2,
294 'title' => 'Category 2',
295 ],
296 false
297 );
298
299 /** @var QueryBuilder|ObjectProphecy $queryBuilderProphet */
300 $queryBuilderProphet->select('uid', 'sys_category.title')->willReturn($queryBuilderProphet->reveal());
301 $queryBuilderProphet->from('sys_category')->willReturn($queryBuilderProphet->reveal());
302 $queryBuilderProphet->where('`uid` IN (:dcValue1)')->willReturn($queryBuilderProphet->reveal());
303 $queryBuilderProphet->createNamedParameter([1, 2], Connection::PARAM_INT_ARRAY)->willReturn(':dcValue1');
304 $queryBuilderProphet->execute()->willReturn($statementProphet->reveal());
305
306 GeneralUtility::addInstance(RelationHandler::class, $relationHandlerInstance);
307 GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphet->reveal());
308
309 $GLOBALS['TCA'] = [
310 'pages' => [
311 'columns' => [
312 'categories' => [
313 'config' => [
314 'type' => 'select',
315 'foreign_table' => 'sys_category',
316 'MM' => 'sys_category_record_mm',
317 'MM_match_fields' => [
318 'fieldname' => 'categories',
319 'tablesnames' => 'pages',
320 ],
321 'MM_opposite_field' => 'items',
322 ],
323 ],
324 ],
325 ],
326 'sys_category' => [
327 'ctrl' => ['label' => 'title'],
328 'columns' => [
329 'items' => [
330 'config' => [
331 'type' => 'group',
332 'internal_type' => 'db',
333 'allowed' => '*',
334 'MM' => 'sys_category_record_mm',
335 'MM_oppositeUsage' => [],
336 ]
337 ]
338 ],
339 ],
340 ];
341 $GLOBALS['LANG'] = [];
342
343 $this->assertSame(
344 'Category 1; Category 2',
345 ProcessedValueForSelectWithMMRelationFixture::getProcessedValue(
346 'pages',
347 'categories',
348 '2',
349 0,
350 false,
351 false,
352 1
353 )
354 );
355 }
356
357 /**
358 * @test
359 */
360 public function getProcessedValueDisplaysAgeForDateInputFieldsIfSettingAbsent()
361 {
362 /** @var ObjectProphecy $languageServiceProphecy */
363 $languageServiceProphecy = $this->prophesize(LanguageService::class);
364 $languageServiceProphecy->sL(Argument::cetera())->willReturn(' min| hrs| days| yrs| min| hour| day| year');
365 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
366
367 $GLOBALS['EXEC_TIME'] = mktime(0, 0, 0, 8, 30, 2015);
368
369 $GLOBALS['TCA'] = [
370 'tt_content' => [
371 'columns' => [
372 'date' => [
373 'config' => [
374 'type' => 'input',
375 'eval' => 'date',
376 ],
377 ],
378 ],
379 ],
380 ];
381 $this->assertSame('28-08-15 (-2 days)', BackendUtility::getProcessedValue('tt_content', 'date', mktime(0, 0, 0, 8, 28, 2015)));
382 }
383
384 /**
385 * @return array
386 */
387 public function inputTypeDateDisplayOptions()
388 {
389 return [
390 'typeSafe Setting' => [
391 true,
392 '28-08-15',
393 ],
394 'non typesafe setting' => [
395 1,
396 '28-08-15',
397 ],
398 'setting disabled typesafe' => [
399 false,
400 '28-08-15 (-2 days)',
401 ],
402 'setting disabled not typesafe' => [
403 0,
404 '28-08-15 (-2 days)',
405 ],
406 ];
407 }
408
409 /**
410 * @test
411 *
412 * @dataProvider inputTypeDateDisplayOptions
413 *
414 * @param string $input
415 * @param string $expected
416 */
417 public function getProcessedValueHandlesAgeDisplayCorrectly($input, $expected)
418 {
419 /** @var ObjectProphecy $languageServiceProphecy */
420 $languageServiceProphecy = $this->prophesize(LanguageService::class);
421 $languageServiceProphecy->sL(Argument::cetera())->willReturn(' min| hrs| days| yrs| min| hour| day| year');
422 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
423
424 $GLOBALS['EXEC_TIME'] = mktime(0, 0, 0, 8, 30, 2015);
425
426 $GLOBALS['TCA'] = [
427 'tt_content' => [
428 'columns' => [
429 'date' => [
430 'config' => [
431 'type' => 'input',
432 'eval' => 'date',
433 'disableAgeDisplay' => $input,
434 ],
435 ],
436 ],
437 ],
438 ];
439 $this->assertSame($expected, BackendUtility::getProcessedValue('tt_content', 'date', mktime(0, 0, 0, 8, 28, 2015)));
440 }
441
442 /**
443 * @test
444 */
445 public function getProcessedValueForCheckWithSingleItem()
446 {
447 $GLOBALS['TCA'] = [
448 'tt_content' => [
449 'columns' => [
450 'hide' => [
451 'config' => [
452 'type' => 'check',
453 'items' => [
454 [
455 0 => '',
456 1 => '',
457 ]
458 ]
459 ]
460 ]
461 ]
462 ]
463 ];
464 $languageServiceProphecy = $this->prophesize(\TYPO3\CMS\Core\Localization\LanguageService::class);
465 $languageServiceProphecy->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:yes')->willReturn('Yes');
466 $languageServiceProphecy->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:no')->willReturn('No');
467 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
468 $this->assertSame('Yes', BackendUtility::getProcessedValue('tt_content', 'hide', 1));
469 }
470
471 /**
472 * @test
473 */
474 public function getProcessedValueForCheckWithSingleItemInvertStateDisplay()
475 {
476 $GLOBALS['TCA'] = [
477 'tt_content' => [
478 'columns' => [
479 'hide' => [
480 'config' => [
481 'type' => 'check',
482 'items' => [
483 [
484 0 => '',
485 1 => '',
486 'invertStateDisplay' => true,
487 ]
488 ]
489 ]
490 ]
491 ]
492 ]
493 ];
494 $languageServiceProphecy = $this->prophesize(\TYPO3\CMS\Core\Localization\LanguageService::class);
495 $languageServiceProphecy->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:yes')->willReturn('Yes');
496 $languageServiceProphecy->sL('LLL:EXT:core/Resources/Private/Language/locallang_common.xlf:no')->willReturn('No');
497 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
498 $this->assertSame('No', BackendUtility::getProcessedValue('tt_content', 'hide', 1));
499 }
500
501 /**
502 * Tests concerning getCommonSelectFields
503 */
504
505 /**
506 * Data provider for getCommonSelectFieldsReturnsCorrectFields
507 *
508 * @return array The test data with $table, $prefix, $presetFields, $tca, $expectedFields
509 */
510 public function getCommonSelectFieldsReturnsCorrectFieldsDataProvider()
511 {
512 return [
513 'only uid' => [
514 'table' => 'test_table',
515 'prefix' => '',
516 'presetFields' => [],
517 'tca' => [],
518 'expectedFields' => 'uid'
519 ],
520 'label set' => [
521 'table' => 'test_table',
522 'prefix' => '',
523 'presetFields' => [],
524 'tca' => [
525 'ctrl' => [
526 'label' => 'label'
527 ]
528 ],
529 'expectedFields' => 'uid,label'
530 ],
531 'label_alt set' => [
532 'table' => 'test_table',
533 'prefix' => '',
534 'presetFields' => [],
535 'tca' => [
536 'ctrl' => [
537 'label_alt' => 'label,label2'
538 ]
539 ],
540 'expectedFields' => 'uid,label,label2'
541 ],
542 'versioningWS set' => [
543 'table' => 'test_table',
544 'prefix' => '',
545 'presetFields' => [],
546 'tca' => [
547 'ctrl' => [
548 'versioningWS' => true
549 ]
550 ],
551 'expectedFields' => 'uid,t3ver_id,t3ver_state,t3ver_wsid,t3ver_count'
552 ],
553 'selicon_field set' => [
554 'table' => 'test_table',
555 'prefix' => '',
556 'presetFields' => [],
557 'tca' => [
558 'ctrl' => [
559 'selicon_field' => 'field'
560 ]
561 ],
562 'expectedFields' => 'uid,field'
563 ],
564 'typeicon_column set' => [
565 'table' => 'test_table',
566 'prefix' => '',
567 'presetFields' => [],
568 'tca' => [
569 'ctrl' => [
570 'typeicon_column' => 'field'
571 ]
572 ],
573 'expectedFields' => 'uid,field'
574 ],
575 'enablecolumns set' => [
576 'table' => 'test_table',
577 'prefix' => '',
578 'presetFields' => [],
579 'tca' => [
580 'ctrl' => [
581 'enablecolumns' => [
582 'disabled' => 'hidden',
583 'starttime' => 'start',
584 'endtime' => 'stop',
585 'fe_group' => 'groups'
586 ]
587 ]
588 ],
589 'expectedFields' => 'uid,hidden,start,stop,groups'
590 ],
591 'label set to uid' => [
592 'table' => 'test_table',
593 'prefix' => '',
594 'presetFields' => [],
595 'tca' => [
596 'ctrl' => [
597 'label' => 'uid'
598 ]
599 ],
600 'expectedFields' => 'uid'
601 ]
602 ];
603 }
604
605 /**
606 * @test
607 * @dataProvider getCommonSelectFieldsReturnsCorrectFieldsDataProvider
608 *
609 * @param string $table
610 * @param string $prefix
611 * @param array $presetFields
612 * @param array $tca
613 * @param string $expectedFields
614 */
615 public function getCommonSelectFieldsReturnsCorrectFields($table, $prefix = '', array $presetFields, array $tca, $expectedFields = '')
616 {
617 $GLOBALS['TCA'][$table] = $tca;
618 $selectFields = BackendUtility::getCommonSelectFields($table, $prefix, $presetFields);
619 $this->assertEquals($selectFields, $expectedFields);
620 }
621
622 /**
623 * Tests concerning getLabelFromItemlist
624 */
625
626 /**
627 * Data provider for getLabelFromItemlistReturnsCorrectFields
628 *
629 * @return array The test data with $table, $col, $key, $expectedLabel
630 */
631 public function getLabelFromItemlistReturnsCorrectFieldsDataProvider()
632 {
633 return [
634 'item set' => [
635 'table' => 'tt_content',
636 'col' => 'menu_type',
637 'key' => '1',
638 'tca' => [
639 'columns' => [
640 'menu_type' => [
641 'config' => [
642 'items' => [
643 ['Item 1', '0'],
644 ['Item 2', '1'],
645 ['Item 3', '3']
646 ]
647 ]
648 ]
649 ]
650 ],
651 'expectedLabel' => 'Item 2'
652 ],
653 'item set twice' => [
654 'table' => 'tt_content',
655 'col' => 'menu_type',
656 'key' => '1',
657 'tca' => [
658 'columns' => [
659 'menu_type' => [
660 'config' => [
661 'items' => [
662 ['Item 1', '0'],
663 ['Item 2a', '1'],
664 ['Item 2b', '1'],
665 ['Item 3', '3']
666 ]
667 ]
668 ]
669 ]
670 ],
671 'expectedLabel' => 'Item 2a'
672 ],
673 'item not found' => [
674 'table' => 'tt_content',
675 'col' => 'menu_type',
676 'key' => '5',
677 'tca' => [
678 'columns' => [
679 'menu_type' => [
680 'config' => [
681 'items' => [
682 ['Item 1', '0'],
683 ['Item 2', '1'],
684 ['Item 3', '2']
685 ]
686 ]
687 ]
688 ]
689 ],
690 'expectedLabel' => null
691 ]
692 ];
693 }
694
695 /**
696 * @test
697 * @dataProvider getLabelFromItemlistReturnsCorrectFieldsDataProvider
698 *
699 * @param string $table
700 * @param string $col
701 * @param string $key
702 * @param array $tca
703 * @param string $expectedLabel
704 */
705 public function getLabelFromItemlistReturnsCorrectFields($table, $col = '', $key = '', array $tca, $expectedLabel = '')
706 {
707 $GLOBALS['TCA'][$table] = $tca;
708 $label = BackendUtility::getLabelFromItemlist($table, $col, $key);
709 $this->assertEquals($label, $expectedLabel);
710 }
711
712 /**
713 * Tests concerning getLabelFromItemListMerged
714 */
715
716 /**
717 * Data provider for getLabelFromItemListMerged
718 *
719 * @return array The test data with $pageId, $table, $column, $key, $expectedLabel
720 */
721 public function getLabelFromItemListMergedReturnsCorrectFieldsDataProvider()
722 {
723 return [
724 'no field found' => [
725 'pageId' => '123',
726 'table' => 'tt_content',
727 'col' => 'menu_type',
728 'key' => '10',
729 'tca' => [
730 'columns' => [
731 'menu_type' => [
732 'config' => [
733 'items' => [
734 ['Item 1', '0'],
735 ['Item 2', '1'],
736 ['Item 3', '3']
737 ]
738 ]
739 ]
740 ]
741 ],
742 'expectedLabel' => ''
743 ],
744 'no tsconfig set' => [
745 'pageId' => '123',
746 'table' => 'tt_content',
747 'col' => 'menu_type',
748 'key' => '1',
749 'tca' => [
750 'columns' => [
751 'menu_type' => [
752 'config' => [
753 'items' => [
754 ['Item 1', '0'],
755 ['Item 2', '1'],
756 ['Item 3', '3']
757 ]
758 ]
759 ]
760 ]
761 ],
762 'expectedLabel' => 'Item 2'
763 ]
764 ];
765 }
766
767 /**
768 * @test
769 * @dataProvider getLabelFromItemListMergedReturnsCorrectFieldsDataProvider
770 *
771 * @param int $pageId
772 * @param string $table
773 * @param string $column
774 * @param string $key
775 * @param array $tca
776 * @param string $expectedLabel
777 */
778 public function getLabelFromItemListMergedReturnsCorrectFields($pageId, $table, $column = '', $key = '', array $tca, $expectedLabel = '')
779 {
780 $GLOBALS['TCA'][$table] = $tca;
781
782 $this->assertEquals($expectedLabel, LabelFromItemListMergedReturnsCorrectFieldsFixture::getLabelFromItemListMerged($pageId, $table, $column, $key));
783 }
784
785 /**
786 * Tests concerning getFuncCheck
787 */
788
789 /**
790 * @test
791 */
792 public function getFuncCheckReturnsInputTagWithValueAttribute()
793 {
794 $this->assertStringMatchesFormat('<input %Svalue="1"%S/>', BackendUtility::getFuncCheck('params', 'test', true));
795 }
796
797 /*
798 * Tests concerning getLabelsFromItemsList
799 */
800
801 /**
802 * @return array
803 */
804 public function getLabelsFromItemsListDataProvider()
805 {
806 return [
807 'return value if found' => [
808 'foobar', // table
809 'someColumn', // col
810 'foo, bar', // keyList
811 [ // TCA
812 'columns' => [
813 'someColumn' => [
814 'config' => [
815 'items' => [
816 '0' => ['aFooLabel', 'foo'],
817 '1' => ['aBarLabel', 'bar']
818 ]
819 ]
820 ]
821 ]
822 ],
823 [], // page TSconfig
824 'aFooLabel, aBarLabel' // expected
825 ],
826 'page TSconfig overrules TCA' => [
827 'foobar', // table
828 'someColumn', // col
829 'foo,bar, add', // keyList
830 [ // TCA
831 'columns' => [
832 'someColumn' => [
833 'config' => [
834 'items' => [
835 '0' => ['aFooLabel', 'foo'],
836 '1' => ['aBarLabel', 'bar']
837 ]
838 ]
839 ]
840 ]
841 ],
842 [ // page TSconfig
843 'addItems.' => ['add' => 'aNewLabel'],
844 'altLabels.' => ['bar' => 'aBarDiffLabel'],
845 ],
846 'aFooLabel, aBarDiffLabel, aNewLabel' // expected
847 ]
848 ];
849 }
850
851 /**
852 * @test
853 * @dataProvider getLabelsFromItemsListDataProvider
854 *
855 * @param string $table
856 * @param string $col
857 * @param string $keyList
858 * @param array $tca
859 * @param array $pageTsConfig
860 * @param string $expectedLabel
861 */
862 public function getLabelsFromItemsListReturnsCorrectValue($table, $col, $keyList, $tca, array $pageTsConfig, $expectedLabel)
863 {
864 // Stub LanguageService and let sL() return the same value that came in again
865 $GLOBALS['LANG'] = $this->createMock(LanguageService::class);
866 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
867
868 $GLOBALS['TCA'][$table] = $tca;
869 $label = BackendUtility::getLabelsFromItemsList($table, $col, $keyList, $pageTsConfig);
870 $this->assertEquals($expectedLabel, $label);
871 }
872
873 /**
874 * @test
875 */
876 public function getProcessedValueReturnsLabelsForExistingValuesSolely()
877 {
878 $table = 'foobar';
879 $col = 'someColumn';
880 $tca = [
881 'columns' => [
882 'someColumn' => [
883 'config' => [
884 'type' => 'select',
885 'items' => [
886 '0' => ['aFooLabel', 'foo'],
887 '1' => ['aBarLabel', 'bar']
888 ]
889 ]
890 ]
891 ]
892 ];
893 // Stub LanguageService and let sL() return the same value that came in again
894 $GLOBALS['LANG'] = $this->createMock(LanguageService::class);
895 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
896
897 $GLOBALS['TCA'][$table] = $tca;
898 $label = BackendUtility::getProcessedValue($table, $col, 'foo,invalidKey,bar');
899 $this->assertEquals('aFooLabel, aBarLabel', $label);
900 }
901
902 /**
903 * @test
904 */
905 public function getProcessedValueReturnsPlainValueIfItemIsNotFound()
906 {
907 $table = 'foobar';
908 $col = 'someColumn';
909 $tca = [
910 'columns' => [
911 'someColumn' => [
912 'config' => [
913 'type' => 'select',
914 'items' => [
915 '0' => ['aFooLabel', 'foo']
916 ]
917 ]
918 ]
919 ]
920 ];
921 // Stub LanguageService and let sL() return the same value that came in again
922 $GLOBALS['LANG'] = $this->createMock(LanguageService::class);
923 $GLOBALS['LANG']->expects($this->any())->method('sL')->will($this->returnArgument(0));
924
925 $GLOBALS['TCA'][$table] = $tca;
926 $label = BackendUtility::getProcessedValue($table, $col, 'invalidKey');
927 $this->assertEquals('invalidKey', $label);
928 }
929
930 /**
931 * Tests concerning viewOnClick
932 */
933
934 /**
935 * @test
936 */
937 public function viewOnClickReturnsOnClickCodeWithAlternativeUrl()
938 {
939 // Make sure the hook inside viewOnClick is not fired. This may be removed if unit tests
940 // bootstrap does not initialize TYPO3_CONF_VARS anymore.
941 unset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['viewOnClickClass']);
942
943 $alternativeUrl = 'https://typo3.org/about/typo3-the-cms/the-history-of-typo3/#section';
944 $onclickCode = 'var previewWin = window.open(' . GeneralUtility::quoteJSvalue($alternativeUrl) . ',\'newTYPO3frontendWindow\');' . LF
945 . 'if (previewWin.location.href === ' . GeneralUtility::quoteJSvalue($alternativeUrl) . ') { previewWin.location.reload(); };';
946 $this->assertStringMatchesFormat(
947 $onclickCode,
948 BackendUtility::viewOnClick(null, null, null, null, $alternativeUrl, null, false)
949 );
950 }
951
952 /**
953 * @test
954 */
955 public function dateTimeAgeReturnsCorrectValues()
956 {
957 /** @var ObjectProphecy|LanguageService $languageServiceProphecy */
958 $languageServiceProphecy = $this->prophesize(LanguageService::class);
959 $languageServiceProphecy->sL(Argument::cetera())->willReturn(' min| hrs| days| yrs| min| hour| day| year');
960 $GLOBALS['LANG'] = $languageServiceProphecy->reveal();
961 $GLOBALS['EXEC_TIME'] = mktime(0, 0, 0, 3, 23, 2016);
962
963 $this->assertSame('24-03-16 00:00 (-1 day)', BackendUtility::dateTimeAge($GLOBALS['EXEC_TIME'] + 86400));
964 $this->assertSame('24-03-16 (-1 day)', BackendUtility::dateTimeAge($GLOBALS['EXEC_TIME'] + 86400, 1, 'date'));
965 }
966
967 /**
968 * @test
969 */
970 public function purgeComputedPropertyNamesRemovesPropertiesStartingWithUnderscore()
971 {
972 $propertyNames = [
973 'uid',
974 'pid',
975 '_ORIG_PID'
976 ];
977 $computedPropertyNames = BackendUtility::purgeComputedPropertyNames($propertyNames);
978 self::assertSame(['uid', 'pid'], $computedPropertyNames);
979 }
980
981 /**
982 * @test
983 */
984 public function purgeComputedPropertiesFromRecordRemovesPropertiesStartingWithUnderscore()
985 {
986 $record = [
987 'uid' => 1,
988 'pid' => 2,
989 '_ORIG_PID' => 1
990 ];
991 $expected = [
992 'uid' => 1,
993 'pid' => 2
994 ];
995 $computedProperties = BackendUtility::purgeComputedPropertiesFromRecord($record);
996 self::assertSame($expected, $computedProperties);
997 }
998
999 public function splitTableUidDataProvider()
1000 {
1001 return [
1002 'simple' => [
1003 'pages_23',
1004 ['pages', '23']
1005 ],
1006 'complex' => [
1007 'tt_content_13',
1008 ['tt_content', '13']
1009 ],
1010 'multiple underscores' => [
1011 'tx_runaway_domain_model_crime_scene_1234',
1012 ['tx_runaway_domain_model_crime_scene', '1234']
1013 ]
1014 ];
1015 }
1016
1017 /**
1018 * @test
1019 * @dataProvider splitTableUidDataProvider
1020 */
1021 public function splitTableUid($input, $expected)
1022 {
1023 $result = BackendUtility::splitTable_Uid($input);
1024 self::assertSame($expected, $result);
1025 }
1026
1027 /**
1028 * Tests if the method getPagesTSconfig can be called without having a GLOBAL['BE_USER'] object.
1029 * However, this test also shows all the various other dependencies this method has.
1030 *
1031 * @test
1032 */
1033 public function getPagesTSconfigWorksWithoutInitializedBackendUser()
1034 {
1035 $expected = ['called.' => ['config']];
1036 $pageId = 13;
1037 $parserProphecy = $this->prophesize(TsConfigParser::class);
1038 $parserProphecy->parseTSconfig(Argument::cetera())->willReturn(['hash' => $pageId, 'TSconfig' => $expected]);
1039 GeneralUtility::addInstance(TsConfigParser::class, $parserProphecy->reveal());
1040
1041 $cacheManagerProphecy = $this->prophesize(CacheManager::class);
1042 $cacheProphecy = $this->prophesize(FrontendInterface::class);
1043 $cacheManagerProphecy->getCache('cache_runtime')->willReturn($cacheProphecy->reveal());
1044 $cacheHashProphecy = $this->prophesize(FrontendInterface::class);
1045 $cacheManagerProphecy->hasCache('extbase_reflection')->willReturn(false);
1046 $cacheManagerProphecy->getCache('cache_hash')->willReturn($cacheHashProphecy->reveal());
1047 $cacheProphecy->has(Argument::cetera())->willReturn(false);
1048 $cacheProphecy->get(Argument::cetera())->willReturn(false);
1049 $cacheProphecy->set(Argument::cetera())->willReturn(false);
1050 $cacheProphecy->get('backendUtilityBeGetRootLine')->willReturn(['13--1' => []]);
1051 GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
1052 $result = BackendUtility::getPagesTSconfig($pageId);
1053 $this->assertEquals($expected, $result);
1054 }
1055
1056 /**
1057 * @test
1058 */
1059 public function returnNullForMissingTcaConfigInResolveFileReferences()
1060 {
1061 $tableName = 'table_a';
1062 $fieldName = 'field_a';
1063 $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'] = [];
1064 $this->assertNull(BackendUtility::resolveFileReferences($tableName, $fieldName, []));
1065 }
1066
1067 /**
1068 * @test
1069 */
1070 public function fixVersioningPidDoesNotChangeValuesForNoBeUserAvailable()
1071 {
1072 $GLOBALS['BE_USER'] = null;
1073 $tableName = 'table_a';
1074 $GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] = 'not_empty';
1075 $rr = [
1076 'pid' => -1,
1077 't3ver_oid' => 7,
1078 't3ver_wsid' => 42,
1079 ];
1080 $reference = $rr;
1081 BackendUtility::fixVersioningPid($tableName, $rr);
1082 $this->assertSame($reference, $rr);
1083 }
1084
1085 /**
1086 * @test
1087 * @dataProvider unfitResolveFileReferencesTableConfig
1088 */
1089 public function returnNullForUnfitTableConfigInResolveFileReferences(array $config)
1090 {
1091 $tableName = 'table_a';
1092 $fieldName = 'field_a';
1093 $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'] = $config;
1094 $this->assertNull(BackendUtility::resolveFileReferences($tableName, $fieldName, []));
1095 }
1096
1097 public function unfitResolveFileReferencesTableConfig(): array
1098 {
1099 return [
1100 'invalid table' => [
1101 [
1102 'type' => 'inline',
1103 'foreign_table' => 'table_b',
1104 ],
1105 ],
1106 'empty table' => [
1107 [
1108 'type' => 'inline',
1109 'foreign_table' => '',
1110 ],
1111 ],
1112 'invalid type' => [
1113 [
1114 'type' => 'select',
1115 'foreign_table' => 'sys_file_reference',
1116 ],
1117 ],
1118 'empty type' => [
1119 [
1120 'type' => '',
1121 'foreign_table' => 'sys_file_reference',
1122 ],
1123 ],
1124 'empty' => [
1125 [
1126 'type' => '',
1127 'foreign_table' => '',
1128 ],
1129 ],
1130 ];
1131 }
1132
1133 /**
1134 * @test
1135 */
1136 public function workspaceOLDoesNotChangeValuesForNoBeUserAvailable()
1137 {
1138 $GLOBALS['BE_USER'] = null;
1139 $tableName = 'table_a';
1140 $row = [
1141 'uid' => 1,
1142 'pid' => 17,
1143 ];
1144 $reference = $row;
1145 BackendUtility::workspaceOL($tableName, $row);
1146 $this->assertSame($reference, $row);
1147 }
1148
1149 /**
1150 * @test
1151 */
1152 public function versioningPlaceholderClauseReturnsEmptyIfNoBeUserIsAvailable()
1153 {
1154 $GLOBALS['BE_USER'] = null;
1155 $tableName = 'table_a';
1156 $GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] = 'not_empty';
1157 $this->assertSame('', BackendUtility::versioningPlaceholderClause($tableName));
1158 }
1159
1160 /**
1161 * @test
1162 */
1163 public function resolveFileReferencesReturnsEmptyResultForNoReferencesAvailable()
1164 {
1165 $tableName = 'table_a';
1166 $fieldName = 'field_a';
1167 $relationHandler = $this->prophesize(RelationHandler::class);
1168 $relationHandler->start(
1169 'foo',
1170 'sys_file_reference',
1171 '',
1172 42,
1173 $tableName,
1174 ['type' => 'inline', 'foreign_table' => 'sys_file_reference']
1175 )->shouldBeCalled();
1176 $relationHandler->tableArray = ['sys_file_reference' => []];
1177 $relationHandler->processDeletePlaceholder()->shouldBeCalled();
1178 GeneralUtility::addInstance(RelationHandler::class, $relationHandler->reveal());
1179 $GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'] = [
1180 'type' => 'inline',
1181 'foreign_table' => 'sys_file_reference',
1182 ];
1183 $elementData = [
1184 $fieldName => 'foo',
1185 'uid' => 42,
1186 ];
1187
1188 $this->assertEmpty(BackendUtility::resolveFileReferences($tableName, $fieldName, $elementData));
1189 }
1190
1191 /**
1192 * @test
1193 */
1194 public function getWorkspaceWhereClauseReturnsEmptyIfNoBeUserIsAvailable()
1195 {
1196 $GLOBALS['BE_USER'] = null;
1197 $tableName = 'table_a';
1198 $GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'] = 'not_empty';
1199 $this->assertSame('', BackendUtility::getWorkspaceWhereClause($tableName));
1200 }
1201
1202 /**
1203 * @test
1204 */
1205 public function wsMapIdReturnsLiveIdIfNoBeUserIsAvailable()
1206 {
1207 $GLOBALS['BE_USER'] = null;
1208 $tableName = 'table_a';
1209 $uid = 42;
1210 $this->assertSame(42, BackendUtility::wsMapId($tableName, $uid));
1211 }
1212
1213 /**
1214 * @test
1215 */
1216 public function getMovePlaceholderReturnsFalseIfNoBeUserIsAvailable()
1217 {
1218 $GLOBALS['BE_USER'] = null;
1219 $tableName = 'table_a';
1220 $this->assertFalse(BackendUtility::getMovePlaceholder($tableName, 42));
1221 }
1222 }