a4e9f96284cf44eefd8dcaa433da94daece003c7
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / DataHandling / DataHandlerTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\DataHandler;
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 TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
18 use TYPO3\CMS\Core\Database\DatabaseConnection;
19 use TYPO3\CMS\Core\DataHandling\DataHandler;
20 use TYPO3\CMS\Core\Tests\AccessibleObjectInterface;
21 use TYPO3\CMS\Core\Tests\Unit\DataHandling\Fixtures\AllowAccessHookFixture;
22 use TYPO3\CMS\Core\Tests\Unit\DataHandling\Fixtures\InvalidHookFixture;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25 /**
26 * Test case
27 */
28 class DataHandlerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
29 {
30 /**
31 * @var array A backup of registered singleton instances
32 */
33 protected $singletonInstances = array();
34
35 /**
36 * @var DataHandler|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface
37 */
38 protected $subject;
39
40 /**
41 * @var BackendUserAuthentication a mock logged-in back-end user
42 */
43 protected $backEndUser;
44
45 /**
46 * @var DatabaseConnection|\PHPUnit_Framework_MockObject_MockObject
47 */
48 protected $mockDatabaseConnection;
49
50 /**
51 * Set up the tests
52 */
53 protected function setUp()
54 {
55 $GLOBALS['TCA'] = array();
56 $this->singletonInstances = GeneralUtility::getSingletonInstances();
57 $this->backEndUser = $this->createMock(BackendUserAuthentication::class);
58 $this->mockDatabaseConnection = $this->createMock(DatabaseConnection::class);
59 $GLOBALS['TYPO3_DB'] = $this->mockDatabaseConnection;
60 $this->subject = $this->getAccessibleMock(DataHandler::class, ['dummy']);
61 $this->subject->start(array(), '', $this->backEndUser);
62 }
63
64 /**
65 * Tear down the tests
66 */
67 protected function tearDown()
68 {
69 GeneralUtility::resetSingletonInstances($this->singletonInstances);
70 parent::tearDown();
71 }
72
73 //////////////////////////////////////
74 // Tests for the basic functionality
75 //////////////////////////////////////
76 /**
77 * @test
78 */
79 public function fixtureCanBeCreated()
80 {
81 $this->assertTrue($this->subject instanceof DataHandler);
82 }
83
84 //////////////////////////////////////////
85 // Test concerning checkModifyAccessList
86 //////////////////////////////////////////
87 /**
88 * @test
89 */
90 public function adminIsAllowedToModifyNonAdminTable()
91 {
92 $this->subject->admin = true;
93 $this->assertTrue($this->subject->checkModifyAccessList('tt_content'));
94 }
95
96 /**
97 * @test
98 */
99 public function nonAdminIsNorAllowedToModifyNonAdminTable()
100 {
101 $this->subject->admin = false;
102 $this->assertFalse($this->subject->checkModifyAccessList('tt_content'));
103 }
104
105 /**
106 * @test
107 */
108 public function nonAdminWithTableModifyAccessIsAllowedToModifyNonAdminTable()
109 {
110 $this->subject->admin = false;
111 $this->backEndUser->groupData['tables_modify'] = 'tt_content';
112 $this->assertTrue($this->subject->checkModifyAccessList('tt_content'));
113 }
114
115 /**
116 * @test
117 */
118 public function adminIsAllowedToModifyAdminTable()
119 {
120 $this->subject->admin = true;
121 $this->assertTrue($this->subject->checkModifyAccessList('be_users'));
122 }
123
124 /**
125 * @test
126 */
127 public function nonAdminIsNotAllowedToModifyAdminTable()
128 {
129 $this->subject->admin = false;
130 $this->assertFalse($this->subject->checkModifyAccessList('be_users'));
131 }
132
133 /**
134 * @test
135 */
136 public function nonAdminWithTableModifyAccessIsNotAllowedToModifyAdminTable()
137 {
138 $tableName = $this->getUniqueId('aTable');
139 $GLOBALS['TCA'] = array(
140 $tableName => array(
141 'ctrl' => array(
142 'adminOnly' => true,
143 ),
144 ),
145 );
146 $this->subject->admin = false;
147 $this->backEndUser->groupData['tables_modify'] = $tableName;
148 $this->assertFalse($this->subject->checkModifyAccessList($tableName));
149 }
150
151 /**
152 * @test
153 */
154 public function evalCheckValueDouble2()
155 {
156 $testData = array(
157 '-0,5' => '-0.50',
158 '1000' => '1000.00',
159 '1000,10' => '1000.10',
160 '1000,0' => '1000.00',
161 '600.000.000,00' => '600000000.00',
162 '60aaa00' => '6000.00'
163 );
164 foreach ($testData as $value => $expectedReturnValue) {
165 $returnValue = $this->subject->checkValue_input_Eval($value, array('double2'), '');
166 $this->assertSame($returnValue['value'], $expectedReturnValue);
167 }
168 }
169
170 public function dataProviderDatetime()
171 {
172 // Three elements: input, timezone of input, expected output (UTC)
173 return [
174 // German standard time (without DST) is one hour ahead of UTC
175 'date in 2016 in German timezone' => [
176 1457103519, 'Europe/Berlin', 1457103519 - 3600
177 ],
178 'date in 1969 in German timezone' => [
179 -7200, 'Europe/Berlin', -10800
180 ],
181 // Los Angeles is 8 hours behind UTC
182 'date in 2016 in Los Angeles timezone' => [
183 1457103519, 'America/Los_Angeles', 1457103519 + 28800
184 ],
185 'date in UTC' => [
186 1457103519, 'UTC', 1457103519
187 ]
188 ];
189 }
190
191 /**
192 * @test
193 * @dataProvider dataProviderDatetime
194 */
195 public function evalCheckValueDatetime($input, $serverTimezone, $expectedOutput)
196 {
197 $oldTimezone = date_default_timezone_get();
198 date_default_timezone_set($serverTimezone);
199
200 $output = $this->subject->checkValue_input_Eval($input, ['datetime'], '');
201
202 // set before the assertion is performed, so it is restored even for failing tests
203 date_default_timezone_set($oldTimezone);
204
205 $this->assertEquals($expectedOutput, $output['value']);
206 }
207
208 /**
209 * Data provider for inputValueCheckRecognizesStringValuesAsIntegerValuesCorrectly
210 *
211 * @return array
212 */
213 public function inputValuesStringsDataProvider()
214 {
215 return array(
216 '"0" returns zero as integer' => array(
217 '0',
218 0
219 ),
220 '"-1999999" is interpreted correctly as -1999999 and is lot lower than -200000' => array(
221 '-1999999',
222 -1999999
223 ),
224 '"3000000" is interpreted correctly as 3000000 but is higher then 200000 and set to 200000' => array(
225 '3000000',
226 2000000
227 ),
228 );
229 }
230
231 /**
232 * @test
233 * @dataProvider inputValuesStringsDataProvider
234 * @param string $value
235 * @param int $expectedReturnValue
236 */
237 public function inputValueCheckRecognizesStringValuesAsIntegerValuesCorrectly($value, $expectedReturnValue)
238 {
239 $tcaFieldConf = array(
240 'input' => array(),
241 'eval' => 'int',
242 'range' => array(
243 'lower' => '-2000000',
244 'upper' => '2000000'
245 )
246 );
247 $returnValue = $this->subject->_call('checkValueForInput', $value, $tcaFieldConf, '', 0, 0, '');
248 $this->assertSame($returnValue['value'], $expectedReturnValue);
249 }
250
251 /**
252 * @return array
253 */
254 public function inputValueCheckCallsGetDateTimeFormatsForDatetimeFieldsDataProvider()
255 {
256 return array(
257 'dbType = date' => array(
258 'date'
259 ),
260 'dbType = datetime' => array(
261 'datetime'
262 )
263 );
264 }
265
266 /**
267 * @test
268 * @dataProvider inputValueCheckCallsGetDateTimeFormatsForDatetimeFieldsDataProvider
269 * @param string $dbType
270 */
271 public function inputValueCheckCallsNotGetDateTimeFormatsForDatetimeFieldsWithEmptyValue($dbType)
272 {
273 $tcaFieldConf = array(
274 'input' => array(),
275 'dbType' => $dbType
276 );
277 $this->mockDatabaseConnection->expects($this->never())->method('getDateTimeFormats');
278 $this->subject->_call('checkValueForInput', '', $tcaFieldConf, '', 0, 0, '');
279 }
280
281 /**
282 * @test
283 * @dataProvider inputValueCheckCallsGetDateTimeFormatsForDatetimeFieldsDataProvider
284 * @param string $dbType
285 */
286 public function inputValueCheckCallsGetDateTimeFormatsForDatetimeFieldsWithNonEmptyValue($dbType)
287 {
288 $dateTimeFormats = [
289 'date' => array(
290 'empty' => '0000-00-00',
291 'format' => 'Y-m-d'
292 ),
293 'datetime' => array(
294 'empty' => '0000-00-00 00:00:00',
295 'format' => 'Y-m-d H:i:s'
296 )
297 ];
298 $tcaFieldConf = array(
299 'input' => array(),
300 'dbType' => $dbType
301 );
302 $this->mockDatabaseConnection->expects($this->once())->method('getDateTimeFormats')->willReturn($dateTimeFormats);
303 $this->subject->_call('checkValueForInput', $dateTimeFormats[$dbType]['empty'], $tcaFieldConf, '', 0, 0, '');
304 }
305
306 /**
307 * @return array
308 */
309 public function inputValueCheckDoesNotCallGetDateTimeFormatsForNonDatetimeFieldsDataProvider()
310 {
311 return array(
312 'tca without dbType' => array(
313 array(
314 'input' => array()
315 )
316 ),
317 'tca with dbType != date/datetime' => array(
318 array(
319 'input' => array(),
320 'dbType' => 'foo'
321 )
322 )
323 );
324 }
325
326 /**
327 * @test
328 * @param array $tcaFieldConf
329 * @dataProvider inputValueCheckDoesNotCallGetDateTimeFormatsForNonDatetimeFieldsDataProvider
330 */
331 public function inputValueCheckDoesNotCallGetDateTimeFormatsForNonDatetimeFields($tcaFieldConf)
332 {
333 $this->mockDatabaseConnection->expects($this->never())->method('getDateTimeFormats');
334 $this->subject->_call('checkValueForInput', '', $tcaFieldConf, '', 0, 0, '');
335 }
336
337 ///////////////////////////////////////////
338 // Tests concerning checkModifyAccessList
339 ///////////////////////////////////////////
340 //
341 /**
342 * Tests whether a wrong interface on the 'checkModifyAccessList' hook throws an exception.
343 * @test
344 */
345 public function doesCheckModifyAccessListThrowExceptionOnWrongHookInterface()
346 {
347 $this->expectException(\UnexpectedValueException::class);
348 $this->expectExceptionCode(1251892472);
349
350 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'][] = InvalidHookFixture::class;
351 $this->subject->checkModifyAccessList('tt_content');
352 }
353
354 /**
355 * Tests whether the 'checkModifyAccessList' hook is called correctly.
356 *
357 * @test
358 */
359 public function doesCheckModifyAccessListHookGetsCalled()
360 {
361 $hookClass = $this->getUniqueId('tx_coretest');
362 $hookMock = $this->getMockBuilder(\TYPO3\CMS\Core\DataHandling\DataHandlerCheckModifyAccessListHookInterface::class)
363 ->setMethods(array('checkModifyAccessList'))
364 ->setMockClassName($hookClass)
365 ->getMock();
366 $hookMock->expects($this->once())->method('checkModifyAccessList');
367 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'][] = $hookClass;
368 GeneralUtility::addInstance($hookClass, $hookMock);
369 $this->subject->checkModifyAccessList('tt_content');
370 }
371
372 /**
373 * Tests whether the 'checkModifyAccessList' hook modifies the $accessAllowed variable.
374 *
375 * @test
376 */
377 public function doesCheckModifyAccessListHookModifyAccessAllowed()
378 {
379 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'][] = AllowAccessHookFixture::class;
380 $this->assertTrue($this->subject->checkModifyAccessList('tt_content'));
381 }
382
383 /////////////////////////////////////
384 // Tests concerning process_datamap
385 /////////////////////////////////////
386 /**
387 * @test
388 */
389 public function processDatamapForFrozenNonZeroWorkspaceReturnsFalse()
390 {
391 /** @var DataHandler $subject */
392 $subject = $this->getMockBuilder(DataHandler::class)
393 ->setMethods(array('newlog'))
394 ->getMock();
395 $this->backEndUser->workspace = 1;
396 $this->backEndUser->workspaceRec = array('freeze' => true);
397 $subject->BE_USER = $this->backEndUser;
398 $this->assertFalse($subject->process_datamap());
399 }
400
401 /**
402 * @test
403 */
404 public function processDatamapWhenEditingRecordInWorkspaceCreatesNewRecordInWorkspace()
405 {
406 $GLOBALS['TCA'] = array(
407 'pages' => array(
408 'columns' => array(),
409 ),
410 );
411
412 /** @var $subject DataHandler|\PHPUnit_Framework_MockObject_MockObject */
413 $subject = $this->getMockBuilder(DataHandler::class)
414 ->setMethods(array('newlog', 'checkModifyAccessList', 'tableReadOnly', 'checkRecordUpdateAccess', 'recordInfo'))
415 ->getMock();
416
417 $subject->bypassWorkspaceRestrictions = false;
418 $subject->datamap = array(
419 'pages' => array(
420 '1' => array(
421 'header' => 'demo'
422 )
423 )
424 );
425 $subject->expects($this->once())->method('recordInfo')->will($this->returnValue(null));
426 $subject->expects($this->once())->method('checkModifyAccessList')->with('pages')->will($this->returnValue(true));
427 $subject->expects($this->once())->method('tableReadOnly')->with('pages')->will($this->returnValue(false));
428 $subject->expects($this->once())->method('checkRecordUpdateAccess')->will($this->returnValue(true));
429
430 /** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $backEndUser */
431 $backEndUser = $this->createMock(BackendUserAuthentication::class);
432 $backEndUser->workspace = 1;
433 $backEndUser->workspaceRec = array('freeze' => false);
434 $backEndUser->expects($this->once())->method('workspaceAllowAutoCreation')->will($this->returnValue(true));
435 $backEndUser->expects($this->once())->method('workspaceCannotEditRecord')->will($this->returnValue(true));
436 $backEndUser->expects($this->once())->method('recordEditAccessInternals')->with('pages', 1)->will($this->returnValue(true));
437 $subject->BE_USER = $backEndUser;
438 $createdTceMain = $this->createMock(DataHandler::class);
439 $createdTceMain->expects($this->once())->method('start')->with(array(), array(
440 'pages' => array(
441 1 => array(
442 'version' => array(
443 'action' => 'new',
444 'treeLevels' => -1,
445 'label' => 'Auto-created for WS #1'
446 )
447 )
448 )
449 ));
450 $createdTceMain->expects($this->never())->method('process_datamap');
451 $createdTceMain->expects($this->once())->method('process_cmdmap');
452 GeneralUtility::addInstance(DataHandler::class, $createdTceMain);
453 $subject->process_datamap();
454 }
455
456 /**
457 * @test
458 */
459 public function doesCheckFlexFormValueHookGetsCalled()
460 {
461 $hookClass = $this->getUniqueId('tx_coretest');
462 $hookMock = $this->getMockBuilder($hookClass)
463 ->setMethods(array('checkFlexFormValue_beforeMerge'))
464 ->getMock();
465 $hookMock->expects($this->once())->method('checkFlexFormValue_beforeMerge');
466 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkFlexFormValue'][] = $hookClass;
467 GeneralUtility::addInstance($hookClass, $hookMock);
468 $this->subject->_call('checkValueForFlex', [], [], [], '', 0, '', '', 0, 0, 0, [], '');
469 }
470
471 /////////////////////////////////////
472 // Tests concerning log
473 /////////////////////////////////////
474 /**
475 * @test
476 */
477 public function logCallsWriteLogOfBackendUserIfLoggingIsEnabled()
478 {
479 $backendUser = $this->createMock(BackendUserAuthentication::class);
480 $backendUser->expects($this->once())->method('writelog');
481 $this->subject->enableLogging = true;
482 $this->subject->BE_USER = $backendUser;
483 $this->subject->log('', 23, 0, 42, 0, 'details');
484 }
485
486 /**
487 * @test
488 */
489 public function logDoesNotCallWriteLogOfBackendUserIfLoggingIsDisabled()
490 {
491 $backendUser = $this->createMock(BackendUserAuthentication::class);
492 $backendUser->expects($this->never())->method('writelog');
493 $this->subject->enableLogging = false;
494 $this->subject->BE_USER = $backendUser;
495 $this->subject->log('', 23, 0, 42, 0, 'details');
496 }
497
498 /**
499 * @test
500 */
501 public function logAddsEntryToLocalErrorLogArray()
502 {
503 $backendUser = $this->createMock(BackendUserAuthentication::class);
504 $this->subject->BE_USER = $backendUser;
505 $this->subject->enableLogging = true;
506 $this->subject->errorLog = array();
507 $logDetailsUnique = $this->getUniqueId('details');
508 $this->subject->log('', 23, 0, 42, 1, $logDetailsUnique);
509 $this->assertStringEndsWith($logDetailsUnique, $this->subject->errorLog[0]);
510 }
511
512 /**
513 * @test
514 */
515 public function logFormatsDetailMessageWithAdditionalDataInLocalErrorArray()
516 {
517 $backendUser = $this->createMock(BackendUserAuthentication::class);
518 $this->subject->BE_USER = $backendUser;
519 $this->subject->enableLogging = true;
520 $this->subject->errorLog = array();
521 $logDetails = $this->getUniqueId('details');
522 $this->subject->log('', 23, 0, 42, 1, '%1$s' . $logDetails . '%2$s', -1, array('foo', 'bar'));
523 $expected = 'foo' . $logDetails . 'bar';
524 $this->assertStringEndsWith($expected, $this->subject->errorLog[0]);
525 }
526
527 /**
528 * @param bool $expected
529 * @param string $submittedValue
530 * @param string $storedValue
531 * @param string $storedType
532 * @param bool $allowNull
533 *
534 * @dataProvider equalSubmittedAndStoredValuesAreDeterminedDataProvider
535 * @test
536 */
537 public function equalSubmittedAndStoredValuesAreDetermined($expected, $submittedValue, $storedValue, $storedType, $allowNull)
538 {
539 $result = $this->callInaccessibleMethod(
540 $this->subject,
541 'isSubmittedValueEqualToStoredValue',
542 $submittedValue, $storedValue, $storedType, $allowNull
543 );
544 $this->assertEquals($expected, $result);
545 }
546
547 /**
548 * @return array
549 */
550 public function equalSubmittedAndStoredValuesAreDeterminedDataProvider()
551 {
552 return array(
553 // String
554 'string value "" vs. ""' => array(
555 true,
556 '', '', 'string', false
557 ),
558 'string value 0 vs. "0"' => array(
559 true,
560 0, '0', 'string', false
561 ),
562 'string value 1 vs. "1"' => array(
563 true,
564 1, '1', 'string', false
565 ),
566 'string value "0" vs. ""' => array(
567 false,
568 '0', '', 'string', false
569 ),
570 'string value 0 vs. ""' => array(
571 false,
572 0, '', 'string', false
573 ),
574 'string value null vs. ""' => array(
575 true,
576 null, '', 'string', false
577 ),
578 // Integer
579 'integer value 0 vs. 0' => array(
580 true,
581 0, 0, 'int', false
582 ),
583 'integer value "0" vs. "0"' => array(
584 true,
585 '0', '0', 'int', false
586 ),
587 'integer value 0 vs. "0"' => array(
588 true,
589 0, '0', 'int', false
590 ),
591 'integer value "" vs. "0"' => array(
592 true,
593 '', '0', 'int', false
594 ),
595 'integer value "" vs. 0' => array(
596 true,
597 '', 0, 'int', false
598 ),
599 'integer value "0" vs. 0' => array(
600 true,
601 '0', 0, 'int', false
602 ),
603 'integer value 1 vs. 1' => array(
604 true,
605 1, 1, 'int', false
606 ),
607 'integer value 1 vs. "1"' => array(
608 true,
609 1, '1', 'int', false
610 ),
611 'integer value "1" vs. "1"' => array(
612 true,
613 '1', '1', 'int', false
614 ),
615 'integer value "1" vs. 1' => array(
616 true,
617 '1', 1, 'int', false
618 ),
619 'integer value "0" vs. "1"' => array(
620 false,
621 '0', '1', 'int', false
622 ),
623 // String with allowed NULL values
624 'string with allowed null value "" vs. ""' => array(
625 true,
626 '', '', 'string', true
627 ),
628 'string with allowed null value 0 vs. "0"' => array(
629 true,
630 0, '0', 'string', true
631 ),
632 'string with allowed null value 1 vs. "1"' => array(
633 true,
634 1, '1', 'string', true
635 ),
636 'string with allowed null value "0" vs. ""' => array(
637 false,
638 '0', '', 'string', true
639 ),
640 'string with allowed null value 0 vs. ""' => array(
641 false,
642 0, '', 'string', true
643 ),
644 'string with allowed null value null vs. ""' => array(
645 false,
646 null, '', 'string', true
647 ),
648 'string with allowed null value "" vs. null' => array(
649 false,
650 '', null, 'string', true
651 ),
652 'string with allowed null value null vs. null' => array(
653 true,
654 null, null, 'string', true
655 ),
656 // Integer with allowed NULL values
657 'integer with allowed null value 0 vs. 0' => array(
658 true,
659 0, 0, 'int', true
660 ),
661 'integer with allowed null value "0" vs. "0"' => array(
662 true,
663 '0', '0', 'int', true
664 ),
665 'integer with allowed null value 0 vs. "0"' => array(
666 true,
667 0, '0', 'int', true
668 ),
669 'integer with allowed null value "" vs. "0"' => array(
670 true,
671 '', '0', 'int', true
672 ),
673 'integer with allowed null value "" vs. 0' => array(
674 true,
675 '', 0, 'int', true
676 ),
677 'integer with allowed null value "0" vs. 0' => array(
678 true,
679 '0', 0, 'int', true
680 ),
681 'integer with allowed null value 1 vs. 1' => array(
682 true,
683 1, 1, 'int', true
684 ),
685 'integer with allowed null value "1" vs. "1"' => array(
686 true,
687 '1', '1', 'int', true
688 ),
689 'integer with allowed null value "1" vs. 1' => array(
690 true,
691 '1', 1, 'int', true
692 ),
693 'integer with allowed null value 1 vs. "1"' => array(
694 true,
695 1, '1', 'int', true
696 ),
697 'integer with allowed null value "0" vs. "1"' => array(
698 false,
699 '0', '1', 'int', true
700 ),
701 'integer with allowed null value null vs. ""' => array(
702 false,
703 null, '', 'int', true
704 ),
705 'integer with allowed null value "" vs. null' => array(
706 false,
707 '', null, 'int', true
708 ),
709 'integer with allowed null value null vs. null' => array(
710 true,
711 null, null, 'int', true
712 ),
713 'integer with allowed null value null vs. "0"' => array(
714 false,
715 null, '0', 'int', true
716 ),
717 'integer with allowed null value null vs. 0' => array(
718 false,
719 null, 0, 'int', true
720 ),
721 'integer with allowed null value "0" vs. null' => array(
722 false,
723 '0', null, 'int', true
724 ),
725 );
726 }
727
728 /**
729 * @param bool $expected
730 * @param array $eval
731 * @dataProvider getPlaceholderTitleForTableLabelReturnsLabelThatsMatchesLabelFieldConditionsDataProvider
732 * @test
733 */
734 public function getPlaceholderTitleForTableLabelReturnsLabelThatsMatchesLabelFieldConditions($expected, $eval)
735 {
736 $table = 'phpunit_dummy';
737
738 /** @var DataHandler|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface $subject */
739 $subject = $this->getAccessibleMock(
740 DataHandler::class,
741 array('dummy')
742 );
743
744 $backendUser = $this->createMock(BackendUserAuthentication::class);
745 $subject->BE_USER = $backendUser;
746 $subject->BE_USER->workspace = 1;
747
748 $GLOBALS['TCA'][$table] = array();
749 $GLOBALS['TCA'][$table]['ctrl'] = array('label' => 'dummy');
750 $GLOBALS['TCA'][$table]['columns'] = array(
751 'dummy' => array(
752 'config' => array(
753 'eval' => $eval
754 )
755 )
756 );
757
758 $this->assertEquals($expected, $subject->_call('getPlaceholderTitleForTableLabel', $table));
759 }
760
761 /**
762 * @return array
763 */
764 public function getPlaceholderTitleForTableLabelReturnsLabelThatsMatchesLabelFieldConditionsDataProvider()
765 {
766 return array(
767 array(
768 0.10,
769 'double2'
770 ),
771 array(
772 0,
773 'int'
774 ),
775 array(
776 '0',
777 'datetime'
778 ),
779 array(
780 '[PLACEHOLDER, WS#1]',
781 ''
782 )
783 );
784 }
785
786 /**
787 * @test
788 */
789 public function deletePagesOnRootLevelIsDenied()
790 {
791 /** @var DataHandler|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface $dataHandlerMock */
792 $dataHandlerMock = $this->getMockBuilder(DataHandler::class)
793 ->setMethods(['canDeletePage', 'newlog2'])
794 ->getMock();
795 $dataHandlerMock
796 ->expects($this->never())
797 ->method('canDeletePage');
798 $dataHandlerMock
799 ->expects($this->once())
800 ->method('newlog2')
801 ->with('Deleting all pages starting from the root-page is disabled.', 'pages', 0, 0, 2);
802
803 $dataHandlerMock->deletePages(0);
804 }
805
806 /**
807 * @test
808 */
809 public function deleteRecord_procBasedOnFieldTypeRespectsEnableCascadingDelete()
810 {
811 $table = $this->getUniqueId('foo_');
812 $conf = array(
813 'type' => 'inline',
814 'foreign_table' => $this->getUniqueId('foreign_foo_'),
815 'behaviour' => array(
816 'enableCascadingDelete' => 0,
817 )
818 );
819
820 /** @var \TYPO3\CMS\Core\Database\RelationHandler $mockRelationHandler */
821 $mockRelationHandler = $this->createMock(\TYPO3\CMS\Core\Database\RelationHandler::class);
822 $mockRelationHandler->itemArray = array(
823 '1' => array('table' => $this->getUniqueId('bar_'), 'id' => 67)
824 );
825
826 /** @var DataHandler|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface $mockDataHandler */
827 $mockDataHandler = $this->getAccessibleMock(DataHandler::class, array('getInlineFieldType', 'deleteAction', 'createRelationHandlerInstance'), array(), '', false);
828 $mockDataHandler->expects($this->once())->method('getInlineFieldType')->will($this->returnValue('field'));
829 $mockDataHandler->expects($this->once())->method('createRelationHandlerInstance')->will($this->returnValue($mockRelationHandler));
830 $mockDataHandler->expects($this->never())->method('deleteAction');
831 $mockDataHandler->deleteRecord_procBasedOnFieldType($table, 42, 'foo', 'bar', $conf);
832 }
833
834 /**
835 * @return array
836 */
837 public function checkValue_checkReturnsExpectedValuesDataProvider()
838 {
839 return array(
840 'None item selected' => array(
841 0,
842 0
843 ),
844 'All items selected' => array(
845 7,
846 7
847 ),
848 'Item 1 and 2 are selected' => array(
849 3,
850 3
851 ),
852 'Value is higher than allowed (all checkboxes checked)' => array(
853 15,
854 7
855 ),
856 'Value is higher than allowed (some checkboxes checked)' => array(
857 11,
858 3
859 ),
860 'Negative value' => array(
861 -5,
862 0
863 )
864 );
865 }
866
867 /**
868 * @param string $value
869 * @param string $expectedValue
870 *
871 * @dataProvider checkValue_checkReturnsExpectedValuesDataProvider
872 * @test
873 */
874 public function checkValue_checkReturnsExpectedValues($value, $expectedValue)
875 {
876 $expectedResult = array(
877 'value' => $expectedValue
878 );
879 $result = array();
880 $tcaFieldConfiguration = array(
881 'items' => array(
882 array('Item 1', 0),
883 array('Item 2', 0),
884 array('Item 3', 0)
885 )
886 );
887 $this->assertSame($expectedResult, $this->subject->_call('checkValueForCheck', $result, $value, $tcaFieldConfiguration, '', 0, 0, ''));
888 }
889
890 /**
891 * @test
892 */
893 public function checkValueForInputConvertsNullToEmptyString()
894 {
895 $previousLanguageService = $GLOBALS['LANG'];
896 $GLOBALS['LANG'] = GeneralUtility::makeInstance(\TYPO3\CMS\Lang\LanguageService::class);
897 $GLOBALS['LANG']->init('default');
898 $expectedResult = array('value' => '');
899 $this->assertSame($expectedResult, $this->subject->_call('checkValueForInput', null, array('type' => 'string', 'max' => 40), 'tt_content', 'NEW55c0e67f8f4d32.04974534', 89, 'table_caption'));
900 $GLOBALS['LANG'] = $previousLanguageService;
901 }
902
903 /**
904 * @param mixed $value
905 * @param array $configuration
906 * @param int|string $expected
907 * @test
908 * @dataProvider referenceValuesAreCastedDataProvider
909 */
910 public function referenceValuesAreCasted($value, array $configuration, $expected)
911 {
912 $this->assertEquals(
913 $expected,
914 $this->subject->_call('castReferenceValue', $value, $configuration)
915 );
916 }
917
918 /**
919 * @return array
920 */
921 public function referenceValuesAreCastedDataProvider()
922 {
923 return array(
924 'all empty' => array(
925 '', array(), ''
926 ),
927 'cast zero with MM table' => array(
928 '', array('MM' => 'table'), 0
929 ),
930 'cast zero with MM table with default value' => array(
931 '', array('MM' => 'table', 'default' => 13), 0
932 ),
933 'cast zero with foreign field' => array(
934 '', array('foreign_field' => 'table', 'default' => 13), 0
935 ),
936 'cast zero with foreign field with default value' => array(
937 '', array('foreign_field' => 'table'), 0
938 ),
939 'pass zero' => array(
940 '0', array(), '0'
941 ),
942 'pass value' => array(
943 '1', array('default' => 13), '1'
944 ),
945 'use default value' => array(
946 '', array('default' => 13), 13
947 ),
948 );
949 }
950 }