2 namespace TYPO3\CMS\Frontend\Tests\Unit\ContentObject
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use Prophecy\Argument
;
18 use TYPO3\CMS\Core\Cache\CacheManager
;
19 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
as CacheFrontendInterface
;
20 use TYPO3\CMS\Core\Core\ApplicationContext
;
21 use TYPO3\CMS\Core\Core\Environment
;
22 use TYPO3\CMS\Core\LinkHandling\LinkService
;
23 use TYPO3\CMS\Core\Log\Logger
;
24 use TYPO3\CMS\Core\Resource\Exception\InvalidPathException
;
25 use TYPO3\CMS\Core\Resource\File
;
26 use TYPO3\CMS\Core\Resource\ResourceFactory
;
27 use TYPO3\CMS\Core\Resource\ResourceStorage
;
28 use TYPO3\CMS\Core\TimeTracker\TimeTracker
;
29 use TYPO3\CMS\Core\TypoScript\TemplateService
;
30 use TYPO3\CMS\Core\Utility\DebugUtility
;
31 use TYPO3\CMS\Core\Utility\GeneralUtility
;
32 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
;
33 use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject
;
34 use TYPO3\CMS\Frontend\ContentObject\CaseContentObject
;
35 use TYPO3\CMS\Frontend\ContentObject\ContentContentObject
;
36 use TYPO3\CMS\Frontend\ContentObject\ContentObjectArrayContentObject
;
37 use TYPO3\CMS\Frontend\ContentObject\ContentObjectArrayInternalContentObject
;
38 use TYPO3\CMS\Frontend\ContentObject\ContentObjectOneSourceCollectionHookInterface
;
39 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
;
40 use TYPO3\CMS\Frontend\ContentObject\ContentObjectStdWrapHookInterface
;
41 use TYPO3\CMS\Frontend\ContentObject\EditPanelContentObject
;
42 use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException
;
43 use TYPO3\CMS\Frontend\ContentObject\FileContentObject
;
44 use TYPO3\CMS\Frontend\ContentObject\FilesContentObject
;
45 use TYPO3\CMS\Frontend\ContentObject\FluidTemplateContentObject
;
46 use TYPO3\CMS\Frontend\ContentObject\HierarchicalMenuContentObject
;
47 use TYPO3\CMS\Frontend\ContentObject\ImageContentObject
;
48 use TYPO3\CMS\Frontend\ContentObject\ImageResourceContentObject
;
49 use TYPO3\CMS\Frontend\ContentObject\LoadRegisterContentObject
;
50 use TYPO3\CMS\Frontend\ContentObject\RecordsContentObject
;
51 use TYPO3\CMS\Frontend\ContentObject\RestoreRegisterContentObject
;
52 use TYPO3\CMS\Frontend\ContentObject\ScalableVectorGraphicsContentObject
;
53 use TYPO3\CMS\Frontend\ContentObject\TemplateContentObject
;
54 use TYPO3\CMS\Frontend\ContentObject\TextContentObject
;
55 use TYPO3\CMS\Frontend\ContentObject\UserContentObject
;
56 use TYPO3\CMS\Frontend\ContentObject\UserInternalContentObject
;
57 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
;
58 use TYPO3\CMS\Frontend\Page\PageRepository
;
59 use TYPO3\TestingFramework\Core\Unit\UnitTestCase
;
64 class ContentObjectRendererTest
extends UnitTestCase
67 * Subject is not notice free, disable E_NOTICES
69 protected static $suppressNotices = true
;
72 * @var bool Reset singletons created by subject
74 protected $resetSingletonInstances = true
;
77 * @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
82 * @var \PHPUnit_Framework_MockObject_MockObject|TypoScriptFrontendController|\TYPO3\TestingFramework\Core\AccessibleObjectInterface
84 protected $frontendControllerMock;
87 * @var \PHPUnit_Framework_MockObject_MockObject|CacheFrontendInterface
89 protected $cacheFrontendMock;
92 * @var \PHPUnit_Framework_MockObject_MockObject|TemplateService
94 protected $templateServiceMock;
97 * Default content object name -> class name map, shipped with TYPO3 CMS
101 protected $contentObjectMap = [
102 'TEXT' => TextContentObject
::class,
103 'CASE' => CaseContentObject
::class,
104 'COBJ_ARRAY' => ContentObjectArrayContentObject
::class,
105 'COA' => ContentObjectArrayContentObject
::class,
106 'COA_INT' => ContentObjectArrayInternalContentObject
::class,
107 'USER' => UserContentObject
::class,
108 'USER_INT' => UserInternalContentObject
::class,
109 'FILE' => FileContentObject
::class,
110 'FILES' => FilesContentObject
::class,
111 'IMAGE' => ImageContentObject
::class,
112 'IMG_RESOURCE' => ImageResourceContentObject
::class,
113 'CONTENT' => ContentContentObject
::class,
114 'RECORDS' => RecordsContentObject
::class,
115 'HMENU' => HierarchicalMenuContentObject
::class,
116 'CASEFUNC' => CaseContentObject
::class,
117 'LOAD_REGISTER' => LoadRegisterContentObject
::class,
118 'RESTORE_REGISTER' => RestoreRegisterContentObject
::class,
119 'TEMPLATE' => TemplateContentObject
::class,
120 'FLUIDTEMPLATE' => FluidTemplateContentObject
::class,
121 'SVG' => ScalableVectorGraphicsContentObject
::class,
122 'EDITPANEL' => EditPanelContentObject
::class
128 protected function setUp()
130 $this->templateServiceMock
=
131 $this->getMockBuilder(TemplateService
::class)
132 ->setMethods(['getFileName', 'linkData'])->getMock();
133 $pageRepositoryMock =
134 $this->getAccessibleMock(PageRepository
::class, ['getRawRecord', 'getMountPointInfo']);
135 $this->frontendControllerMock
=
136 $this->getAccessibleMock(
137 TypoScriptFrontendController
::class,
143 $this->frontendControllerMock
->tmpl
= $this->templateServiceMock
;
144 $this->frontendControllerMock
->config
= [];
145 $this->frontendControllerMock
->page
= [];
146 $this->frontendControllerMock
->sys_page
= $pageRepositoryMock;
147 $GLOBALS['TSFE'] = $this->frontendControllerMock
;
149 $this->subject
= $this->getAccessibleMock(
150 ContentObjectRenderer
::class,
151 ['getResourceFactory', 'getEnvironmentVariable'],
152 [$this->frontendControllerMock
]
155 $logger = $this->prophesize(Logger
::class);
156 $this->subject
->setLogger($logger->reveal());
157 $this->subject
->setContentObjectClassMap($this->contentObjectMap
);
158 $this->subject
->start([], 'tt_content');
161 //////////////////////
163 //////////////////////
166 * @return TypoScriptFrontendController
168 protected function getFrontendController()
170 return $GLOBALS['TSFE'];
174 * Converts the subject and the expected result into utf-8.
176 * @param string $subject the subject, will be modified
177 * @param string $expected the expected result, will be modified
179 protected function handleCharset(&$subject, &$expected)
181 $subject = mb_convert_encoding($subject, 'utf-8', 'iso-8859-1');
182 $expected = mb_convert_encoding($expected, 'utf-8', 'iso-8859-1');
185 /////////////////////////////////////////////
186 // Tests concerning the getImgResource hook
187 /////////////////////////////////////////////
191 public function getImgResourceCallsGetImgResourcePostProcessHook()
193 $this->templateServiceMock
194 ->expects($this->atLeastOnce())
195 ->method('getFileName')
196 ->with('typo3/clear.gif')
197 ->will($this->returnValue('typo3/clear.gif'));
199 $resourceFactory = $this->createMock(ResourceFactory
::class);
200 $this->subject
->expects($this->any())->method('getResourceFactory')->will($this->returnValue($resourceFactory));
202 $className = $this->getUniqueId('tx_coretest');
203 $getImgResourceHookMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\ContentObject\ContentObjectGetImageResourceHookInterface
::class)
204 ->setMethods(['getImgResourcePostProcess'])
205 ->setMockClassName($className)
207 $getImgResourceHookMock
208 ->expects($this->once())
209 ->method('getImgResourcePostProcess')
210 ->will($this->returnCallback([$this, 'isGetImgResourceHookCalledCallback']));
211 $getImgResourceHookObjects = [$getImgResourceHookMock];
212 $this->subject
->_setRef('getImgResourceHookObjects', $getImgResourceHookObjects);
213 $this->subject
->getImgResource('typo3/clear.gif', []);
217 * Handles the arguments that have been sent to the getImgResource hook.
219 * @param string $file
220 * @param array $fileArray
221 * @param $imageResource
222 * @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $parent
224 * @see getImgResourceHookGetsCalled
226 public function isGetImgResourceHookCalledCallback($file, $fileArray, $imageResource, $parent)
228 $this->assertEquals('typo3/clear.gif', $file);
229 $this->assertEquals('typo3/clear.gif', $imageResource['origFile']);
230 $this->assertTrue(is_array($fileArray));
231 $this->assertTrue($parent instanceof ContentObjectRenderer
);
232 return $imageResource;
235 //////////////////////////////////////
236 // Tests related to getContentObject
237 //////////////////////////////////////
240 * Show registration of a class for a TypoScript object name and getting
241 * the registered content object is working.
243 * Prove is done by successfully creating an object based on the mapping.
244 * Note two conditions in contrast to other tests, where the creation
247 * 1. The type must be of AbstractContentObject.
248 * 2. Registration can only be done by public methods.
252 public function canRegisterAContentObjectClassForATypoScriptName()
254 $className = TextContentObject
::class;
255 $contentObjectName = 'TEST_TEXT';
256 $this->subject
->registerContentObjectClass(
260 $object = $this->subject
->getContentObject($contentObjectName);
261 $this->assertInstanceOf($className, $object);
265 * Show that setting of the class map and getting a registered content
268 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
271 public function canSetTheContentObjectClassMapAndGetARegisteredContentObject()
273 $className = TextContentObject
::class;
274 $contentObjectName = 'TEST_TEXT';
275 $classMap = [$contentObjectName => $className];
276 $this->subject
->setContentObjectClassMap($classMap);
277 $object = $this->subject
->getContentObject($contentObjectName);
278 $this->assertInstanceOf($className, $object);
282 * Show that the map is not set as an externally accessible reference.
284 * Prove is done by missing success when trying to use it this way.
286 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
289 public function canNotAccessInternalContentObjectMapByReference()
291 $className = TextContentObject
::class;
292 $contentObjectName = 'TEST_TEXT';
294 $this->subject
->setContentObjectClassMap($classMap);
295 $classMap[$contentObjectName] = $className;
296 $object = $this->subject
->getContentObject($contentObjectName);
297 $this->assertNull($object);
301 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
304 public function willReturnNullForUnregisteredObject()
306 $object = $this->subject
->getContentObject('FOO');
307 $this->assertNull($object);
311 * @see ContentObjectRendererTest::canRegisterAContentObjectClassForATypoScriptName
314 public function willThrowAnExceptionForARegisteredNonContentObject()
316 $this->expectException(ContentRenderingException
::class);
317 $this->subject
->registerContentObjectClass(
321 $this->subject
->getContentObject('STDCLASS');
325 * @return string[][] [[$name, $fullClassName],]
327 public function registersAllDefaultContentObjectsDataProvider(): array
330 foreach ($this->contentObjectMap
as $name => $className) {
331 $dataProvider[] = [$name, $className];
333 return $dataProvider;
337 * Prove that all content objects are registered and a class is available
341 * @dataProvider registersAllDefaultContentObjectsDataProvider
342 * @param string $objectName TypoScript name of content object
343 * @param string $className Expected class name
345 public function registersAllDefaultContentObjects(
350 is_subclass_of($className, AbstractContentObject
::class)
352 $object = $this->subject
->getContentObject($objectName);
353 $this->assertInstanceOf($className, $object);
356 /////////////////////////////////////////
357 // Tests concerning getQueryArguments()
358 /////////////////////////////////////////
362 public function getQueryArgumentsExcludesParameters()
364 $this->subject
->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
365 $this->returnValue('key1=value1&key2=value2&key3[key31]=value31&key3[key32][key321]=value321&key3[key32][key322]=value322')
367 $getQueryArgumentsConfiguration = [];
368 $getQueryArgumentsConfiguration['exclude'] = [];
369 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
370 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
371 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
372 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
373 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2&key3[key32][key322]=value322');
374 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
375 $this->assertEquals($expectedResult, $actualResult);
381 public function getQueryArgumentsExcludesGetParameters()
387 'key31' => 'value31',
389 'key321' => 'value321',
390 'key322' => 'value322'
394 $getQueryArgumentsConfiguration = [];
395 $getQueryArgumentsConfiguration['method'] = 'GET';
396 $getQueryArgumentsConfiguration['exclude'] = [];
397 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
398 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
399 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
400 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
401 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2&key3[key32][key322]=value322');
402 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
403 $this->assertEquals($expectedResult, $actualResult);
409 public function getQueryArgumentsOverrulesSingleParameter()
411 $this->subject
->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
412 $this->returnValue('key1=value1')
414 $getQueryArgumentsConfiguration = [];
415 $overruleArguments = [
416 // Should be overridden
417 'key1' => 'value1Overruled',
418 // Shouldn't be set: Parameter doesn't exist in source array and is not forced
419 'key2' => 'value2Overruled'
421 $expectedResult = '&key1=value1Overruled';
422 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments);
423 $this->assertEquals($expectedResult, $actualResult);
429 public function getQueryArgumentsOverrulesMultiDimensionalParameters()
435 'key31' => 'value31',
437 'key321' => 'value321',
438 'key322' => 'value322'
442 $getQueryArgumentsConfiguration = [];
443 $getQueryArgumentsConfiguration['method'] = 'POST';
444 $getQueryArgumentsConfiguration['exclude'] = [];
445 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
446 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
447 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
448 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
449 $overruleArguments = [
450 // Should be overridden
451 'key2' => 'value2Overruled',
454 // Shouldn't be set: Parameter is excluded and not forced
455 'key321' => 'value321Overruled',
456 // Should be overridden: Parameter is not excluded
457 'key322' => 'value322Overruled',
458 // Shouldn't be set: Parameter doesn't exist in source array and is not forced
459 'key323' => 'value323Overruled'
463 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2Overruled&key3[key32][key322]=value322Overruled');
464 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments);
465 $this->assertEquals($expectedResult, $actualResult);
471 public function getQueryArgumentsOverrulesMultiDimensionalForcedParameters()
473 $this->subject
->expects($this->any())->method('getEnvironmentVariable')->with($this->equalTo('QUERY_STRING'))->will(
474 $this->returnValue('key1=value1&key2=value2&key3[key31]=value31&key3[key32][key321]=value321&key3[key32][key322]=value322')
480 'key31' => 'value31',
482 'key321' => 'value321',
483 'key322' => 'value322'
487 $getQueryArgumentsConfiguration = [];
488 $getQueryArgumentsConfiguration['exclude'] = [];
489 $getQueryArgumentsConfiguration['exclude'][] = 'key1';
490 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key31]';
491 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key321]';
492 $getQueryArgumentsConfiguration['exclude'][] = 'key3[key32][key322]';
493 $getQueryArgumentsConfiguration['exclude'] = implode(',', $getQueryArgumentsConfiguration['exclude']);
494 $overruleArguments = [
495 // Should be overridden
496 'key2' => 'value2Overruled',
499 // Should be set: Parameter is excluded but forced
500 'key321' => 'value321Overruled',
501 // Should be set: Parameter doesn't exist in source array but is forced
502 'key323' => 'value323Overruled'
506 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key2=value2Overruled&key3[key32][key321]=value321Overruled&key3[key32][key323]=value323Overruled');
507 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments, true
);
508 $this->assertEquals($expectedResult, $actualResult);
509 $getQueryArgumentsConfiguration['method'] = 'POST';
510 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration, $overruleArguments, true
);
511 $this->assertEquals($expectedResult, $actualResult);
517 public function getQueryArgumentsWithMethodPostGetMergesParameters()
526 'key331' => 'POST331',
527 'key332' => 'POST332',
536 'key331' => 'GET331',
540 $getQueryArgumentsConfiguration = [];
541 $getQueryArgumentsConfiguration['method'] = 'POST,GET';
542 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key1=POST1&key2=GET2&key3[key31]=POST31&key3[key32]=GET32&key3[key33][key331]=GET331&key3[key33][key332]=POST332');
543 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
544 $this->assertEquals($expectedResult, $actualResult);
550 public function getQueryArgumentsWithMethodGetPostMergesParameters()
559 'key331' => 'GET331',
560 'key332' => 'GET332',
569 'key331' => 'POST331',
573 $getQueryArgumentsConfiguration = [];
574 $getQueryArgumentsConfiguration['method'] = 'GET,POST';
575 $expectedResult = $this->rawUrlEncodeSquareBracketsInUrl('&key1=GET1&key2=POST2&key3[key31]=GET31&key3[key32]=POST32&key3[key33][key331]=POST331&key3[key33][key332]=GET332');
576 $actualResult = $this->subject
->getQueryArguments($getQueryArgumentsConfiguration);
577 $this->assertEquals($expectedResult, $actualResult);
581 * Encodes square brackets in URL.
583 * @param string $string
586 private function rawUrlEncodeSquareBracketsInUrl($string)
588 return str_replace(['[', ']'], ['%5B', '%5D'], $string);
591 //////////////////////////
592 // Tests concerning crop
593 //////////////////////////
597 public function cropIsMultibyteSafe()
599 $this->assertEquals('бла', $this->subject
->crop('бла', '3|...'));
602 //////////////////////////////
604 //////////////////////////////
605 // Tests concerning cropHTML
606 //////////////////////////////
609 * Data provider for cropHTML.
611 * Provides combinations of text type and configuration.
613 * @return array [$expect, $conf, $content]
615 public function cropHTMLDataProvider()
617 $plainText = 'Kasper Sk' . chr(229) . 'rh' . chr(248)
618 . 'j implemented the original version of the crop function.';
619 $textWithMarkup = '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
620 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> the '
621 . 'original version of the crop function.';
622 $textWithEntities = 'Kasper Skårhøj implemented the; '
623 . 'original ' . 'version of the crop function.';
624 $textWithLinebreaks = "Lorem ipsum dolor sit amet,\n"
625 . "consetetur sadipscing elitr,\n"
626 . 'sed diam nonumy eirmod tempor invidunt ut labore e'
627 . 't dolore magna aliquyam';
630 'plain text; 11|...' => [
631 'Kasper Sk' . chr(229) . 'r...',
632 $plainText, '11|...',
634 'plain text; -58|...' => [
635 '...h' . chr(248) . 'j implemented the original version of '
636 . 'the crop function.',
637 $plainText, '-58|...',
639 'plain text; 4|...|1' => [
641 $plainText, '4|...|1',
643 'plain text; 20|...|1' => [
644 'Kasper Sk' . chr(229) . 'rh' . chr(248) . 'j...',
645 $plainText, '20|...|1',
647 'plain text; -5|...|1' => [
649 $plainText, '-5|...|1',
651 'plain text; -49|...|1' => [
652 '...the original version of the crop function.',
653 $plainText, '-49|...|1',
655 'text with markup; 11|...' => [
656 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
657 . chr(229) . 'r...</a></strong>',
658 $textWithMarkup, '11|...',
660 'text with markup; 13|...' => [
661 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
662 . chr(229) . 'rh' . chr(248) . '...</a></strong>',
663 $textWithMarkup, '13|...',
665 'text with markup; 14|...' => [
666 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
667 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
668 $textWithMarkup, '14|...',
670 'text with markup; 15|...' => [
671 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
672 . chr(229) . 'rh' . chr(248) . 'j</a> ...</strong>',
673 $textWithMarkup, '15|...',
675 'text with markup; 29|...' => [
676 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
677 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> '
679 $textWithMarkup, '29|...',
681 'text with markup; -58|...' => [
682 '<strong><a href="mailto:kasper@typo3.org">...h' . chr(248)
683 . 'j</a> implemented</strong> the original version of the crop '
685 $textWithMarkup, '-58|...',
687 'text with markup 4|...|1' => [
688 '<strong><a href="mailto:kasper@typo3.org">Kasp...</a>'
690 $textWithMarkup, '4|...|1',
692 'text with markup; 11|...|1' => [
693 '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
695 $textWithMarkup, '11|...|1',
697 'text with markup; 13|...|1' => [
698 '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
700 $textWithMarkup, '13|...|1',
702 'text with markup; 14|...|1' => [
703 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
704 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
705 $textWithMarkup, '14|...|1',
707 'text with markup; 15|...|1' => [
708 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
709 . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
710 $textWithMarkup, '15|...|1',
712 'text with markup; 29|...|1' => [
713 '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
714 . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong>...',
715 $textWithMarkup, '29|...|1',
717 'text with markup; -66|...|1' => [
718 '<strong><a href="mailto:kasper@typo3.org">...Sk' . chr(229)
719 . 'rh' . chr(248) . 'j</a> implemented</strong> the original v'
720 . 'ersion of the crop function.',
721 $textWithMarkup, '-66|...|1',
723 'text with entities 9|...' => [
725 $textWithEntities, '9|...',
727 'text with entities 10|...' => [
728 'Kasper Skå...',
729 $textWithEntities, '10|...',
731 'text with entities 11|...' => [
732 'Kasper Skår...',
733 $textWithEntities, '11|...',
735 'text with entities 13|...' => [
736 'Kasper Skårhø...',
737 $textWithEntities, '13|...',
739 'text with entities 14|...' => [
740 'Kasper Skårhøj...',
741 $textWithEntities, '14|...',
743 'text with entities 15|...' => [
744 'Kasper Skårhøj ...',
745 $textWithEntities, '15|...',
747 'text with entities 16|...' => [
748 'Kasper Skårhøj i...',
749 $textWithEntities, '16|...',
751 'text with entities -57|...' => [
752 '...j implemented the; original version of the crop function.',
753 $textWithEntities, '-57|...',
755 'text with entities -58|...' => [
756 '...øj implemented the; original version of the crop '
758 $textWithEntities, '-58|...',
760 'text with entities -59|...' => [
761 '...høj implemented the; original version of the crop '
763 $textWithEntities, '-59|...',
765 'text with entities 4|...|1' => [
767 $textWithEntities, '4|...|1',
769 'text with entities 9|...|1' => [
771 $textWithEntities, '9|...|1',
773 'text with entities 10|...|1' => [
775 $textWithEntities, '10|...|1',
777 'text with entities 11|...|1' => [
779 $textWithEntities, '11|...|1',
781 'text with entities 13|...|1' => [
783 $textWithEntities, '13|...|1',
785 'text with entities 14|...|1' => [
786 'Kasper Skårhøj...',
787 $textWithEntities, '14|...|1',
789 'text with entities 15|...|1' => [
790 'Kasper Skårhøj...',
791 $textWithEntities, '15|...|1',
793 'text with entities 16|...|1' => [
794 'Kasper Skårhøj...',
795 $textWithEntities, '16|...|1',
797 'text with entities -57|...|1' => [
798 '...implemented the; original version of the crop function.',
799 $textWithEntities, '-57|...|1',
801 'text with entities -58|...|1' => [
802 '...implemented the; original version of the crop function.',
803 $textWithEntities, '-58|...|1',
805 'text with entities -59|...|1' => [
806 '...implemented the; original version of the crop function.',
807 $textWithEntities, '-59|...|1',
809 'text with dash in html-element 28|...|1' => [
810 'Some text with a link to <link email.address@example.org - '
811 . 'mail "Open email window">my...</link>',
812 'Some text with a link to <link email.address@example.org - m'
813 . 'ail "Open email window">my email.address@example.org<'
814 . '/link> and text after it',
817 'html elements with dashes in attributes' => [
818 '<em data-foo="x">foobar</em>foo',
819 '<em data-foo="x">foobar</em>foobaz',
822 'html elements with iframe embedded 24|...|1' => [
823 'Text with iframe <iframe src="//what.ever/"></iframe> and...',
824 'Text with iframe <iframe src="//what.ever/">'
825 . '</iframe> and text after it',
828 'html elements with script tag embedded 24|...|1' => [
829 'Text with script <script>alert(\'foo\');</script> and...',
830 'Text with script <script>alert(\'foo\');</script> '
831 . 'and text after it',
834 'text with linebreaks' => [
835 "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\ns"
836 . 'ed diam nonumy eirmod tempor invidunt ut labore e'
838 $textWithLinebreaks, '121',
844 * Check if cropHTML works properly.
847 * @dataProvider cropHTMLDataProvider
848 * @param string $expect The expected cropped output.
849 * @param string $content The given input.
850 * @param string $conf The given configuration.
852 public function cropHTML($expect, $content, $conf)
854 $this->handleCharset($content, $expect);
857 $this->subject
->cropHTML($content, $conf)
862 * Data provider for round
864 * @return array [$expect, $contet, $conf]
866 public function roundDataProvider()
870 'down' => [1.0, 1.11, []],
871 'up' => [2.0, 1.51, []],
872 'rounds up from x.50' => [2.0, 1.50, []],
873 'down with decimals' => [0.12, 0.1231, ['decimals' => 2]],
874 'up with decimals' => [0.13, 0.1251, ['decimals' => 2]],
875 'ceil' => [1.0, 0.11, ['roundType' => 'ceil']],
876 'ceil does not accept decimals' => [
878 'roundType' => 'ceil',
882 'floor' => [2.0, 2.99, ['roundType' => 'floor']],
883 'floor does not accept decimals' => [
885 'roundType' => 'floor',
889 'round, down' => [1.0, 1.11, ['roundType' => 'round']],
890 'round, up' => [2.0, 1.55, ['roundType' => 'round']],
891 'round does accept decimals' => [
893 'roundType' => 'round',
898 'emtpy string' => [0.0, '', []],
899 'word string' => [0.0, 'word', []],
900 'float string' => [1.0, '1.123456789', []],
902 'null' => [0.0, null
, []],
903 'false' => [0.0, false
, []],
904 'true' => [1.0, true
, []]
909 * Check if round works properly
913 * - Different types of input are casted to float.
914 * - Configuration ceil rounds like ceil().
915 * - Configuration floor rounds like floor().
916 * - Otherwise rounds like round() and decimals can be applied.
917 * - Always returns float.
919 * @param float $expect The expected output.
920 * @param mixed $content The given content.
921 * @param array $conf The given configuration of 'round.'.
922 * @dataProvider roundDataProvider
925 public function round($expect, $content, $conf)
929 $this->subject
->_call('round', $content, $conf)
936 public function recursiveStdWrapProperlyRendersBasicString()
938 $stdWrapConfiguration = [
939 'noTrimWrap' => '|| 123|',
946 $this->subject
->stdWrap('Test', $stdWrapConfiguration)
953 public function recursiveStdWrapIsOnlyCalledOnce()
955 $stdWrapConfiguration = [
958 'data' => 'register:Counter'
961 'append' => 'LOAD_REGISTER',
964 'prioriCalc' => 'intval',
967 'data' => 'register:Counter',
976 $this->subject
->stdWrap('Counter:', $stdWrapConfiguration)
981 * Data provider for numberFormat.
983 * @return array [$expect, $content, $conf]
985 public function numberFormatDataProvider()
988 'testing decimals' => [
992 'testing decimals with input as string' => [
996 'testing dec_point' => [
998 ['decimals' => 1, 'dec_point' => ',']
1000 'testing thousands_sep' => [
1004 'thousands_sep.' => ['char' => 46]
1007 'testing mixture' => [
1008 '1.281.731,5', 1281731.45,
1011 'dec_point.' => ['char' => 44],
1012 'thousands_sep.' => ['char' => 46]
1019 * Check if numberFormat works properly.
1021 * @dataProvider numberFormatDataProvider
1024 public function numberFormat($expects, $content, $conf)
1028 $this->subject
->numberFormat($content, $conf)
1033 * Data provider replacement
1035 * @return array [$expect, $content, $conf]
1037 public function replacementDataProvider()
1040 'multiple replacements, including regex' => [
1041 'There is an animal, an animal and an animal around the block! Yeah!',
1042 'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
1046 'replace.' => ['char' => '32']
1049 'search' => 'in da hood',
1050 'replace' => 'around the block'
1053 'search' => '#a (Cat|Dog|Tiger)#i',
1054 'replace' => 'an animal',
1059 'replacement with optionSplit, normal pattern' => [
1060 'There1is2a3cat,3a3dog3and3a3tiger3in3da3hood!3Yeah!',
1061 'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
1065 'replace' => '1 || 2 || 3',
1066 'useOptionSplitReplace' => '1'
1070 'replacement with optionSplit, using regex' => [
1071 'There is a tiny cat, a midsized dog and a big tiger in da hood! Yeah!',
1072 'There is a cat, a dog and a tiger in da hood! Yeah!',
1075 'search' => '#(a) (Cat|Dog|Tiger)#i',
1076 'replace' => '${1} tiny ${2} || ${1} midsized ${2} || ${1} big ${2}',
1077 'useOptionSplitReplace' => '1',
1086 * Check if stdWrap.replacement and all of its properties work properly
1089 * @dataProvider replacementDataProvider
1090 * @param string $content The given input.
1091 * @param string $expects The expected result.
1092 * @param array $conf The given configuration.
1094 public function replacement($expects, $content, $conf)
1098 $this->subject
->_call('replacement', $content, $conf)
1103 * Data provider for calcAge.
1105 * @return array [$expect, $timestamp, $labels]
1107 public function calcAgeDataProvider()
1111 '2 min', 120, ' min| hrs| days| yrs',
1114 '2 hrs', 7200, ' min| hrs| days| yrs',
1117 '7 days', 604800, ' min| hrs| days| yrs',
1119 'day with provided singular labels' => [
1120 '1 day', 86400, ' min| hrs| days| yrs| min| hour| day| year',
1123 '45 yrs', 1417997800, ' min| hrs| days| yrs',
1125 'different labels' => [
1126 '2 Minutes', 120, ' Minutes| Hrs| Days| Yrs',
1128 'negative values' => [
1129 '-7 days', -604800, ' min| hrs| days| yrs',
1131 'default label values for wrong label input' => [
1134 'default singular label values for wrong label input' => [
1135 '1 year', 31536000, 10,
1141 * Check if calcAge works properly.
1144 * @dataProvider calcAgeDataProvider
1145 * @param int $expect
1146 * @param int $timestamp
1147 * @param string $labels
1149 public function calcAge($expect, $timestamp, $labels)
1153 $this->subject
->calcAge($timestamp, $labels)
1160 public function stdWrapReturnsExpectationDataProvider()
1163 'Prevent silent bool conversion' => [
1176 * @param string $content
1177 * @param array $configuration
1178 * @param string $expectation
1179 * @dataProvider stdWrapReturnsExpectationDataProvider
1182 public function stdWrapReturnsExpectation($content, array $configuration, $expectation)
1184 $this->assertSame($expectation, $this->subject
->stdWrap($content, $configuration));
1188 * Data provider for substring
1190 * @return array [$expect, $content, $conf]
1192 public function substringDataProvider()
1195 'sub -1' => ['g', 'substring', '-1'],
1196 'sub -1,0' => ['g', 'substring', '-1,0'],
1197 'sub -1,-1' => ['', 'substring', '-1,-1'],
1198 'sub -1,1' => ['g', 'substring', '-1,1'],
1199 'sub 0' => ['substring', 'substring', '0'],
1200 'sub 0,0' => ['substring', 'substring', '0,0'],
1201 'sub 0,-1' => ['substrin', 'substring', '0,-1'],
1202 'sub 0,1' => ['s', 'substring', '0,1'],
1203 'sub 1' => ['ubstring', 'substring', '1'],
1204 'sub 1,0' => ['ubstring', 'substring', '1,0'],
1205 'sub 1,-1' => ['ubstrin', 'substring', '1,-1'],
1206 'sub 1,1' => ['u', 'substring', '1,1'],
1207 'sub' => ['substring', 'substring', ''],
1212 * Check if substring works properly.
1215 * @dataProvider substringDataProvider
1216 * @param string $expect The expected output.
1217 * @param string $content The given input.
1218 * @param array $conf The given configutation.
1220 public function substring($expect, $content, $conf)
1222 $this->assertSame($expect, $this->subject
->substring($content, $conf));
1225 ///////////////////////////////
1226 // Tests concerning getData()
1227 ///////////////////////////////
1232 public function getDataWithTypeGpDataProvider()
1235 'Value in get-data' => ['onlyInGet', 'GetValue'],
1236 'Value in post-data' => ['onlyInPost', 'PostValue'],
1237 'Value in post-data overriding get-data' => ['inGetAndPost', 'ValueInPost'],
1242 * Checks if getData() works with type "gp"
1245 * @dataProvider getDataWithTypeGpDataProvider
1247 public function getDataWithTypeGp($key, $expectedValue)
1250 'onlyInGet' => 'GetValue',
1251 'inGetAndPost' => 'ValueInGet',
1254 'onlyInPost' => 'PostValue',
1255 'inGetAndPost' => 'ValueInPost',
1257 $this->assertEquals($expectedValue, $this->subject
->getData('gp:' . $key));
1261 * Checks if getData() works with type "tsfe"
1265 public function getDataWithTypeTsfe()
1267 $this->assertEquals($GLOBALS['TSFE']->metaCharset
, $this->subject
->getData('tsfe:metaCharset'));
1271 * Checks if getData() works with type "getenv"
1275 public function getDataWithTypeGetenv()
1277 $envName = $this->getUniqueId('frontendtest');
1278 $value = $this->getUniqueId('someValue');
1279 putenv($envName . '=' . $value);
1280 $this->assertEquals($value, $this->subject
->getData('getenv:' . $envName));
1284 * Checks if getData() works with type "getindpenv"
1288 public function getDataWithTypeGetindpenv()
1290 $this->subject
->expects($this->once())->method('getEnvironmentVariable')
1291 ->with($this->equalTo('SCRIPT_FILENAME'))->will($this->returnValue('dummyPath'));
1292 $this->assertEquals('dummyPath', $this->subject
->getData('getindpenv:SCRIPT_FILENAME'));
1296 * Checks if getData() works with type "field"
1300 public function getDataWithTypeField()
1303 $value = 'someValue';
1304 $field = [$key => $value];
1306 $this->assertEquals($value, $this->subject
->getData('field:' . $key, $field));
1310 * Checks if getData() works with type "field" of the field content
1311 * is multi-dimensional (e.g. an array)
1315 public function getDataWithTypeFieldAndFieldIsMultiDimensional()
1317 $key = 'somekey|level1|level2';
1318 $value = 'somevalue';
1319 $field = ['somekey' => ['level1' => ['level2' => 'somevalue']]];
1321 $this->assertEquals($value, $this->subject
->getData('field:' . $key, $field));
1325 * Basic check if getData gets the uid of a file object
1329 public function getDataWithTypeFileReturnsUidOfFileObject()
1331 $uid = $this->getUniqueId();
1332 $file = $this->createMock(File
::class);
1333 $file->expects($this->once())->method('getUid')->will($this->returnValue($uid));
1334 $this->subject
->setCurrentFile($file);
1335 $this->assertEquals($uid, $this->subject
->getData('file:current:uid'));
1339 * Checks if getData() works with type "parameters"
1343 public function getDataWithTypeParameters()
1345 $key = $this->getUniqueId('someKey');
1346 $value = $this->getUniqueId('someValue');
1347 $this->subject
->parameters
[$key] = $value;
1349 $this->assertEquals($value, $this->subject
->getData('parameters:' . $key));
1353 * Checks if getData() works with type "register"
1357 public function getDataWithTypeRegister()
1359 $key = $this->getUniqueId('someKey');
1360 $value = $this->getUniqueId('someValue');
1361 $GLOBALS['TSFE']->register
[$key] = $value;
1363 $this->assertEquals($value, $this->subject
->getData('register:' . $key));
1367 * Checks if getData() works with type "session"
1371 public function getDataWithTypeSession()
1373 $frontendUser = $this->getMockBuilder(FrontendUserAuthentication
::class)
1374 ->setMethods(['getSessionData'])
1376 $frontendUser->expects($this->once())->method('getSessionData')->with('myext')->willReturn([
1381 $GLOBALS['TSFE']->fe_user
= $frontendUser;
1383 $this->assertEquals(42, $this->subject
->getData('session:myext|mydata|someValue'));
1387 * Checks if getData() works with type "level"
1391 public function getDataWithTypeLevel()
1394 0 => ['uid' => 1, 'title' => 'title1'],
1395 1 => ['uid' => 2, 'title' => 'title2'],
1396 2 => ['uid' => 3, 'title' => 'title3'],
1399 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1400 $this->assertEquals(2, $this->subject
->getData('level'));
1404 * Checks if getData() works with type "global"
1408 public function getDataWithTypeGlobal()
1410 $this->assertEquals($GLOBALS['TSFE']->metaCharset
, $this->subject
->getData('global:TSFE|metaCharset'));
1414 * Checks if getData() works with type "leveltitle"
1418 public function getDataWithTypeLeveltitle()
1421 0 => ['uid' => 1, 'title' => 'title1'],
1422 1 => ['uid' => 2, 'title' => 'title2'],
1423 2 => ['uid' => 3, 'title' => ''],
1426 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1427 $this->assertEquals('', $this->subject
->getData('leveltitle:-1'));
1428 // since "title3" is not set, it will slide to "title2"
1429 $this->assertEquals('title2', $this->subject
->getData('leveltitle:-1,slide'));
1433 * Checks if getData() works with type "levelmedia"
1437 public function getDataWithTypeLevelmedia()
1440 0 => ['uid' => 1, 'title' => 'title1', 'media' => 'media1'],
1441 1 => ['uid' => 2, 'title' => 'title2', 'media' => 'media2'],
1442 2 => ['uid' => 3, 'title' => 'title3', 'media' => ''],
1445 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1446 $this->assertEquals('', $this->subject
->getData('levelmedia:-1'));
1447 // since "title3" is not set, it will slide to "title2"
1448 $this->assertEquals('media2', $this->subject
->getData('levelmedia:-1,slide'));
1452 * Checks if getData() works with type "leveluid"
1456 public function getDataWithTypeLeveluid()
1459 0 => ['uid' => 1, 'title' => 'title1'],
1460 1 => ['uid' => 2, 'title' => 'title2'],
1461 2 => ['uid' => 3, 'title' => 'title3'],
1464 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1465 $this->assertEquals(3, $this->subject
->getData('leveluid:-1'));
1466 // every element will have a uid - so adding slide doesn't really make sense, just for completeness
1467 $this->assertEquals(3, $this->subject
->getData('leveluid:-1,slide'));
1471 * Checks if getData() works with type "levelfield"
1475 public function getDataWithTypeLevelfield()
1478 0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1479 1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1480 2 => ['uid' => 3, 'title' => 'title3', 'testfield' => ''],
1483 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1484 $this->assertEquals('', $this->subject
->getData('levelfield:-1,testfield'));
1485 $this->assertEquals('field2', $this->subject
->getData('levelfield:-1,testfield,slide'));
1489 * Checks if getData() works with type "fullrootline"
1493 public function getDataWithTypeFullrootline()
1496 0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1499 0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1500 1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1501 2 => ['uid' => 3, 'title' => 'title3', 'testfield' => 'field3'],
1504 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline1;
1505 $GLOBALS['TSFE']->rootLine
= $rootline2;
1506 $this->assertEquals('field2', $this->subject
->getData('fullrootline:-1,testfield'));
1510 * Checks if getData() works with type "date"
1514 public function getDataWithTypeDate()
1517 $defaultFormat = 'd/m Y';
1519 $this->assertEquals(date($format, $GLOBALS['EXEC_TIME']), $this->subject
->getData('date:' . $format));
1520 $this->assertEquals(date($defaultFormat, $GLOBALS['EXEC_TIME']), $this->subject
->getData('date'));
1524 * Checks if getData() works with type "page"
1528 public function getDataWithTypePage()
1531 $GLOBALS['TSFE']->page
['uid'] = $uid;
1532 $this->assertEquals($uid, $this->subject
->getData('page:uid'));
1536 * Checks if getData() works with type "current"
1540 public function getDataWithTypeCurrent()
1542 $key = $this->getUniqueId('someKey');
1543 $value = $this->getUniqueId('someValue');
1544 $this->subject
->data
[$key] = $value;
1545 $this->subject
->currentValKey
= $key;
1546 $this->assertEquals($value, $this->subject
->getData('current'));
1550 * Checks if getData() works with type "db"
1554 public function getDataWithTypeDb()
1556 $dummyRecord = ['uid' => 5, 'title' => 'someTitle'];
1558 $GLOBALS['TSFE']->sys_page
->expects($this->atLeastOnce())->method('getRawRecord')->with('tt_content', '106')->will($this->returnValue($dummyRecord));
1559 $this->assertEquals($dummyRecord['title'], $this->subject
->getData('db:tt_content:106:title'));
1563 * Checks if getData() works with type "lll"
1567 public function getDataWithTypeLll()
1569 $key = $this->getUniqueId('someKey');
1570 $value = $this->getUniqueId('someValue');
1571 $GLOBALS['TSFE']->expects($this->once())->method('sL')->with('LLL:' . $key)->will($this->returnValue($value));
1572 $this->assertEquals($value, $this->subject
->getData('lll:' . $key));
1576 * Checks if getData() works with type "path"
1580 public function getDataWithTypePath()
1582 $filenameIn = $this->getUniqueId('someValue');
1583 $filenameOut = $this->getUniqueId('someValue');
1584 $this->templateServiceMock
->expects($this->atLeastOnce())->method('getFileName')->with($filenameIn)->will($this->returnValue($filenameOut));
1585 $this->assertEquals($filenameOut, $this->subject
->getData('path:' . $filenameIn));
1589 * Checks if getData() works with type "parentRecordNumber"
1593 public function getDataWithTypeParentRecordNumber()
1595 $recordNumber = rand();
1596 $this->subject
->parentRecordNumber
= $recordNumber;
1597 $this->assertEquals($recordNumber, $this->subject
->getData('cobj:parentRecordNumber'));
1601 * Checks if getData() works with type "debug:rootLine"
1605 public function getDataWithTypeDebugRootline()
1608 0 => ['uid' => 1, 'title' => 'title1'],
1609 1 => ['uid' => 2, 'title' => 'title2'],
1610 2 => ['uid' => 3, 'title' => ''],
1612 $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)';
1613 $GLOBALS['TSFE']->tmpl
->rootLine
= $rootline;
1615 DebugUtility
::useAnsiColor(false
);
1616 $result = $this->subject
->getData('debug:rootLine');
1617 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1619 $this->assertEquals($expectedResult, $cleanedResult);
1623 * Checks if getData() works with type "debug:fullRootLine"
1627 public function getDataWithTypeDebugFullRootline()
1630 0 => ['uid' => 1, 'title' => 'title1'],
1631 1 => ['uid' => 2, 'title' => 'title2'],
1632 2 => ['uid' => 3, 'title' => ''],
1634 $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)';
1635 $GLOBALS['TSFE']->rootLine
= $rootline;
1637 DebugUtility
::useAnsiColor(false
);
1638 $result = $this->subject
->getData('debug:fullRootLine');
1639 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1641 $this->assertEquals($expectedResult, $cleanedResult);
1645 * Checks if getData() works with type "debug:data"
1649 public function getDataWithTypeDebugData()
1651 $key = $this->getUniqueId('someKey');
1652 $value = $this->getUniqueId('someValue');
1653 $this->subject
->data
= [$key => $value];
1655 $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1657 DebugUtility
::useAnsiColor(false
);
1658 $result = $this->subject
->getData('debug:data');
1659 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1661 $this->assertEquals($expectedResult, $cleanedResult);
1665 * Checks if getData() works with type "debug:register"
1669 public function getDataWithTypeDebugRegister()
1671 $key = $this->getUniqueId('someKey');
1672 $value = $this->getUniqueId('someValue');
1673 $GLOBALS['TSFE']->register
= [$key => $value];
1675 $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1677 DebugUtility
::useAnsiColor(false
);
1678 $result = $this->subject
->getData('debug:register');
1679 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1681 $this->assertEquals($expectedResult, $cleanedResult);
1685 * Checks if getData() works with type "data:page"
1689 public function getDataWithTypeDebugPage()
1692 $GLOBALS['TSFE']->page
= ['uid' => $uid];
1694 $expectedResult = 'array(1item)uid=>' . $uid . '(integer)';
1696 DebugUtility
::useAnsiColor(false
);
1697 $result = $this->subject
->getData('debug:page');
1698 $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1700 $this->assertEquals($expectedResult, $cleanedResult);
1706 public function aTagParamsHasLeadingSpaceIfNotEmpty()
1708 $aTagParams = $this->subject
->getATagParams(['ATagParams' => 'data-test="testdata"']);
1709 $this->assertEquals(' data-test="testdata"', $aTagParams);
1715 public function aTagParamsHaveSpaceBetweenLocalAndGlobalParams()
1717 $GLOBALS['TSFE']->ATagParams
= 'data-global="dataglobal"';
1718 $aTagParams = $this->subject
->getATagParams(['ATagParams' => 'data-test="testdata"']);
1719 $this->assertEquals(' data-global="dataglobal" data-test="testdata"', $aTagParams);
1725 public function aTagParamsHasNoLeadingSpaceIfEmpty()
1727 // make sure global ATagParams are empty
1728 $GLOBALS['TSFE']->ATagParams
= '';
1729 $aTagParams = $this->subject
->getATagParams(['ATagParams' => '']);
1730 $this->assertEquals('', $aTagParams);
1736 public function getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFoundDataProvider()
1742 ['fooo', ['foo' => 'bar']]
1747 * Make sure that the rendering falls back to the classic <img style if nothing else is found
1750 * @dataProvider getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFoundDataProvider
1751 * @param string $key
1752 * @param array $configuration
1754 public function getImageTagTemplateFallsBackToDefaultTemplateIfNoTemplateIsFound($key, $configuration)
1756 $defaultImgTagTemplate = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1757 $result = $this->subject
->getImageTagTemplate($key, $configuration);
1758 $this->assertEquals($result, $defaultImgTagTemplate);
1764 public function getImageTagTemplateReturnTemplateElementIdentifiedByKeyDataProvider()
1772 'element' => '<img src="###SRC###" srcset="###SOURCES###" ###PARAMS### ###ALTPARAMS### ###FOOBAR######SELFCLOSINGTAGSLASH###>'
1776 '<img src="###SRC###" srcset="###SOURCES###" ###PARAMS### ###ALTPARAMS### ###FOOBAR######SELFCLOSINGTAGSLASH###>'
1783 * Assure if a layoutKey and layout is given the selected layout is returned
1786 * @dataProvider getImageTagTemplateReturnTemplateElementIdentifiedByKeyDataProvider
1787 * @param string $key
1788 * @param array $configuration
1789 * @param string $expectation
1791 public function getImageTagTemplateReturnTemplateElementIdentifiedByKey($key, $configuration, $expectation)
1793 $result = $this->subject
->getImageTagTemplate($key, $configuration);
1794 $this->assertEquals($result, $expectation);
1800 public function getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefinedDataProvider()
1804 ['foo', null
, null
],
1805 ['foo', ['sourceCollection.' => 1], 'bar']
1810 * Make sure the source collection is empty if no valid configuration or source collection is defined
1813 * @dataProvider getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefinedDataProvider
1814 * @param string $layoutKey
1815 * @param array $configuration
1816 * @param string $file
1818 public function getImageSourceCollectionReturnsEmptyStringIfNoSourcesAreDefined($layoutKey, $configuration, $file)
1820 $result = $this->subject
->getImageSourceCollection($layoutKey, $configuration, $file);
1821 $this->assertSame($result, '');
1825 * Make sure the generation of subimages calls the generation of the subimages and uses the layout -> source template
1829 public function getImageSourceCollectionRendersDefinedSources()
1831 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
1832 $cObj = $this->getMockBuilder(ContentObjectRenderer
::class)
1833 ->setMethods(['stdWrap', 'getImgResource'])
1836 $cObj->start([], 'tt_content');
1838 $layoutKey = 'test';
1841 'layoutKey' => 'test',
1844 'element' => '<img ###SRC### ###SRCCOLLECTION### ###SELFCLOSINGTAGSLASH###>',
1845 'source' => '---###SRC###---'
1848 'sourceCollection.' => [
1855 $file = 'testImageName';
1857 // Avoid calling of stdWrap
1859 ->expects($this->any())
1861 ->will($this->returnArgument(0));
1863 // Avoid calling of imgResource
1865 ->expects($this->exactly(1))
1866 ->method('getImgResource')
1867 ->with($this->equalTo('testImageName'))
1868 ->will($this->returnValue([100, 100, null
, 'bar']));
1870 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
1872 $this->assertEquals('---bar---', $result);
1876 * Data provider for the getImageSourceCollectionRendersDefinedLayoutKeyDefault test
1878 * @return array multi-dimensional array with the second level like this:
1879 * @see getImageSourceCollectionRendersDefinedLayoutKeyDefault
1881 public function getImageSourceCollectionRendersDefinedLayoutKeyDataDefaultProvider()
1883 $sourceCollectionArray = [
1886 'srcsetCandidate' => '600w',
1887 'mediaQuery' => '(max-device-width: 600px)',
1888 'dataKey' => 'small',
1891 'if.directReturn' => 0,
1893 'pixelDensity' => '2',
1894 'srcsetCandidate' => '600w 2x',
1895 'mediaQuery' => '(max-device-width: 600px) AND (min-resolution: 192dpi)',
1896 'dataKey' => 'smallRetina',
1903 'layoutKey' => 'default',
1906 'element' => '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>',
1910 'sourceCollection.' => $sourceCollectionArray
1917 * Make sure the generation of subimages renders the expected HTML Code for the sourceset
1920 * @dataProvider getImageSourceCollectionRendersDefinedLayoutKeyDataDefaultProvider
1921 * @param string $layoutKey
1922 * @param array $configuration
1924 public function getImageSourceCollectionRendersDefinedLayoutKeyDefault($layoutKey, $configuration)
1926 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
1927 $cObj = $this->getMockBuilder(ContentObjectRenderer
::class)
1928 ->setMethods(['stdWrap', 'getImgResource'])
1931 $cObj->start([], 'tt_content');
1933 $file = 'testImageName';
1935 // Avoid calling of stdWrap
1937 ->expects($this->any())
1939 ->will($this->returnArgument(0));
1941 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
1943 $this->assertEmpty($result);
1947 * Data provider for the getImageSourceCollectionRendersDefinedLayoutKeyData test
1949 * @return array multi-dimensional array with the second level like this:
1950 * @see getImageSourceCollectionRendersDefinedLayoutKeyData
1952 public function getImageSourceCollectionRendersDefinedLayoutKeyDataDataProvider()
1954 $sourceCollectionArray = [
1957 'srcsetCandidate' => '600w',
1958 'mediaQuery' => '(max-device-width: 600px)',
1959 'dataKey' => 'small',
1962 'if.directReturn' => 1,
1964 'pixelDensity' => '2',
1965 'srcsetCandidate' => '600w 2x',
1966 'mediaQuery' => '(max-device-width: 600px) AND (min-resolution: 192dpi)',
1967 'dataKey' => 'smallRetina',
1974 'layoutKey' => 'srcset',
1977 'element' => '<img src="###SRC###" srcset="###SOURCECOLLECTION###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
1978 'source' => '|*|###SRC### ###SRCSETCANDIDATE###,|*|###SRC### ###SRCSETCANDIDATE###'
1981 'sourceCollection.' => $sourceCollectionArray
1984 'bar-file.jpg 600w,bar-file.jpg 600w 2x',
1989 'layoutKey' => 'picture',
1992 'element' => '<picture>###SOURCECOLLECTION###<img src="###SRC###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###></picture>',
1993 'source' => '<source src="###SRC###" media="###MEDIAQUERY###"###SELFCLOSINGTAGSLASH###>'
1996 'sourceCollection.' => $sourceCollectionArray,
1999 '<source src="bar-file.jpg" media="(max-device-width: 600px)" /><source src="bar-file.jpg" media="(max-device-width: 600px) AND (min-resolution: 192dpi)" />',
2004 'layoutKey' => 'picture',
2007 'element' => '<picture>###SOURCECOLLECTION###<img src="###SRC###" ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###></picture>',
2008 'source' => '<source src="###SRC###" media="###MEDIAQUERY###"###SELFCLOSINGTAGSLASH###>'
2011 'sourceCollection.' => $sourceCollectionArray,
2014 '<source src="bar-file.jpg" media="(max-device-width: 600px)"><source src="bar-file.jpg" media="(max-device-width: 600px) AND (min-resolution: 192dpi)">',
2019 'layoutKey' => 'data',
2022 'element' => '<img src="###SRC###" ###SOURCECOLLECTION### ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2023 'source' => 'data-###DATAKEY###="###SRC###"'
2026 'sourceCollection.' => $sourceCollectionArray
2029 'data-small="bar-file.jpg"data-smallRetina="bar-file.jpg"',
2035 * Make sure the generation of subimages renders the expected HTML Code for the sourceset
2038 * @dataProvider getImageSourceCollectionRendersDefinedLayoutKeyDataDataProvider
2039 * @param string $layoutKey
2040 * @param array $configuration
2041 * @param string $xhtmlDoctype
2042 * @param string $expectedHtml
2044 public function getImageSourceCollectionRendersDefinedLayoutKeyData($layoutKey, $configuration, $xhtmlDoctype, $expectedHtml)
2046 /** @var $cObj \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer */
2047 $cObj = $this->getMockBuilder(ContentObjectRenderer
::class)
2048 ->setMethods(['stdWrap', 'getImgResource'])
2051 $cObj->start([], 'tt_content');
2053 $file = 'testImageName';
2055 $GLOBALS['TSFE']->xhtmlDoctype
= $xhtmlDoctype;
2057 // Avoid calling of stdWrap
2059 ->expects($this->any())
2061 ->will($this->returnArgument(0));
2063 // Avoid calling of imgResource
2065 ->expects($this->exactly(2))
2066 ->method('getImgResource')
2067 ->with($this->equalTo('testImageName'))
2068 ->will($this->returnValue([100, 100, null
, 'bar-file.jpg']));
2070 $result = $cObj->getImageSourceCollection($layoutKey, $configuration, $file);
2072 $this->assertEquals($expectedHtml, $result);
2076 * Make sure the hook in get sourceCollection is called
2080 public function getImageSourceCollectionHookCalled()
2082 $this->subject
= $this->getAccessibleMock(
2083 ContentObjectRenderer
::class,
2084 ['getResourceFactory', 'stdWrap', 'getImgResource']
2086 $this->subject
->start([], 'tt_content');
2088 // Avoid calling stdwrap and getImgResource
2089 $this->subject
->expects($this->any())
2091 ->will($this->returnArgument(0));
2093 $this->subject
->expects($this->any())
2094 ->method('getImgResource')
2095 ->will($this->returnValue([100, 100, null
, 'bar-file.jpg']));
2097 $resourceFactory = $this->createMock(ResourceFactory
::class);
2098 $this->subject
->expects($this->any())->method('getResourceFactory')->will($this->returnValue($resourceFactory));
2100 $className = $this->getUniqueId('tx_coretest_getImageSourceCollectionHookCalled');
2101 $getImageSourceCollectionHookMock = $this->getMockBuilder(
2102 ContentObjectOneSourceCollectionHookInterface
::class
2104 ->setMethods(['getOneSourceCollection'])
2105 ->setMockClassName($className)
2107 GeneralUtility
::addInstance($className, $getImageSourceCollectionHookMock);
2108 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'][] = $className;
2110 $getImageSourceCollectionHookMock
2111 ->expects($this->exactly(1))
2112 ->method('getOneSourceCollection')
2113 ->will($this->returnCallback([$this, 'isGetOneSourceCollectionCalledCallback']));
2116 'layoutKey' => 'data',
2119 'element' => '<img src="###SRC###" ###SOURCECOLLECTION### ###PARAMS### ###ALTPARAMS######SELFCLOSINGTAGSLASH###>',
2120 'source' => 'data-###DATAKEY###="###SRC###"'
2123 'sourceCollection.' => [
2126 'srcsetCandidate' => '600w',
2127 'mediaQuery' => '(max-device-width: 600px)',
2128 'dataKey' => 'small',
2133 $result = $this->subject
->getImageSourceCollection('data', $configuration, $this->getUniqueId('testImage-'));
2135 $this->assertSame($result, 'isGetOneSourceCollectionCalledCallback');
2139 * Handles the arguments that have been sent to the getImgResource hook.
2141 * @param array $sourceRenderConfiguration
2142 * @param array $sourceConfiguration
2143 * @param $oneSourceCollection
2146 * @see getImageSourceCollectionHookCalled
2148 public function isGetOneSourceCollectionCalledCallback($sourceRenderConfiguration, $sourceConfiguration, $oneSourceCollection, $parent)
2150 $this->assertTrue(is_array($sourceRenderConfiguration));
2151 $this->assertTrue(is_array($sourceConfiguration));
2152 return 'isGetOneSourceCollectionCalledCallback';
2158 public function renderingContentObjectThrowsException()
2160 $this->expectException(\LogicException
::class);
2161 $this->expectExceptionCode(1414513947);
2162 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2163 $this->subject
->render($contentObjectFixture, []);
2169 public function exceptionHandlerIsEnabledByDefaultInProductionContext()
2171 $backupApplicationContext = GeneralUtility
::getApplicationContext();
2172 Fixtures\GeneralUtilityFixture
::setApplicationContext(new ApplicationContext('Production'));
2174 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2175 $this->subject
->render($contentObjectFixture, []);
2177 Fixtures\GeneralUtilityFixture
::setApplicationContext($backupApplicationContext);
2183 public function renderingContentObjectDoesNotThrowExceptionIfExceptionHandlerIsConfiguredLocally()
2185 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2188 'exceptionHandler' => '1'
2190 $this->subject
->render($contentObjectFixture, $configuration);
2196 public function renderingContentObjectDoesNotThrowExceptionIfExceptionHandlerIsConfiguredGlobally()
2198 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2200 $this->frontendControllerMock
->config
['config']['contentObjectExceptionHandler'] = '1';
2201 $this->subject
->render($contentObjectFixture, []);
2207 public function globalExceptionHandlerConfigurationCanBeOverriddenByLocalConfiguration()
2209 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2210 $this->expectException(\LogicException
::class);
2211 $this->expectExceptionCode(1414513947);
2212 $this->frontendControllerMock
->config
['config']['contentObjectExceptionHandler'] = '1';
2214 'exceptionHandler' => '0'
2216 $this->subject
->render($contentObjectFixture, $configuration);
2222 public function renderedErrorMessageCanBeCustomized()
2224 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2227 'exceptionHandler' => '1',
2228 'exceptionHandler.' => [
2229 'errorMessage' => 'New message for testing',
2233 $this->assertSame('New message for testing', $this->subject
->render($contentObjectFixture, $configuration));
2239 public function localConfigurationOverridesGlobalConfiguration()
2241 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2243 $this->frontendControllerMock
2244 ->config
['config']['contentObjectExceptionHandler.'] = [
2245 'errorMessage' => 'Global message for testing',
2248 'exceptionHandler' => '1',
2249 'exceptionHandler.' => [
2250 'errorMessage' => 'New message for testing',
2254 $this->assertSame('New message for testing', $this->subject
->render($contentObjectFixture, $configuration));
2260 public function specificExceptionsCanBeIgnoredByExceptionHandler()
2262 $contentObjectFixture = $this->createContentObjectThrowingExceptionFixture();
2265 'exceptionHandler' => '1',
2266 'exceptionHandler.' => [
2267 'ignoreCodes.' => ['10.' => '1414513947'],
2270 $this->expectException(\LogicException
::class);
2271 $this->expectExceptionCode(1414513947);
2272 $this->subject
->render($contentObjectFixture, $configuration);
2276 * @return \PHPUnit_Framework_MockObject_MockObject | AbstractContentObject
2278 protected function createContentObjectThrowingExceptionFixture()
2280 $contentObjectFixture = $this->getMockBuilder(AbstractContentObject
::class)
2281 ->setConstructorArgs([$this->subject
])
2283 $contentObjectFixture->expects($this->once())
2285 ->willReturnCallback(function () {
2286 throw new \
LogicException('Exception during rendering', 1414513947);
2288 return $contentObjectFixture;
2294 protected function getLibParseFunc()
2300 'keep' => '{$styles.content.links.keep}',
2313 'data' => 'parameters : allParams',
2322 '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',
2324 'sword' => '<span class="csc-sword">|</span>',
2326 'nonTypoTagStdWrap.' => [
2327 'HTMLparser' => '1',
2329 'keepNonMatchedTags' => '1',
2330 'htmlSpecialChars' => '2',
2339 protected function getLibParseFunc_RTE()
2344 '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',
2347 'externalBlocks' => 'article, aside, blockquote, div, dd, dl, footer, header, nav, ol, section, table, ul, pre',
2348 'externalBlocks.' => [
2350 'callRecursive' => '1',
2354 'callRecursive' => '1',
2358 'callRecursive' => '1',
2362 'callRecursive' => '1',
2366 'callRecursive' => '1',
2370 'callRecursive' => '1',
2374 'callRecursive' => '1',
2378 'callRecursive' => '1',
2382 'callRecursive' => '1',
2386 'callRecursive' => '1',
2390 'callRecursive' => '1',
2394 'HTMLtableCells' => '1',
2395 'HTMLtableCells.' => [
2396 'addChr10BetweenParagraphs' => '1',
2399 'parseFunc' => '=< lib.parseFunc_RTE',
2401 'nonTypoTagStdWrap.' => [
2403 'nonWrappedTag' => '',
2411 'HTMLparser' => '1',
2413 'keepNonMatchedTags' => '1',
2419 'default' => 'contenttable',
2420 'list' => 'contenttable',
2430 'callRecursive' => '1',
2438 'override' => '_blank',
2443 'nonTypoTagStdWrap.' => [
2445 'addAttributes.' => [
2447 'class' => 'bodytext',
2449 'setOnly' => 'blank',
2453 'encapsTagList' => 'p,pre,h1,h2,h3,h4,h5,h6,hr,dt,li',
2454 'innerStdWrap_all.' => [
2455 'ifBlank' => ' ',
2457 'nonWrappedTag' => 'P',
2462 'HTMLparser' => '1',
2464 'htmlSpecialChars' => '2',
2465 'keepNonMatchedTags' => '1',
2468 'sword' => '<span class="csc-sword">|</span>',
2481 'data' => 'parameters : allParams',
2496 public function _parseFuncReturnsCorrectHtmlDataProvider()
2499 'Text without tag is wrapped with <p> tag' => [
2501 $this->getLibParseFunc_RTE(),
2502 '<p class="bodytext">Text without tag</p>',
2504 'Text wrapped with <p> tag remains the same' => [
2505 '<p class="myclass">Text with <p> tag</p>',
2506 $this->getLibParseFunc_RTE(),
2507 '<p class="myclass">Text with <p> tag</p>',
2509 'Text with absolute external link' => [
2510 'Text with <link http://example.com/foo/>external link</link>',
2511 $this->getLibParseFunc_RTE(),
2512 '<p class="bodytext">Text with <a href="http://example.com/foo/">external link</a></p>',
2514 'Empty lines are not duplicated' => [
2516 $this->getLibParseFunc_RTE(),
2517 '<p class="bodytext"> </p>',
2519 'Multiple empty lines with no text' => [
2521 $this->getLibParseFunc_RTE(),
2522 '<p class="bodytext"> </p>' . LF
. '<p class="bodytext"> </p>' . LF
. '<p class="bodytext"> </p>',
2524 'Empty lines are not duplicated at the end of content' => [
2526 $this->getLibParseFunc_RTE(),
2527 '<p class="bodytext">test</p>' . LF
. '<p class="bodytext"> </p>',
2529 'Empty lines are not trimmed' => [
2531 $this->getLibParseFunc_RTE(),
2532 '<p class="bodytext"> </p>' . LF
. '<p class="bodytext">test</p>' . LF
. '<p class="bodytext"> </p>',
2539 * @dataProvider _parseFuncReturnsCorrectHtmlDataProvider
2540 * @param string $value
2541 * @param array $configuration
2542 * @param string $expectedResult
2544 public function stdWrap_parseFuncReturnsParsedHtml($value, $configuration, $expectedResult)
2546 $this->assertEquals($expectedResult, $this->subject
->stdWrap_parseFunc($value, $configuration));
2552 public function typolinkReturnsCorrectLinksForEmailsAndUrlsDataProvider()
2558 'parameter' => 'http://typo3.org',
2560 '<a href="http://typo3.org">TYPO3</a>',
2562 'Link to url without schema' => [
2565 'parameter' => 'typo3.org',
2567 '<a href="http://typo3.org">TYPO3</a>',
2569 'Link to url without link text' => [
2572 'parameter' => 'http://typo3.org',
2574 '<a href="http://typo3.org">http://typo3.org</a>',
2576 'Link to url with attributes' => [
2579 'parameter' => 'http://typo3.org',
2580 'ATagParams' => 'class="url-class"',
2581 'extTarget' => '_blank',
2582 'title' => 'Open new window',
2584 '<a href="http://typo3.org" title="Open new window" target="_blank" class="url-class">TYPO3</a>',
2586 'Link to url with attributes in parameter' => [
2589 'parameter' => 'http://typo3.org _blank url-class "Open new window"',
2591 '<a href="http://typo3.org" title="Open new window" target="_blank" class="url-class">TYPO3</a>',
2593 'Link to url with script tag' => [
2596 'parameter' => 'http://typo3.org<script>alert(123)</script>',
2598 '<a href="http://typo3.org<script>alert(123)</script>">http://typo3.org<script>alert(123)</script></a>',
2600 'Link to email address' => [
2603 'parameter' => 'foo@bar.org',
2605 '<a href="mailto:foo@bar.org">Email address</a>',
2607 'Link to email address without link text' => [
2610 'parameter' => 'foo@bar.org',
2612 '<a href="mailto:foo@bar.org">foo@bar.org</a>',
2614 'Link to email with attributes' => [
2617 'parameter' => 'foo@bar.org',
2618 'ATagParams' => 'class="email-class"',
2619 'title' => 'Write an email',
2621 '<a href="mailto:foo@bar.org" title="Write an email" class="email-class">Email address</a>',
2623 'Link to email with attributes in parameter' => [
2626 'parameter' => 'foo@bar.org - email-class "Write an email"',
2628 '<a href="mailto:foo@bar.org" title="Write an email" class="email-class">Email address</a>',
2635 * @param string $linkText
2636 * @param array $configuration
2637 * @param string $expectedResult
2638 * @dataProvider typolinkReturnsCorrectLinksForEmailsAndUrlsDataProvider
2640 public function typolinkReturnsCorrectLinksForEmailsAndUrls($linkText, $configuration, $expectedResult)
2642 $templateServiceObjectMock = $this->getMockBuilder(TemplateService
::class)
2643 ->setMethods(['dummy'])
2645 $templateServiceObjectMock->setup
= [
2647 'parseFunc.' => $this->getLibParseFunc(),
2650 $typoScriptFrontendControllerMockObject = $this->createMock(TypoScriptFrontendController
::class);
2651 $typoScriptFrontendControllerMockObject->config
= [
2654 $typoScriptFrontendControllerMockObject->tmpl
= $templateServiceObjectMock;
2655 $GLOBALS['TSFE'] = $typoScriptFrontendControllerMockObject;
2656 $this->subject
->_set('typoScriptFrontendController', $typoScriptFrontendControllerMockObject);
2658 $this->assertEquals($expectedResult, $this->subject
->typoLink($linkText, $configuration));
2662 * @param array $settings
2663 * @param string $linkText
2664 * @param string $mailAddress
2665 * @param string $expected
2666 * @dataProvider typoLinkEncodesMailAddressForSpamProtectionDataProvider
2669 public function typoLinkEncodesMailAddressForSpamProtection(array $settings, $linkText, $mailAddress, $expected)
2671 $this->getFrontendController()->spamProtectEmailAddresses
= $settings['spamProtectEmailAddresses'];
2672 $this->getFrontendController()->config
['config'] = $settings;
2673 $typoScript = ['parameter' => $mailAddress];
2675 $this->assertEquals($expected, $this->subject
->typoLink($linkText, $typoScript));
2681 public function typoLinkEncodesMailAddressForSpamProtectionDataProvider()
2684 'plain mail without mailto scheme' => [
2686 'spamProtectEmailAddresses' => '',
2687 'spamProtectEmailAddresses_atSubst' => '',
2688 'spamProtectEmailAddresses_lastDotSubst' => '',
2690 'some.body@test.typo3.org',
2691 'some.body@test.typo3.org',
2692 '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
2694 'plain mail with mailto scheme' => [
2696 'spamProtectEmailAddresses' => '',
2697 'spamProtectEmailAddresses_atSubst' => '',
2698 'spamProtectEmailAddresses_lastDotSubst' => '',
2700 'some.body@test.typo3.org',
2701 'mailto:some.body@test.typo3.org',
2702 '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
2704 'plain with at and dot substitution' => [
2706 'spamProtectEmailAddresses' => '0',
2707 'spamProtectEmailAddresses_atSubst' => '(at)',
2708 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2710 'some.body@test.typo3.org',
2711 'mailto:some.body@test.typo3.org',
2712 '<a href="mailto:some.body@test.typo3.org">some.body@test.typo3.org</a>',
2714 'mono-alphabetic substitution offset +1' => [
2716 'spamProtectEmailAddresses' => '1',
2717 'spamProtectEmailAddresses_atSubst' => '',
2718 'spamProtectEmailAddresses_lastDotSubst' => '',
2720 'some.body@test.typo3.org',
2721 'mailto:some.body@test.typo3.org',
2722 '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnf\/cpezAuftu\/uzqp4\/psh\');">some.body(at)test.typo3.org</a>',
2724 'mono-alphabetic substitution offset +1 with at substitution' => [
2726 'spamProtectEmailAddresses' => '1',
2727 'spamProtectEmailAddresses_atSubst' => '@',
2728 'spamProtectEmailAddresses_lastDotSubst' => '',
2730 'some.body@test.typo3.org',
2731 'mailto:some.body@test.typo3.org',
2732 '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnf\/cpezAuftu\/uzqp4\/psh\');">some.body@test.typo3.org</a>',
2734 'mono-alphabetic substitution offset +1 with at and dot substitution' => [
2736 'spamProtectEmailAddresses' => '1',
2737 'spamProtectEmailAddresses_atSubst' => '(at)',
2738 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2740 'some.body@test.typo3.org',
2741 'mailto:some.body@test.typo3.org',
2742 '<a href="javascript:linkTo_UnCryptMailto(\'nbjmup+tpnf\/cpezAuftu\/uzqp4\/psh\');">some.body(at)test.typo3(dot)org</a>',
2744 'mono-alphabetic substitution offset -1 with at and dot substitution' => [
2746 'spamProtectEmailAddresses' => '-1',
2747 'spamProtectEmailAddresses_atSubst' => '(at)',
2748 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2750 'some.body@test.typo3.org',
2751 'mailto:some.body@test.typo3.org',
2752 '<a href="javascript:linkTo_UnCryptMailto(\'lzhksn9rnld-ancxZsdrs-sxon2-nqf\');">some.body(at)test.typo3(dot)org</a>',
2754 'entity substitution with at and dot substitution' => [
2756 'spamProtectEmailAddresses' => 'ascii',
2757 'spamProtectEmailAddresses_atSubst' => '',
2758 'spamProtectEmailAddresses_lastDotSubst' => '',
2760 'some.body@test.typo3.org',
2761 'mailto:some.body@test.typo3.org',
2762 '<a href="mailto:some.body@test.typo3.org">some.body(at)test.typo3.org</a>',
2764 'entity substitution with at and dot substitution with at and dot substitution' => [
2766 'spamProtectEmailAddresses' => 'ascii',
2767 'spamProtectEmailAddresses_atSubst' => '(at)',
2768 'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
2770 'some.body@test.typo3.org',
2771 'mailto:some.body@test.typo3.org',
2772 '<a href="mailto:some.body@test.typo3.org">some.body(at)test.typo3(dot)org</a>',
2780 public function typolinkReturnsCorrectLinksFilesDataProvider()
2786 'parameter' => 'fileadmin/foo.bar',
2788 '<a href="fileadmin/foo.bar">My file</a>',
2790 'Link to file without link text' => [
2793 'parameter' => 'fileadmin/foo.bar',
2795 '<a href="fileadmin/foo.bar">fileadmin/foo.bar</a>',
2797 'Link to file with attributes' => [
2800 'parameter' => 'fileadmin/foo.bar',
2801 'ATagParams' => 'class="file-class"',
2802 'fileTarget' => '_blank',
2803 'title' => 'Title of the file',
2805 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2807 'Link to file with attributes and additional href' => [
2810 'parameter' => 'fileadmin/foo.bar',
2811 'ATagParams' => 'href="foo-bar"',
2812 'fileTarget' => '_blank',
2813 'title' => 'Title of the file',
2815 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank">My file</a>',
2817 'Link to file with attributes and additional href and class' => [
2820 'parameter' => 'fileadmin/foo.bar',
2821 'ATagParams' => 'href="foo-bar" class="file-class"',
2822 'fileTarget' => '_blank',
2823 'title' => 'Title of the file',
2825 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2827 'Link to file with attributes and additional class and href' => [
2830 'parameter' => 'fileadmin/foo.bar',
2831 'ATagParams' => 'class="file-class" href="foo-bar"',
2832 'fileTarget' => '_blank',
2833 'title' => 'Title of the file',
2835 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2837 'Link to file with attributes and additional class and href and title' => [
2840 'parameter' => 'fileadmin/foo.bar',
2841 'ATagParams' => 'class="file-class" href="foo-bar" title="foo-bar"',
2842 'fileTarget' => '_blank',
2843 'title' => 'Title of the file',
2845 '<a href="fileadmin/foo.bar" title="foo-bar" target="_blank" class="file-class">My file</a>',
2847 'Link to file with attributes and empty ATagParams' => [
2850 'parameter' => 'fileadmin/foo.bar',
2852 'fileTarget' => '_blank',
2853 'title' => 'Title of the file',
2855 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank">My file</a>',
2857 'Link to file with attributes in parameter' => [
2860 'parameter' => 'fileadmin/foo.bar _blank file-class "Title of the file"',
2862 '<a href="fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2864 'Link to file with script tag in name' => [
2867 'parameter' => 'fileadmin/<script>alert(123)</script>',
2869 '<a href="fileadmin/<script>alert(123)</script>">fileadmin/<script>alert(123)</script></a>',
2876 * @param string $linkText
2877 * @param array $configuration
2878 * @param string $expectedResult
2879 * @dataProvider typolinkReturnsCorrectLinksFilesDataProvider
2881 public function typolinkReturnsCorrectLinksFiles($linkText, $configuration, $expectedResult)
2883 $templateServiceObjectMock = $this->getMockBuilder(TemplateService
::class)
2884 ->setMethods(['dummy'])
2886 $templateServiceObjectMock->setup
= [
2888 'parseFunc.' => $this->getLibParseFunc(),
2891 $typoScriptFrontendControllerMockObject = $this->createMock(TypoScriptFrontendController
::class);
2892 $typoScriptFrontendControllerMockObject->config
= [
2895 $typoScriptFrontendControllerMockObject->tmpl
= $templateServiceObjectMock;
2896 $GLOBALS['TSFE'] = $typoScriptFrontendControllerMockObject;
2898 $this->subject
->_set('typoScriptFrontendController', $typoScriptFrontendControllerMockObject);
2900 $this->assertEquals($expectedResult, $this->subject
->typoLink($linkText, $configuration));
2906 public function typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider()
2912 'parameter' => 'fileadmin/foo.bar',
2915 '<a href="/fileadmin/foo.bar">My file</a>',
2917 'Link to file with longer absRefPrefix' => [
2920 'parameter' => 'fileadmin/foo.bar',
2923 '<a href="/sub/fileadmin/foo.bar">My file</a>',
2925 'Link to absolute file' => [
2928 'parameter' => '/images/foo.bar',
2931 '<a href="/images/foo.bar">My file</a>',
2933 'Link to absolute file with longer absRefPrefix' => [
2936 'parameter' => '/images/foo.bar',
2939 '<a href="/images/foo.bar">My file</a>',
2941 'Link to absolute file with identical longer absRefPrefix' => [
2944 'parameter' => '/sub/fileadmin/foo.bar',
2947 '<a href="/sub/fileadmin/foo.bar">My file</a>',
2949 'Link to file with empty absRefPrefix' => [
2952 'parameter' => 'fileadmin/foo.bar',
2955 '<a href="fileadmin/foo.bar">My file</a>',
2957 'Link to absolute file with empty absRefPrefix' => [
2960 'parameter' => '/fileadmin/foo.bar',
2963 '<a href="/fileadmin/foo.bar">My file</a>',
2965 'Link to file with attributes with absRefPrefix' => [
2968 'parameter' => 'fileadmin/foo.bar',
2969 'ATagParams' => 'class="file-class"',
2970 'fileTarget' => '_blank',
2971 'title' => 'Title of the file',
2974 '<a href="/fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2976 'Link to file with attributes with longer absRefPrefix' => [
2979 'parameter' => 'fileadmin/foo.bar',
2980 'ATagParams' => 'class="file-class"',
2981 'fileTarget' => '_blank',
2982 'title' => 'Title of the file',
2985 '<a href="/sub/fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2987 'Link to absolute file with attributes with absRefPrefix' => [
2990 'parameter' => '/images/foo.bar',
2991 'ATagParams' => 'class="file-class"',
2992 'fileTarget' => '_blank',
2993 'title' => 'Title of the file',
2996 '<a href="/images/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
2998 'Link to absolute file with attributes with longer absRefPrefix' => [
3001 'parameter' => '/images/foo.bar',
3002 'ATagParams' => 'class="file-class"',
3003 'fileTarget' => '_blank',
3004 'title' => 'Title of the file',
3007 '<a href="/images/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
3009 'Link to absolute file with attributes with identical longer absRefPrefix' => [
3012 'parameter' => '/sub/fileadmin/foo.bar',
3013 'ATagParams' => 'class="file-class"',
3014 'fileTarget' => '_blank',
3015 'title' => 'Title of the file',
3018 '<a href="/sub/fileadmin/foo.bar" title="Title of the file" target="_blank" class="file-class">My file</a>',
3025 * @param string $linkText
3026 * @param array $configuration
3027 * @param string $absRefPrefix
3028 * @param string $expectedResult
3029 * @dataProvider typolinkReturnsCorrectLinksForFilesWithAbsRefPrefixDataProvider
3031 public function typolinkReturnsCorrectLinksForFilesWithAbsRefPrefix($linkText, $configuration, $absRefPrefix, $expectedResult)
3033 $templateServiceObjectMock = $this->getMockBuilder(TemplateService
::class)
3034 ->setMethods(['dummy'])
3036 $templateServiceObjectMock->setup
= [