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