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