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