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