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