[TASK] Cleanup TCA
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Unit / ContentObject / ContentObjectRendererTest.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Tests\Unit\ContentObject;
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 Psr\Log\LoggerInterface;
18 use TYPO3\CMS\Core\Cache\CacheManager;
19 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface as CacheFrontendInterface;
20 use TYPO3\CMS\Core\Charset\CharsetConverter;
21 use TYPO3\CMS\Core\Core\ApplicationContext;
22 use TYPO3\CMS\Core\Database\DatabaseConnection;
23 use TYPO3\CMS\Core\Log\LogManager;
24 use TYPO3\CMS\Core\Resource\File;
25 use TYPO3\CMS\Core\Resource\ResourceFactory;
26 use TYPO3\CMS\Core\Tests\UnitTestCase;
27 use TYPO3\CMS\Core\TypoScript\TemplateService;
28 use TYPO3\CMS\Core\Utility\DebugUtility;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject;
31 use TYPO3\CMS\Frontend\ContentObject\CaseContentObject;
32 use TYPO3\CMS\Frontend\ContentObject\ContentContentObject;
33 use TYPO3\CMS\Frontend\ContentObject\ContentObjectArrayContentObject;
34 use TYPO3\CMS\Frontend\ContentObject\ContentObjectArrayInternalContentObject;
35 use TYPO3\CMS\Frontend\ContentObject\ContentObjectOneSourceCollectionHookInterface;
36 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
37 use TYPO3\CMS\Frontend\ContentObject\ContentObjectStdWrapHookInterface;
38 use TYPO3\CMS\Frontend\ContentObject\EditPanelContentObject;
39 use TYPO3\CMS\Frontend\ContentObject\FileContentObject;
40 use TYPO3\CMS\Frontend\ContentObject\FilesContentObject;
41 use TYPO3\CMS\Frontend\ContentObject\FluidTemplateContentObject;
42 use TYPO3\CMS\Frontend\ContentObject\HierarchicalMenuContentObject;
43 use TYPO3\CMS\Frontend\ContentObject\ImageContentObject;
44 use TYPO3\CMS\Frontend\ContentObject\ImageResourceContentObject;
45 use TYPO3\CMS\Frontend\ContentObject\LoadRegisterContentObject;
46 use TYPO3\CMS\Frontend\ContentObject\RecordsContentObject;
47 use TYPO3\CMS\Frontend\ContentObject\RestoreRegisterContentObject;
48 use TYPO3\CMS\Frontend\ContentObject\ScalableVectorGraphicsContentObject;
49 use TYPO3\CMS\Frontend\ContentObject\TemplateContentObject;
50 use TYPO3\CMS\Frontend\ContentObject\TextContentObject;
51 use TYPO3\CMS\Frontend\ContentObject\UserContentObject;
52 use TYPO3\CMS\Frontend\ContentObject\UserInternalContentObject;
53 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
54 use TYPO3\CMS\Frontend\Page\PageRepository;
55 use TYPO3\CMS\Frontend\Tests\Unit\ContentObject\Fixtures\PageRepositoryFixture;
56
57 /**
58 * Testcase for TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
59 */
60 class ContentObjectRendererTest extends UnitTestCase
61 {
62 /**
63 * @var string
64 */
65 protected $currentLocale;
66
67 /**
68 * @var array A backup of registered singleton instances
69 */
70 protected $singletonInstances = array();
71
72 /**
73 * @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
74 */
75 protected $subject = null;
76
77 /**
78 * @var \PHPUnit_Framework_MockObject_MockObject|TypoScriptFrontendController|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
79 */
80 protected $frontendControllerMock = null;
81
82 /**
83 * @var \PHPUnit_Framework_MockObject_MockObject|TemplateService
84 */
85 protected $templateServiceMock = null;
86
87 /**
88 * Default content object name -> class name map, shipped with TYPO3 CMS
89 *
90 * @var array
91 */
92 protected $contentObjectMap = [
93 'TEXT' => TextContentObject::class,
94 'CASE' => CaseContentObject::class,
95 'COBJ_ARRAY' => ContentObjectArrayContentObject::class,
96 'COA' => ContentObjectArrayContentObject::class,
97 'COA_INT' => ContentObjectArrayInternalContentObject::class,
98 'USER' => UserContentObject::class,
99 'USER_INT' => UserInternalContentObject::class,
100 'FILE' => FileContentObject::class,
101 'FILES' => FilesContentObject::class,
102 'IMAGE' => ImageContentObject::class,
103 'IMG_RESOURCE' => ImageResourceContentObject::class,
104 'CONTENT' => ContentContentObject::class,
105 'RECORDS' => RecordsContentObject::class,
106 'HMENU' => HierarchicalMenuContentObject::class,
107 'CASEFUNC' => CaseContentObject::class,
108 'LOAD_REGISTER' => LoadRegisterContentObject::class,
109 'RESTORE_REGISTER' => RestoreRegisterContentObject::class,
110 'TEMPLATE' => TemplateContentObject::class,
111 'FLUIDTEMPLATE' => FluidTemplateContentObject::class,
112 'SVG' => ScalableVectorGraphicsContentObject::class,
113 'EDITPANEL' => EditPanelContentObject::class
114 ];
115
116 /**
117 * Set up
118 */
119 protected function setUp()
120 {
121 $this->currentLocale = setlocale(LC_NUMERIC, 0);
122 $this->singletonInstances = GeneralUtility::getSingletonInstances();
123 $this->createMockedLoggerAndLogManager();
124
125 $this->templateServiceMock =
126 $this->getMockBuilder(TemplateService::class)
127 ->setMethods(['getFileName', 'linkData'])->getMock();
128 $pageRepositoryMock =
129 $this->getMockBuilder(PageRepositoryFixture::class)
130 ->setMethods(['getRawRecord', 'getMountPointInfo'])->getMock();
131 $this->frontendControllerMock =
132 $this->getAccessibleMock(TypoScriptFrontendController::class,
133 ['dummy'], [], '', false);
134 $this->frontendControllerMock->tmpl = $this->templateServiceMock;
135 $this->frontendControllerMock->config = [];
136 $this->frontendControllerMock->page = [];
137 $this->frontendControllerMock->sys_page = $pageRepositoryMock;
138 $GLOBALS['TSFE'] = $this->frontendControllerMock;
139 $GLOBALS['TYPO3_DB'] =
140 $this->getMockBuilder(DatabaseConnection::class)->getMock();
141
142 $this->subject = $this->getAccessibleMock(
143 ContentObjectRenderer::class,
144 ['getResourceFactory', 'getEnvironmentVariable'],
145 [$this->frontendControllerMock]
146 );
147 $this->subject->setContentObjectClassMap($this->contentObjectMap);
148 $this->subject->start([], 'tt_content');
149 }
150
151 protected function tearDown()
152 {
153 setlocale(LC_NUMERIC, $this->currentLocale);
154 GeneralUtility::resetSingletonInstances($this->singletonInstances);
155 parent::tearDown();
156 }
157
158 //////////////////////
159 // Utility functions
160 //////////////////////
161
162 /**
163 * @return TypoScriptFrontendController
164 */
165 protected function getFrontendController()
166 {
167 return $GLOBALS['TSFE'];
168 }
169
170 /**
171 * Avoid logging to the file system (file writer is currently the only configured writer)
172 */
173 protected function createMockedLoggerAndLogManager()
174 {
175 $logManagerMock = $this->getMockBuilder(LogManager::class)->getMock();
176 $loggerMock = $this->getMockBuilder(LoggerInterface::class)->getMock();
177 $logManagerMock->expects($this->any())
178 ->method('getLogger')
179 ->willReturn($loggerMock);
180 GeneralUtility::setSingletonInstance(LogManager::class, $logManagerMock);
181 }
182
183 /**
184 * Converts the subject and the expected result into utf-8.
185 *
186 * @param string $subject the subject, will be modified
187 * @param string $expected the expected result, will be modified
188 */
189 protected function handleCharset(&$subject, &$expected)
190 {
191 $charsetConverter = new CharsetConverter();
192 $subject = $charsetConverter->conv($subject, 'iso-8859-1', 'utf-8');
193 $expected = $charsetConverter->conv($expected, 'iso-8859-1', 'utf-8');
194 }
195
196 /////////////////////////////////////////////
197 // Tests concerning the getImgResource hook
198 /////////////////////////////////////////////
199 /**
200 * @test
201 */
202 public function getImgResourceCallsGetImgResourcePostProcessHook()
203 {
204 $this->templateServiceMock
205 ->expects($this->atLeastOnce())
206 ->method('getFileName')
207 ->with('typo3/clear.gif')
208 ->will($this->returnValue('typo3/clear.gif'));
209
210 $resourceFactory = $this->createMock(ResourceFactory::class);
211 $this->subject->expects($this->any())->method('getResourceFactory')->will($this->returnValue($resourceFactory));
212
213 $className = $this->getUniqueId('tx_coretest');
214 $getImgResourceHookMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\ContentObject\ContentObjectGetImageResourceHookInterface::class)
215 ->setMethods(array('getImgResourcePostProcess'))
216 ->setMockClassName($className)
217 ->getMock();
218 $getImgResourceHookMock
219 ->expects($this->once())
220 ->method('getImgResourcePostProcess')
221 ->will($this->returnCallback(array($this, 'isGetImgResourceHookCalledCallback')));
222 $getImgResourceHookObjects = array($getImgResourceHookMock);
223 $this->subject->_setRef('getImgResourceHookObjects', $getImgResourceHookObjects);
224 $this->subject->getImgResource('typo3/clear.gif', array());
225 }
226
227 /**
228 * Handles the arguments that have been sent to the getImgResource hook.
229 *
230 * @param string $file
231 * @param array $fileArray
232 * @param $imageResource
233 * @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $parent
234 * @return array
235 * @see getImgResourceHookGetsCalled
236 */
237 public function isGetImgResourceHookCalledCallback($file, $fileArray, $imageResource, $parent)
238 {
239 $this->assertEquals('typo3/clear.gif', $file);
240 $this->assertEquals('typo3/clear.gif', $imageResource['origFile']);
241 $this->assertTrue(is_array($fileArray));
242 $this->assertTrue($parent instanceof ContentObjectRenderer);
243 return $imageResource;
244 }
245
246 //////////////////////////////////////
247 // Tests concerning getContentObject
248 //////////////////////////////////////
249
250 public function getContentObjectValidContentObjectsDataProvider()
251 {
252 $dataProvider = array();
253 foreach ($this->contentObjectMap as $name => $className) {
254 $dataProvider[] = array($name, $className);
255 }
256 return $dataProvider;
257 }
258
259 /**
260 * @test
261 * @dataProvider getContentObjectValidContentObjectsDataProvider
262 * @param string $name TypoScript name of content object
263 * @param string $fullClassName Expected class name
264 */
265 public function getContentObjectCallsMakeInstanceForNewContentObjectInstance($name, $fullClassName)
266 {
267 $contentObjectInstance = $this->createMock($fullClassName);
268 GeneralUtility::addInstance($fullClassName, $contentObjectInstance);
269 $this->assertSame($contentObjectInstance, $this->subject->getContentObject($name));
270 }
271
272 /////////////////////////////////////////
273 // Tests concerning getQueryArguments()
274 /////////////////////////////////////////
275 /**
276 * @test
277 */
278 public function getQueryArgumentsExcludesParameters()
279 {
280 $this->subject->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
281 $this->returnValue('key1=value1&key2=value2&key3[key31]=value31&key3[key32][key321]=value321&key3[key32][key322]=value322')
282 );
283 $getQueryArgumentsConfiguration = array();
284 $getQueryArgumentsConfiguration['exclude'] = array();
285 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
286 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
287 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
288 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
289 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2&key3[key32][key322]=value322');
290 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration);
291 $this->assertEquals($expectedResult, $actualResult);
292 }
293
294 /**
295 * @test
296 */
297 public function getQueryArgumentsExcludesGetParameters()
298 {
299 $_GET = array(
300 'key1' => 'value1',
301 'key2' => 'value2',
302 'key3' => array(
303 'key31' => 'value31',
304 'key32' => array(
305 'key321' => 'value321',
306 'key322' => 'value322'
307 )
308 )
309 );
310 $getQueryArgumentsConfiguration = array();
311 $getQueryArgumentsConfiguration['method'] = 'GET';
312 $getQueryArgumentsConfiguration['exclude'] = array();
313 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
314 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
315 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
316 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
317 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2&key3[key32][key322]=value322');
318 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration);
319 $this->assertEquals($expectedResult, $actualResult);
320 }
321
322 /**
323 * @test
324 */
325 public function getQueryArgumentsOverrulesSingleParameter()
326 {
327 $this->subject->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
328 $this->returnValue('key1=value1')
329 );
330 $getQueryArgumentsConfiguration = array();
331 $overruleArguments = array(
332 // Should be overridden
333 'key1' => 'value1Overruled',
334 // Shouldn't be set: Parameter doesn't exist in source array and is not forced
335 'key2' => 'value2Overruled'
336 );
337 $expectedResult = '&key1=value1Overruled';
338 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments);
339 $this->assertEquals($expectedResult, $actualResult);
340 }
341
342 /**
343 * @test
344 */
345 public function getQueryArgumentsOverrulesMultiDimensionalParameters()
346 {
347 $_POST = array(
348 'key1' => 'value1',
349 'key2' => 'value2',
350 'key3' => array(
351 'key31' => 'value31',
352 'key32' => array(
353 'key321' => 'value321',
354 'key322' => 'value322'
355 )
356 )
357 );
358 $getQueryArgumentsConfiguration = array();
359 $getQueryArgumentsConfiguration['method'] = 'POST';
360 $getQueryArgumentsConfiguration['exclude'] = array();
361 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
362 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
363 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
364 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
365 $overruleArguments = array(
366 // Should be overriden
367 'key2' => 'value2Overruled',
368 'key3' => array(
369 'key32' => array(
370 // Shouldn't be set: Parameter is excluded and not forced
371 'key321' => 'value321Overruled',
372 // Should be overriden: Parameter is not excluded
373 'key322' => 'value322Overruled',
374 // Shouldn't be set: Parameter doesn't exist in source array and is not forced
375 'key323' => 'value323Overruled'
376 )
377 )
378 );
379 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2Overruled&key3[key32][key322]=value322Overruled');
380 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments);
381 $this->assertEquals($expectedResult, $actualResult);
382 }
383
384 /**
385 * @test
386 */
387 public function getQueryArgumentsOverrulesMultiDimensionalForcedParameters()
388 {
389 $this->subject->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
390 $this->returnValue('key1=value1&key2=value2&key3[key31]=value31&key3[key32][key321]=value321&key3[key32][key322]=value322')
391 );
392 $_POST = array(
393 'key1' => 'value1',
394 'key2' => 'value2',
395 'key3' => array(
396 'key31' => 'value31',
397 'key32' => array(
398 'key321' => 'value321',
399 'key322' => 'value322'
400 )
401 )
402 );
403 $getQueryArgumentsConfiguration = array();
404 $getQueryArgumentsConfiguration['exclude'] = array();
405 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
406 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
407 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
408 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key322]';
409 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
410 $overruleArguments = array(
411 // Should be overriden
412 'key2' => 'value2Overruled',
413 'key3' => array(
414 'key32' => array(
415 // Should be set: Parameter is excluded but forced
416 'key321' => 'value321Overruled',
417 // Should be set: Parameter doesn't exist in source array but is forced
418 'key323' => 'value323Overruled'
419 )
420 )
421 );
422 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2Overruled&key3[key32][key321]=value321Overruled&key3[key32][key323]=value323Overruled');
423 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments, true);
424 $this->assertEquals($expectedResult, $actualResult);
425 $getQueryArgumentsConfiguration['method'] = 'POST';
426 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments, true);
427 $this->assertEquals($expectedResult, $actualResult);
428 }
429
430 /**
431 * @test
432 */
433 public function getQueryArgumentsWithMethodPostGetMergesParameters()
434 {
435 $_POST = array(
436 'key1' => 'POST1',
437 'key2' => 'POST2',
438 'key3' => array(
439 'key31' => 'POST31',
440 'key32' => 'POST32',
441 'key33' => array(
442 'key331' => 'POST331',
443 'key332' => 'POST332',
444 )
445 )
446 );
447 $_GET = array(
448 'key2' => 'GET2',
449 'key3' => array(
450 'key32' => 'GET32',
451 'key33' => array(
452 'key331' => 'GET331',
453 )
454 )
455 );
456 $getQueryArgumentsConfiguration = array();
457 $getQueryArgumentsConfiguration['method'] = 'POST,GET';
458 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
459 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration);
460 $this->assertEquals($expectedResult, $actualResult);
461 }
462
463 /**
464 * @test
465 */
466 public function getQueryArgumentsWithMethodGetPostMergesParameters()
467 {
468 $_GET = array(
469 'key1' => 'GET1',
470 'key2' => 'GET2',
471 'key3' => array(
472 'key31' => 'GET31',
473 'key32' => 'GET32',
474 'key33' => array(
475 'key331' => 'GET331',
476 'key332' => 'GET332',
477 )
478 )
479 );
480 $_POST = array(
481 'key2' => 'POST2',
482 'key3' => array(
483 'key32' => 'POST32',
484 'key33' => array(
485 'key331' => 'POST331',
486 )
487 )
488 );
489 $getQueryArgumentsConfiguration = array();
490 $getQueryArgumentsConfiguration['method'] = 'GET,POST';
491 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
492 $actualResult = $this->subject->getQueryArguments($getQueryArgumentsConfiguration);
493 $this->assertEquals($expectedResult, $actualResult);
494 }
495
496 /**
497 * Encodes square brackets in URL.
498 *
499 * @param string $string
500 * @return string
501 */
502 private function rawUrlEncodeSquareBracketsInUrl($string)
503 {
504 return str_replace(array('[', ']'), array('%5B', '%5D'), $string);
505 }
506
507 //////////////////////////
508 // Tests concerning crop
509 //////////////////////////
510 /**
511 * @test
512 */
513 public function cropIsMultibyteSafe()
514 {
515 $this->assertEquals('бла', $this->subject->crop('бла', '3|...'));
516 }
517
518 //////////////////////////////
519
520 //////////////////////////////
521 // Tests concerning cropHTML
522 //////////////////////////////
523
524 /**
525 * Data provider for cropHTML.
526 *
527 * Provides combinations of text type and configuration.
528 *
529 * @return array [$expect, $conf, $content]
530 */
531 public function cropHTMLDataProvider()
532 {
533 $plainText = 'Kasper Sk' . chr(229) . 'rh' . chr(248)
534 . 'j implemented the original version of the crop function.';
535 $textWithMarkup = '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
536 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> the '
537 . 'original version of the crop function.';
538 $textWithEntities = 'Kasper Sk&aring;rh&oslash;j implemented the; '
539 . 'original ' . 'version of the crop function.';
540 $textWithLinebreaks = "Lorem ipsum dolor sit amet,\n"
541 . "consetetur sadipscing elitr,\n"
542 . 'sed diam nonumy eirmod tempor invidunt ut labore e'
543 . 't dolore magna aliquyam';
544
545 return [
546 'plain text; 11|...' => [
547 'Kasper Sk' . chr(229) . 'r...',
548 $plainText, '11|...',
549 ],
550 'plain text; -58|...' => [
551 '...h' . chr(248) . 'j implemented the original version of '
552 . 'the crop function.',
553 $plainText, '-58|...',
554 ],
555 'plain text; 4|...|1' => [
556 'Kasp...',
557 $plainText, '4|...|1',
558 ],
559 'plain text; 20|...|1' => [
560 'Kasper Sk' . chr(229) . 'rh' . chr(248) . 'j...',
561 $plainText, '20|...|1',
562 ],
563 'plain text; -5|...|1' => [
564 '...tion.',
565 $plainText, '-5|...|1',
566 ],
567 'plain text; -49|...|1' => [
568 '...the original version of the crop function.',
569 $plainText, '-49|...|1',
570 ],
571 'text with markup; 11|...' => [
572 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
573 . chr(229) . 'r...</a></strong>',
574 $textWithMarkup, '11|...',
575 ],
576 'text with markup; 13|...' => [
577 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
578 . chr(229) . 'rh' . chr(248) . '...</a></strong>',
579 $textWithMarkup, '13|...',
580 ],
581 'text with markup; 14|...' => [
582 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
583 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
584 $textWithMarkup, '14|...',
585 ],
586 'text with markup; 15|...' => [
587 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
588 . chr(229) . 'rh' . chr(248) . 'j</a> ...</strong>',
589 $textWithMarkup, '15|...',
590 ],
591 'text with markup; 29|...' => [
592 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
593 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> '
594 . 'th...',
595 $textWithMarkup, '29|...',
596 ],
597 'text with markup; -58|...' => [
598 '<strong><a href="mailto:kasper@typo3.org">...h' . chr(248)
599 . 'j</a> implemented</strong> the original version of the crop '
600 . 'function.',
601 $textWithMarkup, '-58|...',
602 ],
603 'text with markup 4|...|1' => [
604 '<strong><a href="mailto:kasper@typo3.org">Kasp...</a>'
605 . '</strong>',
606 $textWithMarkup, '4|...|1',
607 ],
608 'text with markup; 11|...|1' => [
609 '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
610 . '</strong>',
611 $textWithMarkup, '11|...|1',
612 ],
613 'text with markup; 13|...|1' => [
614 '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
615 . '</strong>',
616 $textWithMarkup, '13|...|1',
617 ],
618 'text with markup; 14|...|1' => [
619 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
620 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
621 $textWithMarkup, '14|...|1',
622 ],
623 'text with markup; 15|...|1' => [
624 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
625 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
626 $textWithMarkup, '15|...|1',
627 ],
628 'text with markup; 29|...|1' => [
629 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
630 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong>...',
631 $textWithMarkup, '29|...|1',
632 ],
633 'text with markup; -66|...|1' => [
634 '<strong><a href="mailto:kasper@typo3.org">...Sk' . chr(229)
635 . 'rh' . chr(248) . 'j</a> implemented</strong> the original v'
636 . 'ersion of the crop function.',
637 $textWithMarkup, '-66|...|1',
638 ],
639 'text with entities 9|...' => [
640 'Kasper Sk...',
641 $textWithEntities, '9|...',
642 ],
643 'text with entities 10|...' => [
644 'Kasper Sk&aring;...',
645 $textWithEntities, '10|...',
646 ],
647 'text with entities 11|...' => [
648 'Kasper Sk&aring;r...',
649 $textWithEntities, '11|...',
650 ],
651 'text with entities 13|...' => [
652 'Kasper Sk&aring;rh&oslash;...',
653 $textWithEntities, '13|...',
654 ],
655 'text with entities 14|...' => [
656 'Kasper Sk&aring;rh&oslash;j...',
657 $textWithEntities, '14|...',
658 ],
659 'text with entities 15|...' => [
660 'Kasper Sk&aring;rh&oslash;j ...',
661 $textWithEntities, '15|...',
662 ],
663 'text with entities 16|...' => [
664 'Kasper Sk&aring;rh&oslash;j i...',
665 $textWithEntities, '16|...',
666 ],
667 'text with entities -57|...' => [
668 '...j implemented the; original version of the crop function.',
669 $textWithEntities, '-57|...',
670 ],
671 'text with entities -58|...' => [
672 '...&oslash;j implemented the; original version of the crop '
673 . 'function.',
674 $textWithEntities, '-58|...',
675 ],
676 'text with entities -59|...' => [
677 '...h&oslash;j implemented the; original version of the crop '
678 . 'function.',
679 $textWithEntities, '-59|...',
680 ],
681 'text with entities 4|...|1' => [
682 'Kasp...',
683 $textWithEntities, '4|...|1',
684 ],
685 'text with entities 9|...|1' => [
686 'Kasper...',
687 $textWithEntities, '9|...|1',
688 ],
689 'text with entities 10|...|1' => [
690 'Kasper...',
691 $textWithEntities, '10|...|1',
692 ],
693 'text with entities 11|...|1' => [
694 'Kasper...',
695 $textWithEntities, '11|...|1',
696 ],
697 'text with entities 13|...|1' => [
698 'Kasper...',
699 $textWithEntities, '13|...|1',
700 ],
701 'text with entities 14|...|1' => [
702 'Kasper Sk&aring;rh&oslash;j...',
703 $textWithEntities, '14|...|1',
704 ],
705 'text with entities 15|...|1' => [
706 'Kasper Sk&aring;rh&oslash;j...',
707 $textWithEntities, '15|...|1',
708 ],
709 'text with entities 16|...|1' => [
710 'Kasper Sk&aring;rh&oslash;j...',
711 $textWithEntities, '16|...|1',
712 ],
713 'text with entities -57|...|1' => [
714 '...implemented the; original version of the crop function.',
715 $textWithEntities, '-57|...|1',
716 ],
717 'text with entities -58|...|1' => [
718 '...implemented the; original version of the crop function.',
719 $textWithEntities, '-58|...|1',
720 ],
721 'text with entities -59|...|1' => [
722 '...implemented the; original version of the crop function.',
723 $textWithEntities, '-59|...|1',
724 ],
725 'text with dash in html-element 28|...|1' => [
726 'Some text with a link to <link email.address@example.org - '
727 . 'mail "Open email window">my...</link>',
728 'Some text with a link to <link email.address@example.org - m'
729 . 'ail "Open email window">my email.address@example.org<'
730 . '/link> and text after it',
731 '28|...|1',
732 ],
733 'html elements with dashes in attributes' => [
734 '<em data-foo="x">foobar</em>foo',
735 '<em data-foo="x">foobar</em>foobaz',
736 '9',
737 ],
738 'html elements with iframe embedded 24|...|1' => [
739 'Text with iframe <iframe src="//what.ever/"></iframe> and...',
740 'Text with iframe <iframe src="//what.ever/">'
741 . '</iframe> and text after it',
742 '24|...|1',
743 ],
744 'html elements with script tag embedded 24|...|1' => [
745 'Text with script <script>alert(\'foo\');</script> and...',
746 'Text with script <script>alert(\'foo\');</script> '
747 . 'and text after it',
748 '24|...|1',
749 ],
750 'text with linebreaks' => [
751 "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\ns"
752 . 'ed diam nonumy eirmod tempor invidunt ut labore e'
753 . 't dolore magna',
754 $textWithLinebreaks, '121',
755 ],
756 ];
757 }
758
759 /**
760 * Check if cropHTML works properly.
761 *
762 * @test
763 * @dataProvider cropHTMLDataProvider
764 * @param string $expect The expected cropped output.
765 * @param string $content The given input.
766 * @param string $conf The given configuration.
767 * @return void
768 */
769 public function cropHTML($expect, $content, $conf)
770 {
771 $this->handleCharset($content, $expect);
772 $this->assertSame($expect,
773 $this->subject->cropHTML($content, $conf));
774 }
775
776 /**
777 * Data provider for round
778 *
779 * @return array [$expect, $contet, $conf]
780 */
781 public function roundDataProvider()
782 {
783 return [
784 // floats
785 'down' => [1.0, 1.11, []],
786 'up' => [2.0, 1.51, []],
787 'rounds up from x.50' => [2.0, 1.50, []],
788 'down with decimals' => [0.12, 0.1231, ['decimals' => 2]],
789 'up with decimals' => [0.13, 0.1251, ['decimals' => 2]],
790 'ceil' => [1.0, 0.11, ['roundType' => 'ceil']],
791 'ceil does not accept decimals' => [
792 1.0, 0.111, [
793 'roundType' => 'ceil',
794 'decimals' => 2,
795 ],
796 ],
797 'floor' => [2.0, 2.99, ['roundType' => 'floor']],
798 'floor does not accept decimals' => [
799 2.0, 2.999, [
800 'roundType' => 'floor',
801 'decimals' => 2,
802 ],
803 ],
804 'round, down' => [1.0, 1.11, ['roundType' => 'round']],
805 'round, up' => [2.0, 1.55, ['roundType' => 'round']],
806 'round does accept decimals' => [
807 5.56, 5.5555, [
808 'roundType' => 'round',
809 'decimals' => 2,
810 ],
811 ],
812 // strings
813 'emtpy string' => [0.0, '', []],
814 'word string' => [0.0, 'word', []],
815 'float string' => [1.0, '1.123456789', []],
816 // other types
817 'null' => [0.0, null, []],
818 'false' => [0.0, false, []],
819 'true' => [1.0, true, []]
820 ];
821 }
822
823 /**
824 * Check if round works properly
825 *
826 * Show:
827 *
828 * - Different types of input are casted to float.
829 * - Configuration ceil rounds like ceil().
830 * - Configuration floor rounds like floor().
831 * - Otherwise rounds like round() and decimals can be applied.
832 * - Always returns float.
833 *
834 * @param float $expected The expected output.
835 * @param mixed $content The given content.
836 * @param array $conf The given configuration of 'round.'.
837 * @return void
838 * @dataProvider roundDataProvider
839 * @test
840 */
841 public function round($expect, $content, $conf)
842 {
843 $this->assertSame($expect,
844 $this->subject->_call('round', $content, $conf));
845 }
846
847 /**
848 * @test
849 */
850 public function recursiveStdWrapProperlyRendersBasicString()
851 {
852 $stdWrapConfiguration = array(
853 'noTrimWrap' => '|| 123|',
854 'stdWrap.' => array(
855 'wrap' => '<b>|</b>'
856 )
857 );
858 $this->assertSame(
859 '<b>Test</b> 123',
860 $this->subject->stdWrap('Test', $stdWrapConfiguration)
861 );
862 }
863
864 /**
865 * @test
866 */
867 public function recursiveStdWrapIsOnlyCalledOnce()
868 {
869 $stdWrapConfiguration = array(
870 'append' => 'TEXT',
871 'append.' => array(
872 'data' => 'register:Counter'
873 ),
874 'stdWrap.' => array(
875 'append' => 'LOAD_REGISTER',
876 'append.' => array(
877 'Counter.' => array(
878 'prioriCalc' => 'intval',
879 'cObject' => 'TEXT',
880 'cObject.' => array(
881 'data' => 'register:Counter',
882 'wrap' => '|+1',
883 )
884 )
885 )
886 )
887 );
888 $this->assertSame(
889 'Counter:1',
890 $this->subject->stdWrap('Counter:', $stdWrapConfiguration)
891 );
892 }
893
894 /**
895 * Data provider for numberFormat.
896 *
897 * @return array [$expect, $content, $conf]
898 */
899 public function numberFormatDataProvider()
900 {
901 return [
902 'testing decimals' => [
903 '0.80', 0.8,
904 ['decimals' => 2]
905 ],
906 'testing decimals with input as string' => [
907 '0.80', '0.8',
908 ['decimals' => 2]
909 ],
910 'testing dec_point' => [
911 '0,8', 0.8,
912 ['decimals' => 1, 'dec_point' => ',']
913 ],
914 'testing thousands_sep' => [
915 '1.000', 999.99,
916 [
917 'decimals' => 0,
918 'thousands_sep.' => ['char' => 46]
919 ]
920 ],
921 'testing mixture' => [
922 '1.281.731,5', 1281731.45,
923 [
924 'decimals' => 1,
925 'dec_point.' => ['char' => 44],
926 'thousands_sep.' => ['char' => 46]
927 ]
928 ]
929 ];
930 }
931
932 /**
933 * Check if numberFormat works properly.
934 *
935 * @dataProvider numberFormatDataProvider
936 * @test
937 */
938 public function numberFormat($expects, $content, $conf)
939 {
940 $this->assertSame($expects,
941 $this->subject->numberFormat($content, $conf));
942 }
943
944 /**
945 * Data provider replacement
946 *
947 * @return array [$expect, $content, $conf]
948 */
949 public function replacementDataProvider()
950 {
951 return [
952 'multiple replacements, including regex' => [
953 'There is an animal, an animal and an animal around the block! Yeah!',
954 'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
955 [
956 '20.' => [
957 'search' => '_',
958 'replace.' => ['char' => '32']
959 ],
960 '120.' => [
961 'search' => 'in da hood',
962 'replace' => 'around the block'
963 ],
964 '130.' => [
965 'search' => '#a (Cat|Dog|Tiger)#i',
966 'replace' => 'an animal',
967 'useRegExp' => '1'
968 ]
969 ]
970 ],
971 'replacement with optionSplit, normal pattern' => [
972 'There1is2a3cat,3a3dog3and3a3tiger3in3da3hood!3Yeah!',
973 'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
974 [
975 '10.' => [
976 'search' => '_',
977 'replace' => '1 || 2 || 3',
978 'useOptionSplitReplace' => '1'
979 ]
980 ]
981 ],
982 'replacement with optionSplit, using regex' => [
983 'There is a tiny cat, a midsized dog and a big tiger in da hood! Yeah!',
984 'There is a cat, a dog and a tiger in da hood! Yeah!',
985 [
986 '10.' => [
987 'search' => '#(a) (Cat|Dog|Tiger)#i',
988 'replace' => '${1} tiny ${2} || ${1} midsized ${2} || ${1} big ${2}',
989 'useOptionSplitReplace' => '1',
990 'useRegExp' => '1'
991 ]
992 ]
993 ]
994 ];
995 }
996
997 /**
998 * Check if stdWrap.replacement and all of its properties work properly
999 *
1000 * @test
1001 * @dataProvider replacementDataProvider
1002 * @param string $content The given input.
1003 * @param string $expects The expected result.
1004 * @param array $conf The given configuration.
1005 * @return void
1006 */
1007 public function replacement($expects, $content, $conf)
1008 {
1009 $this->assertSame($expects,
1010 $this->subject->_call('replacement', $content, $conf));
1011 }
1012
1013 /**
1014 * Data provider for the getQuery test
1015 *
1016 * @return array multi-dimensional array with the second level like this:
1017 * @see getQuery
1018 */
1019 public function getQueryDataProvider()
1020 {
1021 $data = array(
1022 'testing empty conf' => array(
1023 'tt_content',
1024 array(),
1025 array(
1026 'SELECT' => '*'
1027 )
1028 ),
1029 'testing #17284: adding uid/pid for workspaces' => array(
1030 'tt_content',
1031 array(
1032 'selectFields' => 'header,bodytext'
1033 ),
1034 array(
1035 'SELECT' => 'header,bodytext, tt_content.uid as uid, tt_content.pid as pid, tt_content.t3ver_state as t3ver_state'
1036 )
1037 ),
1038 'testing #17284: no need to add' => array(
1039 'tt_content',
1040 array(
1041 'selectFields' => 'tt_content.*'
1042 ),
1043 array(
1044 'SELECT' => 'tt_content.*'
1045 )
1046 ),
1047 'testing #17284: no need to add #2' => array(
1048 'tt_content',
1049 array(
1050 'selectFields' => '*'
1051 ),
1052 array(
1053 'SELECT' => '*'
1054 )
1055 ),
1056 'testing #29783: joined tables, prefix tablename' => array(
1057 'tt_content',
1058 array(
1059 'selectFields' => 'tt_content.header,be_users.username',
1060 'join' => 'be_users ON tt_content.cruser_id = be_users.uid'
1061 ),
1062 array(
1063 'SELECT' => 'tt_content.header,be_users.username, tt_content.uid as uid, tt_content.pid as pid, tt_content.t3ver_state as t3ver_state'
1064 )
1065 ),
1066 'testing #34152: single count(*), add nothing' => array(
1067 'tt_content',
1068 array(
1069 'selectFields' => 'count(*)'
1070 ),
1071 array(
1072 'SELECT' => 'count(*)'
1073 )
1074 ),
1075 'testing #34152: single max(crdate), add nothing' => array(
1076 'tt_content',
1077 array(
1078 'selectFields' => 'max(crdate)'
1079 ),
1080 array(
1081 'SELECT' => 'max(crdate)'
1082 )
1083 ),
1084 'testing #34152: single min(crdate), add nothing' => array(
1085 'tt_content',
1086 array(
1087 'selectFields' => 'min(crdate)'
1088 ),
1089 array(
1090 'SELECT' => 'min(crdate)'
1091 )
1092 ),
1093 'testing #34152: single sum(is_siteroot), add nothing' => array(
1094 'tt_content',
1095 array(
1096 'selectFields' => 'sum(is_siteroot)'
1097 ),
1098 array(
1099 'SELECT' => 'sum(is_siteroot)'
1100 )
1101 ),
1102 'testing #34152: single avg(crdate), add nothing' => array(
1103 'tt_content',
1104 array(
1105 'selectFields' => 'avg(crdate)'
1106 ),
1107 array(
1108 'SELECT' => 'avg(crdate)'
1109 )
1110 )
1111 );
1112 return $data;
1113 }
1114
1115 /**
1116 * Check if sanitizeSelectPart works as expected
1117 *
1118 * @dataProvider getQueryDataProvider
1119 * @test
1120 */
1121 public function getQuery($table, $conf, $expected)
1122 {
1123 $GLOBALS['TCA'] = array(
1124 'pages' => array(
1125 'ctrl' => array(
1126 'enablecolumns' => array(
1127 'disabled' => 'hidden'
1128 )
1129 )
1130 ),
1131 'tt_content' => array(
1132 'ctrl' => array(
1133 'enablecolumns' => array(
1134 'disabled' => 'hidden'
1135 ),
1136 'versioningWS' => true
1137 )
1138 ),
1139 );
1140 $result = $this->subject->getQuery($table, $conf, true);
1141 foreach ($expected as $field => $value) {
1142 $this->assertEquals($value, $result[$field]);
1143 }
1144 }
1145
1146 /**
1147 * @test
1148 */
1149 public function getQueryCallsGetTreeListWithNegativeValuesIfRecursiveIsSet()
1150 {
1151 $GLOBALS['TCA'] = array(
1152 'pages' => array(
1153 'ctrl' => array(
1154 'enablecolumns' => array(
1155 'disabled' => 'hidden'
1156 )
1157 )
1158 ),
1159 'tt_content' => array(
1160 'ctrl' => array(
1161 'enablecolumns' => array(
1162 'disabled' => 'hidden'
1163 )
1164 )
1165 ),
1166 );
1167 $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, array('getTreeList'));
1168 $this->subject->start(array(), 'tt_content');
1169 $conf = array(
1170 'recursive' => '15',
1171 'pidInList' => '16, -35'
1172 );
1173 $this->subject->expects($this->at(0))
1174 ->method('getTreeList')
1175 ->with(-16, 15)
1176 ->will($this->returnValue('15,16'));
1177 $this->subject->expects($this->at(1))
1178 ->method('getTreeList')
1179 ->with(-35, 15)
1180 ->will($this->returnValue('15,35'));
1181 $this->subject->getQuery('tt_content', $conf, true);
1182 }
1183
1184 /**
1185 * @test
1186 */
1187 public function getQueryCallsGetTreeListWithCurrentPageIfThisIsSet()
1188 {
1189 $GLOBALS['TCA'] = array(
1190 'pages' => array(
1191 'ctrl' => array(
1192 'enablecolumns' => array(
1193 'disabled' => 'hidden'
1194 )
1195 )
1196 ),
1197 'tt_content' => array(
1198 'ctrl' => array(
1199 'enablecolumns' => array(
1200 'disabled' => 'hidden'
1201 )
1202 )
1203 ),
1204 );
1205 $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, array('getTreeList'));
1206 $GLOBALS['TSFE']->id = 27;
1207 $this->subject->start(array(), 'tt_content');
1208 $conf = array(
1209 'pidInList' => 'this',
1210 'recursive' => '4'
1211 );
1212 $this->subject->expects($this->once())
1213 ->method('getTreeList')
1214 ->with(-27)
1215 ->will($this->returnValue('27'));
1216 $this->subject->getQuery('tt_content', $conf, true);
1217 }
1218
1219 /**
1220 * Data provider for calcAge.
1221 *
1222 * @return array [$expect, $timestamp, $labels]
1223 */
1224 public function calcAgeDataProvider()
1225 {
1226 return [
1227 'minutes' => [
1228 '2 min', 120, ' min| hrs| days| yrs',
1229 ],
1230 'hours' => [
1231 '2 hrs', 7200, ' min| hrs| days| yrs',
1232 ],
1233 'days' => [
1234 '7 days', 604800, ' min| hrs| days| yrs',
1235 ],
1236 'day with provided singular labels' => [
1237 '1 day', 86400, ' min| hrs| days| yrs| min| hour| day| year',
1238 ],
1239 'years' => [
1240 '45 yrs', 1417997800, ' min| hrs| days| yrs',
1241 ],
1242 'different labels' => [
1243 '2 Minutes', 120, ' Minutes| Hrs| Days| Yrs',
1244 ],
1245 'negative values' => [
1246 '-7 days', -604800, ' min| hrs| days| yrs',
1247 ],
1248 'default label values for wrong label input' => [
1249 '2 min', 121, 10,
1250 ],
1251 'default singular label values for wrong label input' => [
1252 '1 year', 31536000, 10,
1253 ]
1254 ];
1255 }
1256
1257 /**
1258 * Check if calcAge works properly.
1259 *
1260 * @test
1261 * @dataProvider calcAgeDataProvider
1262 * @param int $expect
1263 * @param int $timestamp
1264 * @param string $labels
1265 * @return void
1266 */
1267 public function calcAge($expect, $timestamp, $labels)
1268 {
1269 $this->assertSame($expect,
1270 $this->subject->calcAge($timestamp, $labels));
1271 }
1272
1273 /**
1274 * @return array
1275 */
1276 public function stdWrapReturnsExpectationDataProvider()
1277 {
1278 return [
1279 'Prevent silent bool conversion' => [
1280 '1+1',
1281 [
1282 'prioriCalc.' => [
1283 'wrap' => '|',
1284 ],
1285 ],
1286 '1+1',
1287 ],
1288 ];
1289 }
1290
1291 /**
1292 * @param string $content
1293 * @param array $configuration
1294 * @param string $expectation
1295 * @dataProvider stdWrapReturnsExpectationDataProvider
1296 * @test
1297 */
1298 public function stdWrapReturnsExpectation($content, array $configuration, $expectation)
1299 {
1300 $this->assertSame($expectation, $this->subject->stdWrap($content, $configuration));
1301 }
1302
1303 /**
1304 * Data provider for substring
1305 *
1306 * @return array [$expect, $content, $conf]
1307 */
1308 public function substringDataProvider()
1309 {
1310 return [
1311 'sub -1' => ['g', 'substring', '-1'],
1312 'sub -1,0' => ['g', 'substring', '-1,0'],
1313 'sub -1,-1' => ['', 'substring', '-1,-1'],
1314 'sub -1,1' => ['g', 'substring', '-1,1'],
1315 'sub 0' => ['substring', 'substring', '0'],
1316 'sub 0,0' => ['substring', 'substring', '0,0'],
1317 'sub 0,-1' => ['substrin', 'substring', '0,-1'],
1318 'sub 0,1' => ['s', 'substring', '0,1'],
1319 'sub 1' => ['ubstring', 'substring', '1'],
1320 'sub 1,0' => ['ubstring', 'substring', '1,0'],
1321 'sub 1,-1' => ['ubstrin', 'substring', '1,-1'],
1322 'sub 1,1' => ['u', 'substring', '1,1'],
1323 'sub' => ['substring', 'substring', ''],
1324 ];
1325 }
1326
1327 /**
1328 * Check if substring works properly.
1329 *
1330 * @test
1331 * @dataProvider substringDataProvider
1332 * @param string $expect The expected output.
1333 * @param string $content The given input.
1334 * @param array $conf The given configutation.
1335 * @return void
1336 */
1337 public function substring($expect, $content, $conf)
1338 {
1339 $this->assertSame($expect, $this->subject->substring($content, $conf));
1340 }
1341
1342 ///////////////////////////////
1343 // Tests concerning getData()
1344 ///////////////////////////////
1345
1346 /**
1347 * @return array
1348 */
1349 public function getDataWithTypeGpDataProvider()
1350 {
1351 return array(
1352 'Value in get-data' => array('onlyInGet', 'GetValue'),
1353 'Value in post-data' => array('onlyInPost', 'PostValue'),
1354 'Value in post-data overriding get-data' => array('inGetAndPost', 'ValueInPost'),
1355 );
1356 }
1357
1358 /**
1359 * Checks if getData() works with type "gp"
1360 *
1361 * @test
1362 * @dataProvider getDataWithTypeGpDataProvider
1363 */
1364 public function getDataWithTypeGp($key, $expectedValue)
1365 {
1366 $_GET = array(
1367 'onlyInGet' => 'GetValue',
1368 'inGetAndPost' => 'ValueInGet',
1369 );
1370 $_POST = array(
1371 'onlyInPost' => 'PostValue',
1372 'inGetAndPost' => 'ValueInPost',
1373 );
1374 $this->assertEquals($expectedValue, $this->subject->getData('gp:' . $key));
1375 }
1376
1377 /**
1378 * Checks if getData() works with type "tsfe"
1379 *
1380 * @test
1381 */
1382 public function getDataWithTypeTsfe()
1383 {
1384 $this->assertEquals($GLOBALS['TSFE']->metaCharset, $this->subject->getData('tsfe:metaCharset'));
1385 }
1386
1387 /**
1388 * Checks if getData() works with type "getenv"
1389 *
1390 * @test
1391 */
1392 public function getDataWithTypeGetenv()
1393 {
1394 $envName = $this->getUniqueId('frontendtest');
1395 $value = $this->getUniqueId('someValue');
1396 putenv($envName . '=' . $value);
1397 $this->assertEquals($value, $this->subject->getData('getenv:' . $envName));
1398 }
1399
1400 /**
1401 * Checks if getData() works with type "getindpenv"
1402 *
1403 * @test
1404 */
1405 public function getDataWithTypeGetindpenv()
1406 {
1407 $this->subject->expects($this->once())->method('getEnvironmentVariable')
1408 ->with($this->equalTo('SCRIPT_FILENAME'))->will($this->returnValue('dummyPath'));
1409 $this->assertEquals('dummyPath', $this->subject->getData('getindpenv:SCRIPT_FILENAME'));
1410 }
1411
1412 /**
1413 * Checks if getData() works with type "field"
1414 *
1415 * @test
1416 */
1417 public function getDataWithTypeField()
1418 {
1419 $key = 'someKey';
1420 $value = 'someValue';
1421 $field = array($key => $value);
1422
1423 $this->assertEquals($value, $this->subject->getData('field:' . $key, $field));
1424 }
1425
1426 /**
1427 * Checks if getData() works with type "field" of the field content
1428 * is multi-dimensional (e.g. an array)
1429 *
1430 * @test
1431 */
1432 public function getDataWithTypeFieldAndFieldIsMultiDimensional()
1433 {
1434 $key = 'somekey|level1|level2';
1435 $value = 'somevalue';
1436 $field = array('somekey' => array('level1' => array('level2' => 'somevalue')));
1437
1438 $this->assertEquals($value, $this->subject->getData('field:' . $key, $field));
1439 }
1440
1441 /**
1442 * Basic check if getData gets the uid of a file object
1443 *
1444 * @test
1445 */
1446 public function getDataWithTypeFileReturnsUidOfFileObject()
1447 {
1448 $uid = $this->getUniqueId();
1449 $file = $this->createMock(File::class);
1450 $file->expects($this->once())->method('getUid')->will($this->returnValue($uid));
1451 $this->subject->setCurrentFile($file);
1452 $this->assertEquals($uid, $this->subject->getData('file:current:uid'));
1453 }
1454
1455 /**
1456 * Checks if getData() works with type "parameters"
1457 *
1458 * @test
1459 */
1460 public function getDataWithTypeParameters()
1461 {
1462 $key = $this->getUniqueId('someKey');
1463 $value = $this->getUniqueId('someValue');
1464 $this->subject->parameters[$key] = $value;
1465
1466 $this->assertEquals($value, $this->subject->getData('parameters:' . $key));
1467 }
1468
1469 /**
1470 * Checks if getData() works with type "register"
1471 *
1472 * @test
1473 */
1474 public function getDataWithTypeRegister()
1475 {
1476 $key = $this->getUniqueId('someKey');
1477 $value = $this->getUniqueId('someValue');
1478 $GLOBALS['TSFE']->register[$key] = $value;
1479
1480 $this->assertEquals($value, $this->subject->getData('register:' . $key));
1481 }
1482
1483 /**
1484 * Checks if getData() works with type "level"
1485 *
1486 * @test
1487 */
1488 public function getDataWithTypeLevel()
1489 {
1490 $rootline = array(
1491 0 => array('uid' => 1, 'title' => 'title1'),
1492 1 => array('uid' => 2, 'title' => 'title2'),
1493 2 => array('uid' => 3, 'title' => 'title3'),
1494 );
1495
1496 $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
1497 $this->assertEquals(2, $this->subject->getData('level'));
1498 }
1499
1500 /**
1501 * Checks if getData() works with type "global"
1502 *
1503 * @test
1504 */
1505 public function getDataWithTypeGlobal()
1506 {
1507 $this->assertEquals($GLOBALS['TSFE']->metaCharset, $this->subject->getData('global:TSFE|metaCharset'));
1508 }
1509
1510 /**
1511 * Checks if getData() works with type "leveltitle"
1512 *
1513 * @test
1514 */
1515 public function getDataWithTypeLeveltitle()
1516 {
1517 $rootline = array(
1518 0 => array('uid' => 1, 'title' => 'title1'),
1519 1 => array('uid' => 2, 'title' => 'title2'),
1520 2 => array('uid' => 3, 'title' => ''),
1521 );
1522
1523 $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
1524 $this->assertEquals('', $this->subject->getData('leveltitle:-1'));
1525 // since "title3" is not set, it will slide to "title2"
1526 $this->assertEquals('title2', $this->subject->getData('leveltitle:-1,slide'));
1527 }
1528
1529 /**
1530 * Checks if getData() works with type "levelmedia"
1531 *
1532 * @test
1533 */
1534 public function getDataWithTypeLevelmedia()
1535 {
1536 $rootline = array(
1537 0 => array('uid' => 1, 'title' => 'title1', 'media' => 'media1'),
1538 1 => array('uid' => 2, 'title' => 'title2', 'media' => 'media2'),
1539 2 => array('uid' => 3, 'title' => 'title3', 'media' => ''),
1540 );
1541
1542 $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
1543 $this->assertEquals('', $this->subject->getData('levelmedia:-1'));
1544 // since "title3" is not set, it will slide to "title2"
1545 $this->assertEquals('media2', $this->subject->getData('levelmedia:-1,slide'));
1546 }
1547
1548 /**
1549 * Checks if getData() works with type "leveluid"
1550 *
1551 * @test
1552 */
1553 public function getDataWithTypeLeveluid()
1554 {
1555 $rootline = array(
1556 0 => array('uid' => 1, 'title' => 'title1'),
1557 1 => array('uid' => 2, 'title' => 'title2'),
1558 2 => array('uid' => 3, 'title' => 'title3'),
1559 );
1560
1561 $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
1562 $this->assertEquals(3, $this->subject->getData('leveluid:-1'));
1563 // every element will have a uid - so adding slide doesn't really make sense, just for completeness
1564 $this->assertEquals(3, $this->subject->getData('leveluid:-1,slide'));
1565 }
1566
1567 /**
1568 * Checks if getData() works with type "levelfield"
1569 *
1570 * @test
1571 */
1572 public function getDataWithTypeLevelfield()
1573 {
1574 $rootline = array(
1575 0 => array('uid' => 1, 'title' => 'title1', 'testfield' => 'field1'),
1576 1 => array('uid' => 2, 'title' => 'title2', 'testfield' => 'field2'),
1577 2 => array('uid' => 3, 'title' => 'title3', 'testfield' => ''),
1578 );
1579
1580 $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
1581 $this->assertEquals('', $this->subject->getData('levelfield:-1,testfield'));
1582 $this->assertEquals('field2', $this->subject->getData('levelfield:-1,testfield,slide'));
1583 }
1584
1585 /**
1586 * Checks if getData() works with type "fullrootline"
1587 *
1588 * @test
1589 */
1590 public function getDataWithTypeFullrootline()
1591 {
1592 $rootline1 = array(
1593 0 => array('uid' => 1, 'title' => 'title1', 'testfield' => 'field1'),
1594 );
1595 $rootline2 = array(
1596 0 => array('uid' => 1, 'title' => 'title1', 'testfield' => 'field1'),
1597 1 => array('uid' => 2, 'title' => 'title2', 'testfield' => 'field2'),
1598 2 => array('uid' => 3, 'title' => 'title3', 'testfield' => 'field3'),
1599 );
1600
1601 $GLOBALS['TSFE']->tmpl->rootLine = $rootline1;
1602 $GLOBALS['TSFE']->rootLine = $rootline2;
1603 $this->assertEquals('field2', $this->subject->getData('fullrootline:-1,testfield'));
1604 }
1605
1606 /**
1607 * Checks if getData() works with type "date"
1608 *
1609 * @test
1610 */
1611 public function getDataWithTypeDate()
1612 {
1613 $format = 'Y-M-D';
1614 $defaultFormat = 'd/m Y';
1615
1616 $this->assertEquals(date($format, $GLOBALS['EXEC_TIME']), $this->subject->getData('date:' . $format));
1617 $this->assertEquals(date($defaultFormat, $GLOBALS['EXEC_TIME']), $this->subject->getData('date'));
1618 }
1619
1620 /**
1621 * Checks if getData() works with type "page"
1622 *
1623 * @test
1624 */
1625 public function getDataWithTypePage()
1626 {
1627 $uid = rand();
1628 $GLOBALS['TSFE']->page['uid'] = $uid;
1629 $this->assertEquals($uid, $this->subject->getData('page:uid'));
1630 }
1631
1632 /**
1633 * Checks if getData() works with type "current"
1634 *
1635 * @test
1636 */
1637 public function getDataWithTypeCurrent()
1638 {
1639 $key = $this->getUniqueId('someKey');
1640 $value = $this->getUniqueId('someValue');
1641 $this->subject->data[$key] = $value;
1642 $this->subject->currentValKey = $key;
1643 $this->assertEquals($value, $this->subject->getData('current'));
1644 }
1645
1646 /**
1647 * Checks if getData() works with type "db"
1648 *
1649 * @test
1650 */
1651 public function getDataWithTypeDb()
1652 {
1653 $dummyRecord = array('uid' => 5, 'title' => 'someTitle');
1654
1655 $GLOBALS['TSFE']->sys_page->expects($this->atLeastOnce())->method('getRawRecord')->with('tt_content', '106')->will($this->returnValue($dummyRecord));
1656 $this->assertEquals($dummyRecord['title'], $this->subject->getData('db:tt_content:106:title'));
1657 }
1658
1659 /**
1660 * Checks if getData() works with type "lll"
1661 *
1662 * @test
1663 */
1664 public function getDataWithTypeLll()
1665 {
1666 $key = $this->getUniqueId('someKey');
1667 $value = $this->getUniqueId('someValue');
1668 $language = $this->getUniqueId('someLanguage');
1669 $GLOBALS['TSFE']->LL_labels_cache[$language]['LLL:' . $key] = $value;
1670 $GLOBALS['TSFE']->lang = $language;
1671
1672 $this->assertEquals($value, $this->subject->getData('lll:' . $key));
1673 }
1674
1675 /**
1676 * Checks if getData() works with type "path"
1677 *
1678 * @test
1679 */
1680 public function getDataWithTypePath()
1681 {
1682 $filenameIn = $this->getUniqueId('someValue');
1683 $filenameOut = $this->getUniqueId('someValue');
1684 $this->templateServiceMock->expects($this->atLeastOnce())->method('getFileName')->with($filenameIn)->will($this->returnValue($filenameOut));
1685 $this->assertEquals($filenameOut, $this->subject->getData('path:' . $filenameIn));
1686 }
1687
1688 /**
1689 * Checks if getData() works with type "parentRecordNumber"
1690 *
1691 * @test
1692 */
1693 public function getDataWithTypeParentRecordNumber()
1694 {
1695 $recordNumber = rand();
1696 $this->subject->parentRecordNumber = $recordNumber;
1697 $this->assertEquals($recordNumber, $this->subject->getData('cobj:parentRecordNumber'));
1698 }
1699
1700 /**
1701 * Checks if getData() works with type "debug:rootLine"
1702 *
1703 * @test
1704 */
1705 public function getDataWithTypeDebugRootline()
1706 {
1707 $rootline = array(
1708 0 => array('uid' => 1, 'title' => 'title1'),
1709 1 => array('uid' => 2, 'title' => 'title2'),
1710 2 => array('uid' => 3, 'title' => ''),
1711 );
1712 $expectedResult = 'array(3items)0=>array(2items)uid=>1(integer)title=>"title1"(6chars)1=>array(2items)uid=>2(integer)title=>"title2"(6chars)2=>array(2items)uid=>3(integer)title=>""(0chars)';
1713 $GLOBALS['TSFE']->tmpl->rootLine = $rootline;
1714
1715 DebugUtility::useAnsiColor(false);
1716 $result = $this->subject->getData('debug:rootLine');
1717 $cleanedResult = str_replace("\r", '', $result);
1718 $cleanedResult = str_replace("\n", '', $cleanedResult);
1719 $cleanedResult = str_replace("\t", '', $cleanedResult);
1720 $cleanedResult = str_replace(' ', '', $cleanedResult);
1721
1722 $this->assertEquals($expectedResult, $cleanedResult);
1723 }
1724
1725 /**
1726 * Checks if getData() works with type "debug:fullRootLine"
1727 *
1728 * @test
1729 */
1730 public function getDataWithTypeDebugFullRootline()
1731 {
1732 $rootline = array(
1733 0 => array('uid' => 1, 'title' => 'title1'),
1734 1 => array('uid' => 2, 'title' => 'title2'),
1735 2 => array('uid' => 3, 'title' => ''),
1736 );
1737 $expectedResult = 'array(3items)0=>array(2items)uid=>1(integer)title=>"title1"(6chars)1=>array(2items)uid=>2(integer)title=>"title2"(6chars)2=>array(2items)uid=>3(integer)title=>""(0chars)';
1738 $GLOBALS['TSFE']->rootLine = $rootline;
1739
1740 DebugUtility::useAnsiColor(false);
1741 $result = $this->subject->getData('debug:fullRootLine');
1742 $cleanedResult = str_replace("\r", '', $result);
1743 $cleanedResult = str_replace("\n", '', $cleanedResult);
1744 $cleanedResult = str_replace("\t", '', $cleanedResult);
1745 $cleanedResult = str_replace(' ', '', $cleanedResult);
1746
1747 $this->assertEquals($expectedResult, $cleanedResult);
1748 }
1749
1750 /**
1751 * Checks if getData() works with type "debug:data"
1752 *
1753 * @test
1754 */
1755 public function getDataWithTypeDebugData()
1756 {
1757 $key = $this->getUniqueId('someKey');
1758 $value = $this->getUniqueId('someValue');
1759 $this->subject->data = array($key => $value);
1760
1761 $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1762
1763 DebugUtility::useAnsiColor(false);
1764 $result = $this->subject->getData('debug:data');
1765 $cleanedResult = str_replace("\r", '', $result);
1766 $cleanedResult = str_replace("\n", '', $cleanedResult);
1767 $cleanedResult = str_replace("\t", '', $cleanedResult);
1768 $cleanedResult = str_replace(' ', '', $cleanedResult);
1769
1770 $this->assertEquals($expectedResult, $cleanedResult);
1771 }
1772
1773 /**
1774 * Checks if getData() works with type "debug:register"
1775 *
1776 * @test
1777 */
1778 public function getDataWithTypeDebugRegister()
1779 {
1780 $key = $this->getUniqueId('someKey');
1781 $value = $this->getUniqueId('someValue');
1782 $GLOBALS['TSFE']->register = array($key => $value);
1783
1784 $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1785
1786 DebugUtility::useAnsiColor(false);
1787 $result = $this->subject->getData('debug:register');
1788 $cleanedResult = str_replace("\r", '', $result);
1789 $cleanedResult = str_replace("\n", '', $cleanedResult);
1790 $cleanedResult = str_replace("\t", '', $cleanedResult);
1791 $cleanedResult = str_replace(' ', '', $cleanedResult);
1792
1793 $this->assertEquals($expectedResult, $cleanedResult);
1794 }
1795
1796 /**
1797 * Checks if getData() works with type "data:page"
1798 *
1799 * @test
1800 */
1801 public function getDataWithTypeDebugPage()
1802 {
1803 $uid = rand();
1804 $GLOBALS['TSFE']->page = array('uid' => $uid);
1805
1806 $expectedResult = 'array(1item)uid=>' . $uid . '(integer)';
1807
1808 DebugUtility::useAnsiColor(false);
1809 $result = $this->subject->getData('debug:page');
1810 $cleanedResult = str_replace("\r", '', $result);
1811 $cleanedResult = str_replace("\n", '', $cleanedResult);
1812 $cleanedResult = str_replace("\t", '', $cleanedResult);
1813 $cleanedResult = str_replace(' ', '', $cleanedResult);
1814
1815 $this->assertEquals($expectedResult, $cleanedResult);
1816 }
1817
1818 /**
1819 * @test
1820 */
1821 public function getTreeListReturnsChildPageUids()
1822 {
1823 $GLOBALS['TYPO3_DB']->expects($this->any())->method('exec_SELECTgetSingleRow')->with('treelist')->will($this->returnValue(null));
1824 $GLOBALS['TSFE']->sys_page
1825 ->expects($this->any())
1826 ->method('getRawRecord')
1827 ->will(
1828 $this->onConsecutiveCalls(
1829 array('uid' => 17),
1830 array('uid' => 321),
1831 array('uid' => 719),
1832 array('uid' => 42)
1833 )
1834 );
1835
1836 $GLOBALS['TSFE']->sys_page->expects($this->any())->method('getMountPointInfo')->will($this->returnValue(null));
1837 $GLOBALS['TYPO3_DB']
1838 ->expects($this->any())
1839 ->method('exec_SELECTgetRows')
1840 ->will(
1841 $this->onConsecutiveCalls(
1842 array(
1843 array('uid' => 321)
1844 ),
1845 array(
1846 array('uid' => 719)
1847 ),
1848 array(
1849 array('uid' => 42)
1850 )
1851 )
1852 );
1853 // 17 = pageId, 5 = recursionLevel, 0 = begin (entry to recursion, internal), TRUE = do not check enable fields
1854 // 17 is positive, we expect 17 NOT to be included in result
1855 $result = $this->subject->getTreeList(17, 5, 0, true);
1856 $expectedResult = '42,719,321';
1857 $this->assertEquals($expectedResult, $result);
1858 }
1859
1860 /**
1861 * @test
1862 */
1863 public function getTreeListReturnsChildPageUidsAndOriginalPidForNegativeValue()
1864 {
1865 $GLOBALS['TYPO3_DB']->expects($this->any())->method('exec_SELECTgetSingleRow')->with('treelist')->will($this->returnValue(null));
1866 $GLOBALS['TSFE']->sys_page
1867 ->expects($this->any())
1868 ->method('getRawRecord')
1869 ->will(
1870 $this->onConsecutiveCalls(
1871 array('uid' => 17),
1872 array('uid' => 321),
1873 array('uid' => 719),
1874 array('uid' => 42)
1875 )
1876 );
1877
1878 $GLOBALS['TSFE']->sys_page->expects($this->any())->method('getMountPointInfo')->will($this->returnValue(null));
1879 $GLOBALS['TYPO3_DB']
1880 ->expects($this->any())
1881 ->method('exec_SELECTgetRows')
1882 ->will(
1883 $this->onConsecutiveCalls(
1884 array(
1885 array('uid' => 321)
1886 ),
1887 array(
1888 array('uid' => 719)
1889 ),
1890 array(
1891 array('uid' => 42)
1892 )
1893 )
1894 );
1895 // 17 = pageId, 5 = recursionLevel, 0 = begin (entry to recursion, internal), TRUE = do not check enable fields
1896 // 17 is negative, we expect 17 to be included in result
1897 $result = $this->subject->getTreeList(-17, 5, 0, true);
1898 $expectedResult = '42,719,321,17';
1899 $this->assertEquals($expectedResult, $result);
1900 }
1901
1902 /**
1903 * @test
1904 */
1905 public function aTagParamsHasLeadingSpaceIfNotEmpty()
1906 {
1907 $aTagParams = $this->subject->getATagParams(array('ATagParams' => 'data-test="testdata"'));
1908 $this->assertEquals(' data-test="testdata"', $aTagParams);
1909 }
1910
1911 /**
1912 * @test
1913 */
1914 public function aTagParamsHaveSpaceBetweenLocalAndGlobalParams()
1915 {
1916 $GLOBALS['TSFE']->ATagParams = 'data-global="dataglobal"';
1917 $aTagParams = $this->subject->getATagParams(array('ATagParams' => 'data-test="testdata"'));
1918 $this->assertEquals(' data-global="dataglobal" data-test="testdata"', $aTagParams);
1919 }
1920
1921 /**
1922 * @test
1923 */
1924 public function aTagParamsHasNoLeadingSpaceIfEmpty()
1925 {
1926 // make sure global ATagParams are empty
1927 $GLOBALS['TSFE']->ATagParams = '';
1928 $aTagParams = $this->subject->getATagParams(array('ATagParams' => ''));
1929 $this->assertEquals('', $aTagParams);
1930 }
1931
1932 /**
1933 * @return array
1934 */
1935 public function getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFoundDataProvider()
1936 {
1937 return array(
1938 array(null, null),
1939 array('', null),
1940 array('', array()),
1941 array('fooo', array('foo' => 'bar'))
1942 );
1943 }
1944
1945 /**
1946 * Make sure that the rendering falls back to the classic <img style if nothing else is found
1947 *
1948 * @test
1949 * @dataProvider getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFoundDataProvider
1950 * @param string $key
1951 * @param array $configuration
1952 */
1953 public function getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFound($key, $configuration)
1954 {
1955 $defaultImgTagTemplate = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1956 $result = $this->subject->getImageTagTemplate($key, $configuration);
1957 $this->assertEquals($result, $defaultImgTagTemplate);
1958 }
1959
1960 /**
1961 * @return array
1962 */
1963 public function getImageTagTemplateReturnTemplateElementIdentifiedByKeyDataProvider()
1964 {
1965 return array(
1966 array(
1967 'foo',
1968 array(
1969 'layout.' => array(
1970 'foo.' => array(
1971 'element' => '<img src="###SRC###" srcset="###SOURCES###" ###PARAMS### ###ALTPARAMS### ###FOOBAR######SELFCLOSINGTAGSLASH###>'
1972 )
1973 )
1974 ),
1975 '<img src="###SRC###" srcset="###SOURCES###" ###PARAMS### ###ALTPARAMS### ###FOOBAR######SELFCLOSINGTAGSLASH###>'
1976 )
1977
1978 );
1979 }
1980
1981 /**
1982 * Assure if a layoutKey and layout is given the selected layout is returned
1983 *
1984 * @test
1985 * @dataProvider getImageTagTemplateReturnTemplateElementIdentifiedByKeyDataProvider
1986 * @param string $key
1987 * @param array $configuration
1988 * @param string $expectation
1989 */
1990 public function getImageTagTemplateReturnTemplateElementIdentifiedByKey($key, $configuration, $expectation)
1991 {
1992 $result = $this->subject->getImageTagTemplate($key, $configuration);
1993 $this->assertEquals($result, $expectation);
1994 }
1995
1996 /**
1997 * @return array
1998 */
1999 public function getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefinedDataProvider()
2000 {
2001 return array(
2002 array(null, null, null),
2003 array('foo', null, null),
2004 array('foo', array('sourceCollection.' => 1), 'bar')
2005 );
2006 }
2007
2008 /**
2009 * Make sure the source collection is empty if no valid configuration or source collection is defined
2010 *
2011 * @test
2012 * @dataProvider getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefinedDataProvider
2013 * @param string $layoutKey
2014 * @param array $configuration
2015 * @param string $file
2016 */
2017 public function getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefined($layoutKey, $configuration, $file)
2018 {
2019 $result = $this->subject->getImageSourceCollection($layoutKey, $configuration, $file);
2020 $this->assertSame($result, '');
2021 }
2022
2023 /**
2024 * Make sure the generation of subimages calls the generation of the subimages and uses the layout -> source template
2025 *
2026 * @test
2027 */
2028 public function getImageSourceCollectionRendersDefinedSources()
2029 {
2030 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
2031 $cObj = $this->getMockBuilder(ContentObjectRenderer::class)
2032 ->setMethods(array('stdWrap', 'getImgResource'))
2033 ->getMock();
2034
2035 $cObj->start(array(), 'tt_content');
2036
2037 $layoutKey = 'test';
2038
2039 $configuration = array(
2040 'layoutKey' => 'test',
2041 'layout.' => array(
2042 'test.' => array(
2043 'element' => '<img ###SRC### ###SRCCOLLECTION### ###SELFCLOSINGTAGSLASH###>',
2044 'source' => '---###SRC###---'
2045 )
2046 ),
2047 'sourceCollection.' => array(
2048 '1.' => array(
2049 'width' => '200'
2050 )
2051 )
2052 );
2053
2054 $file = 'testImageName';
2055
2056 // Avoid calling of stdWrap
2057 $cObj
2058 ->expects($this->any())
2059 ->method('stdWrap')
2060 ->will($this->returnArgument(0));
2061
2062 // Avoid calling of imgResource
2063 $cObj
2064 ->expects($this->exactly(1))
2065 ->method('getImgResource')
2066 ->with($this->equalTo('testImageName'))
2067 ->will($this->returnValue(array(100, 100, null, 'bar')));
2068
2069 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
2070
2071 $this->assertEquals('---bar---', $result);
2072 }
2073
2074 /**
2075 * Data provider for the getImageSourceCollectionRendersDefinedLayoutKeyDefault test
2076 *
2077 * @return array multi-dimensional array with the second level like this:
2078 * @see getImageSourceCollectionRendersDefinedLayoutKeyDefault
2079 */
2080 public function getImageSourceCollectionRendersDefinedLayoutKeyDataDefaultProvider()
2081 {
2082 /**
2083 * @see css_styled_content/static/setup.txt
2084 */
2085 $sourceCollectionArray = array(
2086 'small.' => array(
2087 'width' => 200,
2088 'srcsetCandidate' => '600w',
2089 'mediaQuery' => '(max-device-width: 600px)',
2090 'dataKey' => 'small',
2091 ),
2092 'smallRetina.' => array(
2093 'if.directReturn' => 0,
2094 'width' => 200,
2095 'pixelDensity' => '2',
2096 'srcsetCandidate' => '600w 2x',
2097 'mediaQuery' => '(max-device-width: 600px) AND (min-resolution: 192dpi)',
2098 'dataKey' => 'smallRetina',
2099 )
2100 );
2101 return array(
2102 array(
2103 'default',
2104 array(
2105 'layoutKey' => 'default',
2106 'layout.' => array(
2107 'default.' => array(
2108 'element' => '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>',
2109 'source' => ''
2110 )
2111 ),
2112 'sourceCollection.' => $sourceCollectionArray
2113 )
2114 ),
2115 );
2116 }
2117
2118 /**
2119 * Make sure the generation of subimages renders the expected HTML Code for the sourceset
2120 *
2121 * @test
2122 * @dataProvider getImageSourceCollectionRendersDefinedLayoutKeyDataDefaultProvider
2123 * @param string $layoutKey
2124 * @param array $configuration
2125 */
2126 public function getImageSourceCollectionRendersDefinedLayoutKeyDefault($layoutKey, $configuration)
2127 {
2128 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
2129 $cObj = $this->getMockBuilder(ContentObjectRenderer::class)
2130 ->setMethods(array('stdWrap', 'getImgResource'))
2131 ->getMock();
2132
2133 $cObj->start(array(), 'tt_content');
2134
2135 $file = 'testImageName';
2136
2137 // Avoid calling of stdWrap
2138 $cObj
2139 ->expects($this->any())
2140 ->method('stdWrap')
2141 ->will($this->returnArgument(0));
2142
2143 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
2144
2145 $this->assertEmpty($result);
2146 }
2147
2148 /**
2149 * Data provider for the getImageSourceCollectionRendersDefinedLayoutKeyData test
2150 *
2151 * @return array multi-dimensional array with the second level like this:
2152 * @see getImageSourceCollectionRendersDefinedLayoutKeyData
2153 */
2154 public function getImageSourceCollectionRendersDefinedLayoutKeyDataDataProvider()
2155 {
2156 /**
2157 * @see css_styled_content/static/setup.txt
2158 */
2159 $sourceCollectionArray = array(
2160 'small.' => array(
2161 'width' => 200,
2162 'srcsetCandidate' => '600w',
2163 'mediaQuery' => '(max-device-width: 600px)',
2164 'dataKey' => 'small',
2165 ),
2166 'smallRetina.' => array(
2167 'if.directReturn' => 1,
2168 'width' => 200,
2169 'pixelDensity' => '2',
2170 'srcsetCandidate' => '600w 2x',
2171 'mediaQuery' => '(max-device-width: 600px) AND (min-resolution: 192dpi)',
2172 'dataKey' => 'smallRetina',
2173 )
2174 );
2175 return array(
2176 array(
2177 'srcset',
2178 array(
2179 'layoutKey' => 'srcset',
2180 'layout.' => array(
2181 'srcset.' => array(
2182 'element' => '<img src="###SRC###" srcset="###SOURCECOLLECTION###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2183 'source' => '|*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###'
2184 )
2185 ),
2186 'sourceCollection.' => $sourceCollectionArray
2187 ),
2188 'xhtml_strict',
2189 'bar-file.jpg 600w,bar-file.jpg 600w 2x',
2190 ),
2191 array(
2192 'picture',
2193 array(
2194 'layoutKey' => 'picture',
2195 'layout.' => array(
2196 'picture.' => array(
2197 'element' => '<picture>###SOURCECOLLECTION###<img src="###SRC###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###></picture>',
2198 'source' => '<source src="###SRC###" media="###MEDIAQUERY###"###SELFCLOSINGTAGSLASH###>'
2199 )
2200 ),
2201 'sourceCollection.' => $sourceCollectionArray,
2202 ),
2203 'xhtml_strict',
2204 '<source src="bar-file.jpg" media="(max-device-width: 600px)" /><source src="bar-file.jpg" media="(max-device-width: 600px) AND (min-resolution: 192dpi)" />',
2205 ),
2206 array(
2207 'picture',
2208 array(
2209 'layoutKey' => 'picture',
2210 'layout.' => array(
2211 'picture.' => array(
2212 'element' => '<picture>###SOURCECOLLECTION###<img src="###SRC###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###></picture>',
2213 'source' => '<source src="###SRC###" media="###MEDIAQUERY###"###SELFCLOSINGTAGSLASH###>'
2214 )
2215 ),
2216 'sourceCollection.' => $sourceCollectionArray,
2217 ),
2218 '',
2219 '<source src="bar-file.jpg" media="(max-device-width: 600px)"><source src="bar-file.jpg" media="(max-device-width: 600px) AND (min-resolution: 192dpi)">',
2220 ),
2221 array(
2222 'data',
2223 array(
2224 'layoutKey' => 'data',
2225 'layout.' => array(
2226 'data.' => array(
2227 'element' => '<img src="###SRC###" ###SOURCECOLLECTION### ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2228 'source' => 'data-###DATAKEY###="###SRC###"'
2229 )
2230 ),
2231 'sourceCollection.' => $sourceCollectionArray
2232 ),
2233 'xhtml_strict',
2234 'data-small="bar-file.jpg"data-smallRetina="bar-file.jpg"',
2235 ),
2236 );
2237 }
2238
2239 /**
2240 * Make sure the generation of subimages renders the expected HTML Code for the sourceset
2241 *
2242 * @test
2243 * @dataProvider getImageSourceCollectionRendersDefinedLayoutKeyDataDataProvider
2244 * @param string $layoutKey
2245 * @param array $configuration
2246 * @param string $xhtmlDoctype
2247 * @param string $expectedHtml
2248 */
2249 public function getImageSourceCollectionRendersDefinedLayoutKeyData($layoutKey, $configuration, $xhtmlDoctype, $expectedHtml)
2250 {
2251 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
2252 $cObj = $this->getMockBuilder(ContentObjectRenderer::class)
2253 ->setMethods(array('stdWrap', 'getImgResource'))
2254 ->getMock();
2255
2256 $cObj->start(array(), 'tt_content');
2257
2258 $file = 'testImageName';
2259
2260 $GLOBALS['TSFE']->xhtmlDoctype = $xhtmlDoctype;
2261
2262 // Avoid calling of stdWrap
2263 $cObj
2264 ->expects($this->any())
2265 ->method('stdWrap')
2266 ->will($this->returnArgument(0));
2267
2268 // Avoid calling of imgResource
2269 $cObj
2270 ->expects($this->exactly(2))
2271 ->method('getImgResource')
2272 ->with($this->equalTo('testImageName'))
2273 ->will($this->returnValue(array(100, 100, null, 'bar-file.jpg')));
2274
2275 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
2276
2277 $this->assertEquals($expectedHtml, $result);
2278 }
2279
2280 /**
2281 * Make sure the hook in get sourceCollection is called
2282 *
2283 * @test
2284 */
2285 public function getImageSourceCollectionHookCalled()
2286 {
2287 $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class,
2288 array('getResourceFactory', 'stdWrap', 'getImgResource')
2289 );
2290 $this->subject->start(array(), 'tt_content');
2291
2292 // Avoid calling stdwrap and getImgResource
2293 $this->subject->expects($this->any())
2294 ->method('stdWrap')
2295 ->will($this->returnArgument(0));
2296
2297 $this->subject->expects($this->any())
2298 ->method('getImgResource')
2299 ->will($this->returnValue(array(100, 100, null, 'bar-file.jpg')));
2300
2301 $resourceFactory = $this->createMock(ResourceFactory::class);
2302 $this->subject->expects($this->any())->method('getResourceFactory')->will($this->returnValue($resourceFactory));
2303
2304 $className = $this->getUniqueId('tx_coretest_getImageSourceCollectionHookCalled');
2305 $getImageSourceCollectionHookMock = $this->getMockBuilder(
2306 ContentObjectOneSourceCollectionHookInterface::class)
2307 ->setMethods(array('getOneSourceCollection'))
2308 ->setMockClassName($className)
2309 ->getMock();
2310 GeneralUtility::addInstance($className, $getImageSourceCollectionHookMock);
2311 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'][] = $className;
2312
2313 $getImageSourceCollectionHookMock
2314 ->expects($this->exactly(1))
2315 ->method('getOneSourceCollection')
2316 ->will($this->returnCallback(array($this, 'isGetOneSourceCollectionCalledCallback')));
2317
2318 $configuration = array(
2319 'layoutKey' => 'data',
2320 'layout.' => array(
2321 'data.' => array(
2322 'element' => '<img src="###SRC###" ###SOURCECOLLECTION### ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2323 'source' => 'data-###DATAKEY###="###SRC###"'
2324 )
2325 ),
2326 'sourceCollection.' => array(
2327 'small.' => array(
2328 'width' => 200,
2329 'srcsetCandidate' => '600w',
2330 'mediaQuery' => '(max-device-width: 600px)',
2331 'dataKey' => 'small',
2332 ),
2333 ),
2334 );
2335
2336 $result = $this->subject->getImageSourceCollection('data', $configuration, $this->getUniqueId('testImage-'));
2337
2338 $this->assertSame($result, 'isGetOneSourceCollectionCalledCallback');
2339 }
2340
2341 /**
2342 * Handles the arguments that have been sent to the getImgResource hook.
2343 *
2344 * @param array $sourceRenderConfiguration
2345 * @param array $sourceConfiguration
2346 * @param $oneSourceCollection
2347 * @param $parent
2348 * @return string
2349 * @see getImageSourceCollectionHookCalled
2350 */
2351 public function isGetOneSourceCollectionCalledCallback($sourceRenderConfiguration, $sourceConfiguration, $oneSourceCollection, $parent)
2352 {
2353 $this->assertTrue(is_array($sourceRenderConfiguration));
2354 $this->assertTrue(is_array($sourceConfiguration));
2355 return 'isGetOneSourceCollectionCalledCallback';
2356 }
2357
2358 /**
2359 * @param string $expected The expected URL
2360 * @param string $url The URL to parse and manipulate
2361 * @param array $configuration The configuration array
2362 * @test
2363 * @dataProvider forceAbsoluteUrlReturnsCorrectAbsoluteUrlDataProvider
2364 */
2365 public function forceAbsoluteUrlReturnsCorrectAbsoluteUrl($expected, $url, array $configuration)
2366 {
2367 // Force hostname
2368 $this->subject->expects($this->any())->method('getEnvironmentVariable')->will($this->returnValueMap(
2369 array(
2370 array('HTTP_HOST', 'localhost'),
2371 array('TYPO3_SITE_PATH', '/'),
2372 )
2373 ));
2374 $GLOBALS['TSFE']->absRefPrefix = '';
2375
2376 $this->assertEquals($expected, $this->subject->_call('forceAbsoluteUrl', $url, $configuration));
2377 }
2378
2379 /**
2380 * @return array The test data for forceAbsoluteUrlReturnsAbsoluteUrl
2381 */
2382 public function forceAbsoluteUrlReturnsCorrectAbsoluteUrlDataProvider()
2383 {
2384 return array(
2385 'Missing forceAbsoluteUrl leaves URL untouched' => array(
2386 'foo',
2387 'foo',
2388 array()
2389 ),
2390 'Absolute URL stays unchanged' => array(
2391 'http://example.org/',
2392 'http://example.org/',
2393 array(
2394 'forceAbsoluteUrl' => '1'
2395 )
2396 ),
2397 'Absolute URL stays unchanged 2' => array(
2398 'http://example.org/resource.html',
2399 'http://example.org/resource.html',
2400 array(
2401 'forceAbsoluteUrl' => '1'
2402 )
2403 ),
2404 'Scheme and host w/o ending slash stays unchanged' => array(
2405 'http://example.org',
2406 'http://example.org',
2407 array(
2408 'forceAbsoluteUrl' => '1'
2409 )
2410 ),
2411 'Scheme can be forced' => array(
2412 'typo3://example.org',
2413 'http://example.org',
2414 array(
2415 'forceAbsoluteUrl' => '1',
2416 'forceAbsoluteUrl.' => array(
2417 'scheme' => 'typo3'
2418 )
2419 )
2420 ),
2421 'Relative path old-style' => array(
2422 'http://localhost/fileadmin/dummy.txt',
2423 '/fileadmin/dummy.txt',
2424 array(
2425 'forceAbsoluteUrl' => '1',
2426 )
2427 ),
2428 'Relative path' => array(
2429 'http://localhost/fileadmin/dummy.txt',
2430 'fileadmin/dummy.txt',
2431 array(
2432 'forceAbsoluteUrl' => '1',
2433 )
2434 ),
2435 'Scheme can be forced with pseudo-relative path' => array(
2436 'typo3://localhost/fileadmin/dummy.txt',
2437 '/fileadmin/dummy.txt',
2438 array(
2439 'forceAbsoluteUrl' => '1',
2440 'forceAbsoluteUrl.' => array(
2441 'scheme' => 'typo3'
2442 )
2443 )
2444 ),
2445 'Hostname only is not treated as valid absolute URL' => array(
2446 'http://localhost/example.org',
2447 'example.org',
2448 array(
2449 'forceAbsoluteUrl' => '1'
2450 )
2451 ),
2452 'Scheme and host is added to local file path' => array(
2453 'typo3://localhost/fileadmin/my.pdf',
2454 'fileadmin/my.pdf',
2455 array(
2456 'forceAbsoluteUrl' => '1',
2457 'forceAbsoluteUrl.' => array(
2458 'scheme' => 'typo3'
2459 )
2460 )
2461 )
2462 );
2463 }
2464
2465 /**
2466 * @test
2467 */
2468 public function renderingContentObjectThrowsException()
2469 {
2470 $this->expectException(\LogicException::class);
2471 $this->expectExceptionCode(1414513947);
2472 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2473 $this->subject->render($contentObjectFixture, array());
2474 }
2475
2476 /**
2477 * @test
2478 */
2479 public function exceptionHandlerIsEnabledByDefaultInProductionContext()
2480 {
2481 $backupApplicationContext = GeneralUtility::getApplicationContext();
2482 Fixtures\GeneralUtilityFixture::setApplicationContext(new ApplicationContext('Production'));
2483
2484 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2485 $this->subject->render($contentObjectFixture, array());
2486
2487 Fixtures\GeneralUtilityFixture::setApplicationContext($backupApplicationContext);
2488 }
2489
2490 /**
2491 * @test
2492 */
2493 public function renderingContentObjectDoesNotThrowExceptionIfExceptionHandlerIsConfiguredLocally()
2494 {
2495 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2496
2497 $configuration = array(
2498 'exceptionHandler' => '1'
2499 );
2500 $this->subject->render($contentObjectFixture, $configuration);
2501 }
2502
2503 /**
2504 * @test
2505 */
2506 public function renderingContentObjectDoesNotThrowExceptionIfExceptionHandlerIsConfiguredGlobally()
2507 {
2508 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2509
2510 $this->frontendControllerMock->config['config']['contentObjectExceptionHandler'] = '1';
2511 $this->subject->render($contentObjectFixture, array());
2512 }
2513
2514 /**
2515 * @test
2516 */
2517 public function globalExceptionHandlerConfigurationCanBeOverriddenByLocalConfiguration()
2518 {
2519 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2520 $this->expectException(\LogicException::class);
2521 $this->expectExceptionCode(1414513947);
2522 $this->frontendControllerMock->config['config']['contentObjectExceptionHandler'] = '1';
2523 $configuration = array(
2524 'exceptionHandler' => '0'
2525 );
2526 $this->subject->render($contentObjectFixture, $configuration);
2527 }
2528
2529 /**
2530 * @test
2531 */
2532 public function renderedErrorMessageCanBeCustomized()
2533 {
2534 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2535
2536 $configuration = array(
2537 'exceptionHandler' => '1',
2538 'exceptionHandler.' => array(
2539 'errorMessage' => 'New message for testing',
2540 )
2541 );
2542
2543 $this->assertSame('New message for testing', $this->subject->render($contentObjectFixture, $configuration));
2544 }
2545
2546 /**
2547 * @test
2548 */
2549 public function localConfigurationOverridesGlobalConfiguration()
2550 {
2551 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2552
2553 $this->frontendControllerMock
2554 ->config['config']['contentObjectExceptionHandler.'] = array(
2555 'errorMessage' => 'Global message for testing',
2556 );
2557 $configuration = array(
2558 'exceptionHandler' => '1',
2559 'exceptionHandler.' => array(
2560 'errorMessage' => 'New message for testing',
2561 )
2562 );
2563
2564 $this->assertSame('New message for testing', $this->subject->render($contentObjectFixture, $configuration));
2565 }
2566
2567 /**
2568 * @test
2569 */
2570 public function specificExceptionsCanBeIgnoredByExceptionHandler()
2571 {
2572 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2573
2574 $configuration = array(
2575 'exceptionHandler' => '1',
2576 'exceptionHandler.' => array(
2577 'ignoreCodes.' => array('10.' => '1414513947'),
2578 )
2579 );
2580 $this->expectException(\LogicException::class);
2581 $this->expectExceptionCode(1414513947);
2582 $this->subject->render($contentObjectFixture, $configuration);
2583 }
2584
2585 /**
2586 * @return \PHPUnit_Framework_MockObject_MockObject | AbstractContentObject
2587 */
2588 protected function createContentObjectThrowingExceptionFixture()
2589 {
2590 $contentObjectFixture = $this->getMockBuilder(AbstractContentObject::class)
2591 ->setConstructorArgs(array($this->subject))
2592 ->getMock();
2593 $contentObjectFixture->expects($this->once())
2594 ->method('render')
2595 ->willReturnCallback(function () {
2596 throw new \LogicException('Exception during rendering', 1414513947);
2597 });
2598 return $contentObjectFixture;
2599 }
2600
2601 /**
2602 * @test
2603 */
2604 public function forceAbsoluteUrlReturnsCorrectAbsoluteUrlWithSubfolder()
2605 {
2606 // Force hostname and subfolder
2607 $this->subject->expects($this->any())->method('getEnvironmentVariable')->will($this->returnValueMap(
2608 array(
2609 array('HTTP_HOST', 'localhost'),
2610 array('TYPO3_SITE_PATH', '/subfolder/'),
2611 )
2612 ));
2613
2614 $expected = 'http://localhost/subfolder/fileadmin/my.pdf';
2615 $url = 'fileadmin/my.pdf';
2616 $configuration = array(
2617 'forceAbsoluteUrl' => '1'
2618 );
2619
2620 $this->assertEquals($expected, $this->subject->_call('forceAbsoluteUrl', $url, $configuration));
2621 }
2622
2623 /**
2624 * @return array
2625 */
2626 protected function getLibParseTarget()
2627 {
2628 return array(
2629 'override' => '',
2630 'override.' => array(
2631 'if.' => array(
2632 'isTrue.' => array(
2633 'data' => 'TSFE:dtdAllowsFrames',
2634 ),
2635 ),
2636 ),
2637 );
2638 }
2639
2640 /**
2641 * @return array
2642 */
2643 protected function getLibParseFunc()
2644 {
2645 return array(
2646 'makelinks' => '1',
2647 'makelinks.' => array(
2648 'http.' => array(
2649 'keep' => '{$styles.content.links.keep}',
2650 'extTarget' => '',
2651 'extTarget.' => $this->getLibParseTarget(),
2652 'mailto.' => array(
2653 'keep' => 'path',
2654 ),
2655 ),
2656 ),
2657 'tags' => array(
2658 'link' => 'TEXT',
2659 'link.' => array(
2660 'current' => '1',
2661 'typolink.' => array(
2662 'parameter.' => array(
2663 'data' => 'parameters : allParams',
2664 ),
2665 'extTarget.' => $this->getLibParseTarget(),
2666 'target.' => $this->getLibParseTarget(),
2667 ),
2668 'parseFunc.' => array(
2669 'constants' => '1',
2670 ),
2671 ),
2672 ),
2673
2674 'allowTags' => 'a, abbr, acronym, address, article, aside, b, bdo, big, blockquote, br, caption, center, cite, code, col, colgroup, dd, del, dfn, dl, div, dt, em, font, footer, header, h1, h2, h3, h4, h5, h6, hr, i, img, ins, kbd, label, li, link, meta, nav, ol, p, pre, q, samp, sdfield, section, small, span, strike, strong, style, sub, sup, table, thead, tbody, tfoot, td, th, tr, title, tt, u, ul, var',
2675 'denyTags' => '*',
2676 'sword' => '<span class="csc-sword">|</span>',
2677 'constants' => '1',
2678 'nonTypoTagStdWrap.' => array(
2679 'HTMLparser' => '1',
2680 'HTMLparser.' => array(
2681 'keepNonMatchedTags' => '1',
2682 'htmlSpecialChars' => '2',
2683 ),
2684 ),
2685 );
2686 }
2687
2688 /**
2689 * @return array
2690 */
2691 protected function getLibParseFunc_RTE()
2692 {
2693 return array(
2694 'parseFunc' => '',
2695 'parseFunc.' => array(
2696 'allowTags' => 'a, abbr, acronym, address, article, aside, b, bdo, big, blockquote, br, caption, center, cite, code, col, colgroup, dd, del, dfn, dl, div, dt, em, font, footer, header, h1, h2, h3, h4, h5, h6, hr, i, img, ins, kbd, label, li, link, meta, nav, ol, p, pre, q, samp, sdfield, section, small, span, strike, strong, style, sub, sup, table, thead, tbody, tfoot, td, th, tr, title, tt, u, ul, var',
2697 'constants' => '1',
2698 'denyTags' => '*',
2699 'externalBlocks' => 'article, aside, blockquote, div, dd, dl, footer, header, nav, ol, section, table, ul',
2700 'externalBlocks.' => array(
2701 'article.' => array(
2702 'callRecursive' => '1',
2703 'stripNL' => '1',
2704 ),
2705 'aside.' => array(
2706 'callRecursive' => '1',
2707 'stripNL' => '1',
2708 ),
2709 'blockquote.' => array(
2710 'callRecursive' => '1',
2711 'stripNL' => '1',
2712 ),
2713 'dd.' => array(
2714 'callRecursive' => '1',
2715 'stripNL' => '1',
2716 ),
2717 'div.' => array(
2718 'callRecursive' => '1',
2719 'stripNL' => '1',
2720 ),
2721 'dl.' => array(
2722 'callRecursive' => '1',
2723 'stripNL' => '1',
2724 ),
2725 'footer.' => array(
2726 'callRecursive' => '1',
2727 'stripNL' => '1',
2728 ),
2729 'header.' => array(
2730 'callRecursive' => '1',
2731 'stripNL' => '1',
2732 ),
2733 'nav.' => array(
2734 'callRecursive' => '1',
2735 'stripNL' => '1',
2736 ),
2737 'ol.' => array(
2738 'callRecursive' => '1',
2739 'stripNL' => '1',
2740 ),
2741 'section.' => array(
2742 'callRecursive' => '1',
2743 'stripNL' => '1',
2744 ),
2745 'table.' => array(
2746 'HTMLtableCells' => '1',
2747 'HTMLtableCells.' => array(
2748 'addChr10BetweenParagraphs' => '1',
2749 'default.' => array(
2750 'stdWrap.' => array(
2751 'parseFunc' => '=< lib.parseFunc_RTE',
2752 'parseFunc.' => array(
2753 'nonTypoTagStdWrap.' => array(
2754 'encapsLines.' => array(
2755 'nonWrappedTag' => '',
2756 ),
2757 ),
2758 ),
2759 ),
2760 ),
2761 ),
2762 'stdWrap.' => array(
2763 'HTMLparser' => '1',
2764 'HTMLparser.' => array(
2765 'keepNonMatchedTags' => '1',
2766 'tags.' => array(
2767 'table.' => array(
2768 'fixAttrib.' => array(
2769 'class.' => array(
2770 'always' => '1',
2771 'default' => 'contenttable',
2772 'list' => 'contenttable',
2773 ),
2774 ),
2775 ),
2776 ),
2777 ),
2778 ),
2779 'stripNL' => '1',
2780 ),
2781 'ul.' => array(
2782 'callRecursive' => '1',
2783 'stripNL' => '1',
2784 ),
2785 ),
2786 'makelinks' => '1',
2787 'makelinks.' => array(
2788 'http.' => array(
2789 'extTarget.' => array(
2790 'override' => '_blank',
2791 'override.' => array(
2792 'if.' => array(
2793 'isTrue.' => array(
2794 'data' => 'TSFE:dtdAllowsFrames',
2795 ),
2796 ),
2797 ),
2798 ),
2799 'keep' => 'path',
2800 ),
2801 ),
2802 'nonTypoTagStdWrap.' => array(
2803 'encapsLines.' => array(
2804 'addAttributes.' => array(
2805 'P.' => array(
2806 'class' => 'bodytext',
2807 'class.' => array(
2808 'setOnly' => 'blank',
2809 ),
2810 ),
2811 ),
2812 'encapsTagList' => 'p,pre,h1,h2,h3,h4,h5,h6,hr,dt,li',
2813 'innerStdWrap_all.' => array(
2814 'ifBlank' => '&nbsp;',
2815 ),
2816 'nonWrappedTag' => 'P',
2817 'remapTag.' => array(
2818 'DIV' => 'P',
2819 ),
2820 ),
2821 'HTMLparser' => '1',
2822 'HTMLparser.' => array(
2823 'htmlSpecialChars' => '2',
2824 'keepNonMatchedTags' => '1',
2825 ),
2826 ),
2827 'sword' => '<span class="csc-sword">|</span>',
2828 'tags.' => array(
2829 'link' => 'TEXT',
2830 'link.' => array(
2831 'current' => '1',
2832 'parseFunc.' => array(
2833 'constants' => '1',
2834 ),
2835 'typolink.' => array(
2836 'extTarget.' => array(
2837 'override' => '',
2838 'override.' => array(
2839 'if.' => array(
2840 'isTrue.' => array(
2841 'data' => 'TSFE:dtdAllowsFrames',
2842 ),
2843 ),
2844 ),
2845 ),
2846 'parameter.' => array(
2847 'data' => 'parameters : allParams',
2848 ),
2849 'target.' => array(
2850 'override' => '',
2851 'override.' => array(
2852 'if.' => array(
2853 'isTrue.' => array(
2854 'data' => 'TSFE:dtdAllowsFrames',
2855 ),
2856 ),
2857 ),
2858 ),
2859 ),
2860 ),
2861 ),
2862 ),
2863 );
2864 }
2865
2866 /**
2867 * @return array
2868 */
2869 public function _parseFuncReturnsCorrectHtmlDataProvider()
2870 {
2871 return array(
2872 'Text without tag is wrapped with <p> tag' => array(
2873 'Text without tag',
2874 $this->getLibParseFunc_RTE(),
2875 '<p class="bodytext">Text without tag</p>',
2876 ),
2877 'Text wrapped with <p> tag remains the same' => array(
2878 '<p class="myclass">Text with &lt;p&gt; tag</p>',
2879 $this->getLibParseFunc_RTE(),
2880 '<p class="myclass">Text with &lt;p&gt; tag</p>',
2881 ),
2882 'Text with absolute external link' => array(
2883 'Text with <link http://example.com/foo/>external link</link>',
2884 $this->getLibParseFunc_RTE(),
2885 '<p class="bodytext">Text with <a href="http://example.com/foo/">external link</a></p>',
2886 ),
2887 );
2888 }
2889
2890 /**
2891 * @test
2892 * @dataProvider _parseFuncReturnsCorrectHtmlDataProvider
2893 * @param string $value
2894 * @param array $configuration
2895 * @param string $expectedResult
2896 */
2897 public function stdWrap_parseFuncReturnsParsedHtml($value, $configuration, $expectedResult)
2898 {
2899 $this->assertEquals($expectedResult, $this->subject->stdWrap_parseFunc($value, $configuration));
2900 }
2901
2902 /**
2903 * @return array
2904 */
2905 public function detectLinkTypeFromLinkParameterDataProvider()
2906 {
2907 return [
2908 'Domain only' => [
2909 'example.com',
2910 'url'
2911 ],
2912 'URL without a file' => [
2913 'http://example.com',
2914 'url'
2915 ],
2916 'URL with schema and a file' => [
2917 'http://example.com/index.php',
2918 'url'
2919 ],
2920 'URL with a file but without a schema' => [
2921 'example.com/index.php',
2922 'url'
2923 ],
2924 'file' => [
2925 '/index.php',
2926 'file'
2927 ],
2928 ];
2929 }
2930
2931 /**
2932 * @test
2933 * @param string $linkParameter
2934 * @param string $expectedResult
2935 * @dataProvider detectLinkTypeFromLinkParameterDataProvider
2936 */
2937 public function detectLinkTypeFromLinkParameter($linkParameter, $expectedResult)
2938 {
2939 /** @var TemplateService|\PHPUnit_Framework_MockObject_MockObject $templateServiceObjectMock */
2940 $templateServiceObjectMock = $this->getMockBuilder(TemplateService::class)
2941 ->setMethods(array('dummy'))
2942 ->getMock();
2943 $templateServiceObjectMock->setup = array(
2944 'lib.' => array(
2945 'parseFunc.' => $this->getLibParseFunc(),
2946 ),
2947 );
2948 /** @var TypoScriptFrontendController|\PHPUnit_Framework_MockObject_MockObject $typoScriptFrontendControllerMockObject */
2949 $typoScriptFrontendControllerMockObject = $this->createMock(TypoScriptFrontendController::class);
2950 $typoScriptFrontendControllerMockObject->config = array(
2951 'config' => array(),
2952 'mainScript' => 'index.php',
2953 );
2954 $typoScriptFrontendControllerMockObject->tmpl = $templateServiceObjectMock;
2955 $GLOBALS['TSFE'] = $typoScriptFrontendControllerMockObject;
2956 $this->subject->_set('typoScriptFrontendController', $typoScriptFrontendControllerMockObject);
2957
2958 $this->assertEquals($expectedResult, $this->subject->_call('detectLinkTypeFromLinkParameter', $linkParameter));
2959 }
2960
2961 /**
2962 * @return array
2963 */
2964 public function typolinkReturnsCorrectLinksForEmailsAndUrlsDataProvider()
2965 {
2966 return array(
2967 'Link to url' => array(
2968 'TYPO3',
2969 array(
2970 'parameter' => 'http://typo3.org',
2971 ),
2972 '<a href="http://typo3.org">TYPO3</a>',
2973 ),
2974 'Link to url without schema' => array(
2975 'TYPO3',
2976 array(
2977 'parameter' => 'typo3.org',