[!!!][TASK] Extract testing framework for TYPO3
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Functional / ContentObject / ContentObjectRendererTest.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Tests\Functional\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 TYPO3\CMS\Core\TypoScript\TemplateService;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
20 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
21 use TYPO3\CMS\Frontend\Page\PageRepository;
22
23 /**
24 * Testcase for TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
25 */
26 class ContentObjectRendererTest extends \TYPO3\CMS\Components\TestingFramework\Core\FunctionalTestCase
27 {
28 /**
29 * @var ContentObjectRenderer
30 */
31 protected $subject;
32
33 protected function setUp()
34 {
35 parent::setUp();
36
37 $typoScriptFrontendController = GeneralUtility::makeInstance(
38 TypoScriptFrontendController::class,
39 null,
40 1,
41 0
42 );
43 $typoScriptFrontendController->sys_page = GeneralUtility::makeInstance(PageRepository::class);
44 $typoScriptFrontendController->tmpl = GeneralUtility::makeInstance(TemplateService::class);
45 $GLOBALS['TSFE'] = $typoScriptFrontendController;
46
47 $this->subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
48 }
49
50 /**
51 * Data provider for the getQuery test
52 *
53 * @return array multi-dimensional array with the second level like this:
54 * @see getQuery
55 */
56 public function getQueryDataProvider(): array
57 {
58 $data = [
59 'testing empty conf' => [
60 'tt_content',
61 [],
62 [
63 'SELECT' => '*'
64 ]
65 ],
66 'testing #17284: adding uid/pid for workspaces' => [
67 'tt_content',
68 [
69 'selectFields' => 'header,bodytext'
70 ],
71 [
72 'SELECT' => 'header,bodytext, `tt_content`.`uid` AS `uid`, `tt_content`.`pid` AS `pid`, `tt_content`.`t3ver_state` AS `t3ver_state`'
73 ]
74 ],
75 'testing #17284: no need to add' => [
76 'tt_content',
77 [
78 'selectFields' => 'tt_content.*'
79 ],
80 [
81 'SELECT' => 'tt_content.*'
82 ]
83 ],
84 'testing #17284: no need to add #2' => [
85 'tt_content',
86 [
87 'selectFields' => '*'
88 ],
89 [
90 'SELECT' => '*'
91 ]
92 ],
93 'testing #29783: joined tables, prefix tablename' => [
94 'tt_content',
95 [
96 'selectFields' => 'tt_content.header,be_users.username',
97 'join' => 'be_users ON tt_content.cruser_id = be_users.uid'
98 ],
99 [
100 'SELECT' => 'tt_content.header,be_users.username, `tt_content`.`uid` AS `uid`, `tt_content`.`pid` AS `pid`, `tt_content`.`t3ver_state` AS `t3ver_state`'
101 ]
102 ],
103 'testing #34152: single count(*), add nothing' => [
104 'tt_content',
105 [
106 'selectFields' => 'count(*)'
107 ],
108 [
109 'SELECT' => 'count(*)'
110 ]
111 ],
112 'testing #34152: single max(crdate), add nothing' => [
113 'tt_content',
114 [
115 'selectFields' => 'max(crdate)'
116 ],
117 [
118 'SELECT' => 'max(crdate)'
119 ]
120 ],
121 'testing #34152: single min(crdate), add nothing' => [
122 'tt_content',
123 [
124 'selectFields' => 'min(crdate)'
125 ],
126 [
127 'SELECT' => 'min(crdate)'
128 ]
129 ],
130 'testing #34152: single sum(is_siteroot), add nothing' => [
131 'tt_content',
132 [
133 'selectFields' => 'sum(is_siteroot)'
134 ],
135 [
136 'SELECT' => 'sum(is_siteroot)'
137 ]
138 ],
139 'testing #34152: single avg(crdate), add nothing' => [
140 'tt_content',
141 [
142 'selectFields' => 'avg(crdate)'
143 ],
144 [
145 'SELECT' => 'avg(crdate)'
146 ]
147 ]
148 ];
149
150 return $data;
151 }
152
153 /**
154 * Check if sanitizeSelectPart works as expected
155 *
156 * @dataProvider getQueryDataProvider
157 * @test
158 * @param string $table
159 * @param array $conf
160 * @param array $expected
161 */
162 public function getQuery(string $table, array $conf, array $expected)
163 {
164 $GLOBALS['TCA'] = [
165 'pages' => [
166 'ctrl' => [
167 'enablecolumns' => [
168 'disabled' => 'hidden'
169 ]
170 ]
171 ],
172 'tt_content' => [
173 'ctrl' => [
174 'enablecolumns' => [
175 'disabled' => 'hidden'
176 ],
177 'versioningWS' => true
178 ]
179 ],
180 ];
181
182 $result = $this->subject->getQuery($table, $conf, true);
183 foreach ($expected as $field => $value) {
184 $this->assertEquals($value, $result[$field]);
185 }
186 }
187
188 /**
189 * @test
190 */
191 public function getQueryCallsGetTreeListWithNegativeValuesIfRecursiveIsSet()
192 {
193 $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['getTreeList']);
194 $this->subject->start([], 'tt_content');
195
196 $conf = [
197 'recursive' => '15',
198 'pidInList' => '16, -35'
199 ];
200
201 $this->subject->expects($this->at(0))
202 ->method('getTreeList')
203 ->with(-16, 15)
204 ->will($this->returnValue('15,16'));
205 $this->subject->expects($this->at(1))
206 ->method('getTreeList')
207 ->with(-35, 15)
208 ->will($this->returnValue('15,35'));
209
210 $this->subject->getQuery('tt_content', $conf, true);
211 }
212
213 /**
214 * @test
215 */
216 public function getQueryCallsGetTreeListWithCurrentPageIfThisIsSet()
217 {
218 $GLOBALS['TSFE']->id = 27;
219
220 $this->subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['getTreeList']);
221 $this->subject->start([], 'tt_content');
222
223 $conf = [
224 'pidInList' => 'this',
225 'recursive' => '4'
226 ];
227
228 $this->subject->expects($this->once())
229 ->method('getTreeList')
230 ->with(-27)
231 ->will($this->returnValue('27'));
232
233 $this->subject->getQuery('tt_content', $conf, true);
234 }
235
236 /**
237 * @return array
238 */
239 public function getWhereReturnCorrectQueryDataProvider()
240 {
241 return [
242 [
243 [
244 'tt_content' => [
245 'ctrl' => [
246 ],
247 'columns' => [
248 ]
249 ],
250 ],
251 'tt_content',
252 [
253 'uidInList' => '42',
254 'pidInList' => 43,
255 'where' => 'tt_content.cruser_id=5',
256 'groupBy' => 'tt_content.title',
257 'orderBy' => 'tt_content.sorting',
258 ],
259 'WHERE (`tt_content`.`uid` IN (42)) AND (`tt_content`.`pid` IN (43)) AND (tt_content.cruser_id=5) GROUP BY `tt_content`.`title` ORDER BY `tt_content`.`sorting`',
260 ],
261 [
262 [
263 'tt_content' => [
264 'ctrl' => [
265 'delete' => 'deleted',
266 'enablecolumns' => [
267 'disabled' => 'hidden',
268 'starttime' => 'startdate',
269 'endtime' => 'enddate',
270 ],
271 'languageField' => 'sys_language_uid',
272 'transOrigPointerField' => 'l18n_parent',
273 ],
274 'columns' => [
275 ]
276 ],
277 ],
278 'tt_content',
279 [
280 'uidInList' => 42,
281 'pidInList' => 43,
282 'where' => 'tt_content.cruser_id=5',
283 'groupBy' => 'tt_content.title',
284 'orderBy' => 'tt_content.sorting',
285 ],
286 'WHERE (`tt_content`.`uid` IN (42)) AND (`tt_content`.`pid` IN (43)) AND (tt_content.cruser_id=5) AND (`tt_content`.`sys_language_uid` = 13) AND ((`tt_content`.`deleted` = 0) AND (`tt_content`.`hidden` = 0) AND (`tt_content`.`startdate` <= 4242) AND ((`tt_content`.`enddate` = 0) OR (`tt_content`.`enddate` > 4242))) GROUP BY `tt_content`.`title` ORDER BY `tt_content`.`sorting`',
287 ],
288 [
289 [
290 'tt_content' => [
291 'ctrl' => [
292 'languageField' => 'sys_language_uid',
293 'transOrigPointerField' => 'l18n_parent',
294 ],
295 'columns' => [
296 ]
297 ],
298 ],
299 'tt_content',
300 [
301 'uidInList' => 42,
302 'pidInList' => 43,
303 'where' => 'tt_content.cruser_id=5',
304 'languageField' => 0,
305 ],
306 'WHERE (`tt_content`.`uid` IN (42)) AND (`tt_content`.`pid` IN (43)) AND (tt_content.cruser_id=5)',
307 ],
308 ];
309 }
310
311 /**
312 * @test
313 * @param array $tca
314 * @param string $table
315 * @param array $configuration
316 * @param string $expectedResult
317 * @dataProvider getWhereReturnCorrectQueryDataProvider
318 */
319 public function getWhereReturnCorrectQuery(array $tca, string $table, array $configuration, string $expectedResult)
320 {
321 $GLOBALS['TCA'] = $tca;
322 $GLOBALS['SIM_ACCESS_TIME'] = '4242';
323 $GLOBALS['TSFE']->sys_language_content = 13;
324 /** @var \PHPUnit_Framework_MockObject_MockObject|ContentObjectRenderer $contentObjectRenderer */
325 $contentObjectRenderer = $this->getMockBuilder(ContentObjectRenderer::class)
326 ->setMethods(['checkPidArray'])
327 ->getMock();
328 $contentObjectRenderer->expects($this->any())
329 ->method('checkPidArray')
330 ->willReturn(explode(',', $configuration['pidInList']));
331
332 // Embed the enable fields string into the expected result as the database
333 // connection is still unconfigured when the data provider is being run.
334 $expectedResult = sprintf($expectedResult, $GLOBALS['TSFE']->sys_page->enableFields($table));
335
336 $this->assertSame($expectedResult, $contentObjectRenderer->getWhere($table, $configuration));
337 }
338
339 /**
340 * @return array
341 */
342 public function typolinkReturnsCorrectLinksForPagesDataProvider()
343 {
344 return [
345 'Link to page' => [
346 'My page',
347 [
348 'parameter' => 42,
349 ],
350 [
351 'uid' => 42,
352 'title' => 'Page title',
353 ],
354 '<a href="index.php?id=42">My page</a>',
355 ],
356 'Link to page without link text' => [
357 '',
358 [
359 'parameter' => 42,
360 ],
361 [
362 'uid' => 42,
363 'title' => 'Page title',
364 ],
365 '<a href="index.php?id=42">Page title</a>',
366 ],
367 'Link to page with attributes' => [
368 'My page',
369 [
370 'parameter' => '42',
371 'ATagParams' => 'class="page-class"',
372 'target' => '_self',
373 'title' => 'Link to internal page',
374 ],
375 [
376 'uid' => 42,
377 'title' => 'Page title',
378 ],
379 '<a href="index.php?id=42" title="Link to internal page" target="_self" class="page-class">My page</a>',
380 ],
381 'Link to page with attributes in parameter' => [
382 'My page',
383 [
384 'parameter' => '42 _self page-class "Link to internal page"',
385 ],
386 [
387 'uid' => 42,
388 'title' => 'Page title',
389 ],
390 '<a href="index.php?id=42" title="Link to internal page" target="_self" class="page-class">My page</a>',
391 ],
392 'Link to page with bold tag in title' => [
393 '',
394 [
395 'parameter' => 42,
396 ],
397 [
398 'uid' => 42,
399 'title' => 'Page <b>title</b>',
400 ],
401 '<a href="index.php?id=42">Page <b>title</b></a>',
402 ],
403 'Link to page with script tag in title' => [
404 '',
405 [
406 'parameter' => 42,
407 ],
408 [
409 'uid' => 42,
410 'title' => '<script>alert(123)</script>Page title',
411 ],
412 '<a href="index.php?id=42">&lt;script&gt;alert(123)&lt;/script&gt;Page title</a>',
413 ],
414 ];
415 }
416
417 /**
418 * @test
419 * @param string $linkText
420 * @param array $configuration
421 * @param array $pageArray
422 * @param string $expectedResult
423 * @dataProvider typolinkReturnsCorrectLinksForPagesDataProvider
424 */
425 public function typolinkReturnsCorrectLinksForPages($linkText, $configuration, $pageArray, $expectedResult)
426 {
427 $pageRepositoryMockObject = $this->getMockBuilder(PageRepository::class)
428 ->setMethods(['getPage'])
429 ->getMock();
430 $pageRepositoryMockObject->expects($this->any())->method('getPage')->willReturn($pageArray);
431
432 $typoScriptFrontendController = GeneralUtility::makeInstance(
433 TypoScriptFrontendController::class,
434 null,
435 1,
436 0
437 );
438 $typoScriptFrontendController->config = [
439 'config' => [],
440 'mainScript' => 'index.php',
441 ];
442 $typoScriptFrontendController->sys_page = $pageRepositoryMockObject;
443 $typoScriptFrontendController->tmpl = GeneralUtility::makeInstance(TemplateService::class);
444 $typoScriptFrontendController->tmpl->setup = [
445 'lib.' => [
446 'parseFunc.' => $this->getLibParseFunc(),
447 ],
448 ];
449 $GLOBALS['TSFE'] = $typoScriptFrontendController;
450
451 $subject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
452 $this->assertEquals($expectedResult, $subject->typoLink($linkText, $configuration));
453 }
454
455 /**
456 * @return array
457 */
458 protected function getLibParseTarget()
459 {
460 return [
461 'override' => '',
462 'override.' => [
463 'if.' => [
464 'isTrue.' => [
465 'data' => 'TSFE:dtdAllowsFrames',
466 ],
467 ],
468 ],
469 ];
470 }
471
472 /**
473 * @return array
474 */
475 protected function getLibParseFunc()
476 {
477 return [
478 'makelinks' => '1',
479 'makelinks.' => [
480 'http.' => [
481 'keep' => '{$styles.content.links.keep}',
482 'extTarget' => '',
483 'extTarget.' => $this->getLibParseTarget(),
484 'mailto.' => [
485 'keep' => 'path',
486 ],
487 ],
488 ],
489 'tags' => [
490 'link' => 'TEXT',
491 'link.' => [
492 'current' => '1',
493 'typolink.' => [
494 'parameter.' => [
495 'data' => 'parameters : allParams',
496 ],
497 'extTarget.' => $this->getLibParseTarget(),
498 'target.' => $this->getLibParseTarget(),
499 ],
500 'parseFunc.' => [
501 'constants' => '1',
502 ],
503 ],
504 ],
505
506 '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',
507 'denyTags' => '*',
508 'sword' => '<span class="csc-sword">|</span>',
509 'constants' => '1',
510 'nonTypoTagStdWrap.' => [
511 'HTMLparser' => '1',
512 'HTMLparser.' => [
513 'keepNonMatchedTags' => '1',
514 'htmlSpecialChars' => '2',
515 ],
516 ],
517 ];
518 }
519 }