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