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