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