‪TYPO3CMS  ‪main
ContentObjectRendererTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use PHPUnit\Framework\MockObject\MockObject;
23 use Psr\EventDispatcher\EventDispatcherInterface;
24 use Psr\Http\Message\ServerRequestInterface;
25 use Psr\Log\NullLogger;
26 use Symfony\Component\DependencyInjection\Container;
28 use ‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface as CacheFrontendInterface;
51 use TYPO3\CMS\Core\Package\PackageManager;
96 use TYPO3\CMS\Frontend\Typolink\LinkResult;
98 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
99 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
100 
101 final class ‪ContentObjectRendererTest extends UnitTestCase
102 {
103  protected bool ‪$resetSingletonInstances = true;
104  protected bool ‪$backupEnvironment = true;
105 
106  private ‪ContentObjectRenderer&MockObject&AccessibleObjectInterface ‪$subject;
107  private ‪TypoScriptFrontendController&MockObject&AccessibleObjectInterface ‪$frontendControllerMock;
109 
113  private array ‪$contentObjectMap = [
114  'TEXT' => TextContentObject::class,
115  'CASE' => CaseContentObject::class,
116  'COBJ_ARRAY' => ContentObjectArrayContentObject::class,
117  'COA' => ContentObjectArrayContentObject::class,
118  'COA_INT' => ContentObjectArrayInternalContentObject::class,
119  'USER' => UserContentObject::class,
120  'USER_INT' => UserInternalContentObject::class,
121  'FILES' => FilesContentObject::class,
122  'IMAGE' => ImageContentObject::class,
123  'IMG_RESOURCE' => ImageResourceContentObject::class,
124  'CONTENT' => ContentContentObject::class,
125  'RECORDS' => RecordsContentObject::class,
126  'HMENU' => HierarchicalMenuContentObject::class,
127  'CASEFUNC' => CaseContentObject::class,
128  'LOAD_REGISTER' => LoadRegisterContentObject::class,
129  'RESTORE_REGISTER' => RestoreRegisterContentObject::class,
130  'FLUIDTEMPLATE' => FluidTemplateContentObject::class,
131  'SVG' => ScalableVectorGraphicsContentObject::class,
132  ];
133 
134  protected function ‪setUp(): void
135  {
136  parent::setUp();
137 
138  ‪$GLOBALS['SIM_ACCESS_TIME'] = 1534278180;
139  $this->frontendControllerMock =
140  $this->getAccessibleMock(
141  TypoScriptFrontendController::class,
142  ['sL'],
143  [],
144  '',
145  false
146  );
147  $this->frontendControllerMock->_set('context', new ‪Context());
148  $this->frontendControllerMock->config = [];
149 
150  $this->cacheManagerMock = $this->getMockBuilder(CacheManager::class)->disableOriginalConstructor()->getMock();
151  GeneralUtility::setSingletonInstance(CacheManager::class, $this->cacheManagerMock);
152 
153  $this->subject = $this->getAccessibleMock(
154  ContentObjectRenderer::class,
155  ['getResourceFactory', 'getEnvironmentVariable'],
156  [$this->frontendControllerMock]
157  );
158 
159  $logger = new NullLogger();
160  $this->subject->setLogger($logger);
161  $request = new ‪ServerRequest();
162  $this->subject->setRequest($request);
163 
164  $contentObjectFactoryMock = $this->createContentObjectFactoryMock();
165  $cObj = ‪$this->subject;
166  foreach ($this->contentObjectMap as ‪$name => $className) {
167  $contentObjectFactoryMock->addGetContentObjectCallback(
168  ‪$name,
169  $className,
170  $request,
171  $cObj,
172  $this->getMockBuilder(DataProcessorRegistry::class)->disableOriginalConstructor()->getMock()
173  );
174  }
175  $container = new Container();
176  $container->set(ContentObjectFactory::class, $contentObjectFactoryMock);
177  $container->set(EventDispatcherInterface::class, new ‪NoopEventDispatcher());
178  GeneralUtility::setContainer($container);
179 
180  $this->subject->start([], 'tt_content');
181  }
182 
184  // Utility functions
186 
192  private function ‪handleCharset(string &‪$subject, string &$expected): void
193  {
194  ‪$subject = mb_convert_encoding(‪$subject, 'utf-8', 'iso-8859-1');
195  $expected = mb_convert_encoding($expected, 'utf-8', 'iso-8859-1');
196  }
197 
198  private static function ‪getLibParseFunc_RTE(): array
199  {
200  return [
201  'parseFunc' => '',
202  'parseFunc.' => [
203  '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',
204  'constants' => '1',
205  'denyTags' => '*',
206  'externalBlocks' => 'article, aside, blockquote, div, dd, dl, footer, header, nav, ol, section, table, ul, pre',
207  'externalBlocks.' => [
208  'article.' => [
209  'callRecursive' => '1',
210  'stripNL' => '1',
211  ],
212  'aside.' => [
213  'callRecursive' => '1',
214  'stripNL' => '1',
215  ],
216  'blockquote.' => [
217  'callRecursive' => '1',
218  'stripNL' => '1',
219  ],
220  'dd.' => [
221  'callRecursive' => '1',
222  'stripNL' => '1',
223  ],
224  'div.' => [
225  'callRecursive' => '1',
226  'stripNL' => '1',
227  ],
228  'dl.' => [
229  'callRecursive' => '1',
230  'stripNL' => '1',
231  ],
232  'footer.' => [
233  'callRecursive' => '1',
234  'stripNL' => '1',
235  ],
236  'header.' => [
237  'callRecursive' => '1',
238  'stripNL' => '1',
239  ],
240  'nav.' => [
241  'callRecursive' => '1',
242  'stripNL' => '1',
243  ],
244  'ol.' => [
245  'callRecursive' => '1',
246  'stripNL' => '1',
247  ],
248  'section.' => [
249  'callRecursive' => '1',
250  'stripNL' => '1',
251  ],
252  'table.' => [
253  'HTMLtableCells' => '1',
254  'HTMLtableCells.' => [
255  'addChr10BetweenParagraphs' => '1',
256  'default.' => [
257  'stdWrap.' => [
258  'parseFunc' => '=< lib.parseFunc_RTE',
259  'parseFunc.' => [
260  'nonTypoTagStdWrap.' => [
261  'encapsLines.' => [
262  'nonWrappedTag' => '',
263  ],
264  ],
265  ],
266  ],
267  ],
268  ],
269  'stdWrap.' => [
270  'HTMLparser' => '1',
271  'HTMLparser.' => [
272  'keepNonMatchedTags' => '1',
273  'tags.' => [
274  'table.' => [
275  'fixAttrib.' => [
276  'class.' => [
277  'always' => '1',
278  'default' => 'contenttable',
279  'list' => 'contenttable',
280  ],
281  ],
282  ],
283  ],
284  ],
285  ],
286  'stripNL' => '1',
287  ],
288  'ul.' => [
289  'callRecursive' => '1',
290  'stripNL' => '1',
291  ],
292  ],
293  'makelinks' => '1',
294  'makelinks.' => [
295  'http.' => [
296  'extTarget.' => [
297  'override' => '_blank',
298  ],
299  'keep' => 'path',
300  ],
301  ],
302  'nonTypoTagStdWrap.' => [
303  'encapsLines.' => [
304  'addAttributes.' => [
305  'P.' => [
306  'class' => 'bodytext',
307  'class.' => [
308  'setOnly' => 'blank',
309  ],
310  ],
311  ],
312  'encapsTagList' => 'p,pre,h1,h2,h3,h4,h5,h6,hr,dt,li',
313  'innerStdWrap_all.' => [
314  'ifBlank' => '&nbsp;',
315  ],
316  'nonWrappedTag' => 'P',
317  'remapTag.' => [
318  'DIV' => 'P',
319  ],
320  ],
321  'HTMLparser' => '1',
322  'HTMLparser.' => [
323  'htmlSpecialChars' => '2',
324  'keepNonMatchedTags' => '1',
325  ],
326  ],
327  'sword' => '<span class="csc-sword">|</span>',
328  'tags.' => [
329  'link' => 'TEXT',
330  'link.' => [
331  'current' => '1',
332  'parseFunc.' => [
333  'constants' => '1',
334  ],
335  'typolink.' => [
336  'directImageLink' => false,
337  'extTarget.' => [
338  'override' => '',
339  ],
340  'parameter.' => [
341  'data' => 'parameters : allParams',
342  ],
343  'target.' => [
344  'override' => '',
345  ],
346  ],
347  ],
348  ],
349  ],
350  ];
351  }
352 
353  private function ‪createSiteWithLanguage(array $languageConfiguration): ‪Site
354  {
355  return new ‪Site('test', 1, [
356  'identifier' => 'test',
357  'rootPageId' => 1,
358  'base' => '/',
359  'languages' => [
360  array_merge(
361  $languageConfiguration,
362  [
363  'base' => '/',
364  ]
365  ),
366  ],
367  ]);
368  }
369 
371  // Tests related to getContentObject
373 
376  #[Test]
378  {
379  $object = $this->subject->getContentObject('FOO');
380  self::assertNull($object);
381  }
382 
384  // Tests concerning crop
386 
390  #[Test]
391  public function ‪cropIsMultibyteSafe(): void
392  {
393  self::assertEquals('бла', $this->subject->crop('бла', '3|...'));
394  }
395 
397  // Tests concerning cropHTML
399 
406  public static function ‪cropHTMLDataProvider(): array
407  {
408  $plainText = 'Kasper Sk' . chr(229) . 'rh' . chr(248)
409  . 'j implemented the original version of the crop function.';
410  $textWithMarkup = '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
411  . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> the '
412  . 'original version of the crop function.';
413  $textWithEntities = 'Kasper Sk&aring;rh&oslash;j implemented the; '
414  . 'original version of the crop function.';
415  $textWithLinebreaks = "Lorem ipsum dolor sit amet,\n"
416  . "consetetur sadipscing elitr,\n"
417  . 'sed diam nonumy eirmod tempor invidunt ut labore e'
418  . 't dolore magna aliquyam';
419  $textWith2000Chars = 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ips &amp;. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vesti&amp;';
420  $textWith1000AmpHtmlEntity = str_repeat('&amp;', 1000);
421  $textWith2000AmpHtmlEntity = str_repeat('&amp;', 2000);
422 
423  return [
424  'plain text; 11|...' => [
425  'Kasper Sk' . chr(229) . 'r...',
426  $plainText,
427  '11|...',
428  ],
429  'plain text; -58|...' => [
430  '...h' . chr(248) . 'j implemented the original version of '
431  . 'the crop function.',
432  $plainText,
433  '-58|...',
434  ],
435  'plain text; 4|...|1' => [
436  'Kasp...',
437  $plainText,
438  '4|...|1',
439  ],
440  'plain text; 20|...|1' => [
441  'Kasper Sk' . chr(229) . 'rh' . chr(248) . 'j...',
442  $plainText,
443  '20|...|1',
444  ],
445  'plain text; -5|...|1' => [
446  '...tion.',
447  $plainText,
448  '-5|...|1',
449  ],
450  'plain text; -49|...|1' => [
451  '...the original version of the crop function.',
452  $plainText,
453  '-49|...|1',
454  ],
455  'text with markup; 11|...' => [
456  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
457  . chr(229) . 'r...</a></strong>',
458  $textWithMarkup,
459  '11|...',
460  ],
461  'text with markup; 13|...' => [
462  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
463  . chr(229) . 'rh' . chr(248) . '...</a></strong>',
464  $textWithMarkup,
465  '13|...',
466  ],
467  'text with markup; 14|...' => [
468  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
469  . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
470  $textWithMarkup,
471  '14|...',
472  ],
473  'text with markup; 15|...' => [
474  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
475  . chr(229) . 'rh' . chr(248) . 'j</a> ...</strong>',
476  $textWithMarkup,
477  '15|...',
478  ],
479  'text with markup; 29|...' => [
480  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
481  . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> '
482  . 'th...',
483  $textWithMarkup,
484  '29|...',
485  ],
486  'text with markup; -58|...' => [
487  '<strong><a href="mailto:kasper@typo3.org">...h' . chr(248)
488  . 'j</a> implemented</strong> the original version of the crop '
489  . 'function.',
490  $textWithMarkup,
491  '-58|...',
492  ],
493  'text with markup 4|...|1' => [
494  '<strong><a href="mailto:kasper@typo3.org">Kasp...</a>'
495  . '</strong>',
496  $textWithMarkup,
497  '4|...|1',
498  ],
499  'text with markup; 11|...|1' => [
500  '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
501  . '</strong>',
502  $textWithMarkup,
503  '11|...|1',
504  ],
505  'text with markup; 13|...|1' => [
506  '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
507  . '</strong>',
508  $textWithMarkup,
509  '13|...|1',
510  ],
511  'text with markup; 14|...|1' => [
512  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
513  . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
514  $textWithMarkup,
515  '14|...|1',
516  ],
517  'text with markup; 15|...|1' => [
518  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
519  . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
520  $textWithMarkup,
521  '15|...|1',
522  ],
523  'text with markup; 29|...|1' => [
524  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
525  . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong>...',
526  $textWithMarkup,
527  '29|...|1',
528  ],
529  'text with markup; -66|...|1' => [
530  '<strong><a href="mailto:kasper@typo3.org">...Sk' . chr(229)
531  . 'rh' . chr(248) . 'j</a> implemented</strong> the original v'
532  . 'ersion of the crop function.',
533  $textWithMarkup,
534  '-66|...|1',
535  ],
536  'text with entities 9|...' => [
537  'Kasper Sk...',
538  $textWithEntities,
539  '9|...',
540  ],
541  'text with entities 10|...' => [
542  'Kasper Sk&aring;...',
543  $textWithEntities,
544  '10|...',
545  ],
546  'text with entities 11|...' => [
547  'Kasper Sk&aring;r...',
548  $textWithEntities,
549  '11|...',
550  ],
551  'text with entities 13|...' => [
552  'Kasper Sk&aring;rh&oslash;...',
553  $textWithEntities,
554  '13|...',
555  ],
556  'text with entities 14|...' => [
557  'Kasper Sk&aring;rh&oslash;j...',
558  $textWithEntities,
559  '14|...',
560  ],
561  'text with entities 15|...' => [
562  'Kasper Sk&aring;rh&oslash;j ...',
563  $textWithEntities,
564  '15|...',
565  ],
566  'text with entities 16|...' => [
567  'Kasper Sk&aring;rh&oslash;j i...',
568  $textWithEntities,
569  '16|...',
570  ],
571  'text with entities -57|...' => [
572  '...j implemented the; original version of the crop function.',
573  $textWithEntities,
574  '-57|...',
575  ],
576  'text with entities -58|...' => [
577  '...&oslash;j implemented the; original version of the crop '
578  . 'function.',
579  $textWithEntities,
580  '-58|...',
581  ],
582  'text with entities -59|...' => [
583  '...h&oslash;j implemented the; original version of the crop '
584  . 'function.',
585  $textWithEntities,
586  '-59|...',
587  ],
588  'text with entities 4|...|1' => [
589  'Kasp...',
590  $textWithEntities,
591  '4|...|1',
592  ],
593  'text with entities 9|...|1' => [
594  'Kasper...',
595  $textWithEntities,
596  '9|...|1',
597  ],
598  'text with entities 10|...|1' => [
599  'Kasper...',
600  $textWithEntities,
601  '10|...|1',
602  ],
603  'text with entities 11|...|1' => [
604  'Kasper...',
605  $textWithEntities,
606  '11|...|1',
607  ],
608  'text with entities 13|...|1' => [
609  'Kasper...',
610  $textWithEntities,
611  '13|...|1',
612  ],
613  'text with entities 14|...|1' => [
614  'Kasper Sk&aring;rh&oslash;j...',
615  $textWithEntities,
616  '14|...|1',
617  ],
618  'text with entities 15|...|1' => [
619  'Kasper Sk&aring;rh&oslash;j...',
620  $textWithEntities,
621  '15|...|1',
622  ],
623  'text with entities 16|...|1' => [
624  'Kasper Sk&aring;rh&oslash;j...',
625  $textWithEntities,
626  '16|...|1',
627  ],
628  'text with entities -57|...|1' => [
629  '...implemented the; original version of the crop function.',
630  $textWithEntities,
631  '-57|...|1',
632  ],
633  'text with entities -58|...|1' => [
634  '...implemented the; original version of the crop function.',
635  $textWithEntities,
636  '-58|...|1',
637  ],
638  'text with entities -59|...|1' => [
639  '...implemented the; original version of the crop function.',
640  $textWithEntities,
641  '-59|...|1',
642  ],
643  'text with dash in html-element 28|...|1' => [
644  'Some text with a link to <link email.address@example.org - '
645  . 'mail "Open email window">my...</link>',
646  'Some text with a link to <link email.address@example.org - m'
647  . 'ail "Open email window">my email.address@example.org<'
648  . '/link> and text after it',
649  '28|...|1',
650  ],
651  'html elements with dashes in attributes' => [
652  '<em data-foo="x">foobar</em>foo',
653  '<em data-foo="x">foobar</em>foo',
654  '9',
655  ],
656  'html elements with iframe embedded 24|...|1' => [
657  'Text with iframe <iframe src="//what.ever/"></iframe> and...',
658  'Text with iframe <iframe src="//what.ever/">'
659  . '</iframe> and text after it',
660  '24|...|1',
661  ],
662  'html elements with script tag embedded 24|...|1' => [
663  'Text with script <script>alert(\'foo\');</script> and...',
664  'Text with script <script>alert(\'foo\');</script> '
665  . 'and text after it',
666  '24|...|1',
667  ],
668  'text with linebreaks' => [
669  "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\ns"
670  . 'ed diam nonumy eirmod tempor invidunt ut labore e'
671  . 't dolore magna',
672  $textWithLinebreaks,
673  '121',
674  ],
675  'long text under the crop limit' => [
676  'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit' . ' ...',
677  $textWith2000Chars,
678  '962|...',
679  ],
680  'long text above the crop limit' => [
681  'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ips &amp;. N' . '...',
682  $textWith2000Chars,
683  '1000|...',
684  ],
685  'long text above the crop limit #2' => [
686  'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies nisi. Nam eget dui. Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ips &amp;. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vesti&amp;' . '...',
687  $textWith2000Chars . $textWith2000Chars,
688  '2000|...',
689  ],
690  // ensure that large number of html entities do not break the the regexp splittin
691  'long text with large number of html entities' => [
692  $textWith1000AmpHtmlEntity . '...',
693  $textWith2000AmpHtmlEntity,
694  '1000|...',
695  ],
696  ];
697  }
698 
711  #[DataProvider('cropHTMLDataProvider')]
712  #[Test]
713  public function ‪cropHTML(string $expect, string $content, string $conf): void
714  {
715  $this->‪handleCharset($content, $expect);
716  self::assertSame(
717  $expect,
718  $this->subject->cropHTML($content, $conf)
719  );
720  }
721 
727  public static function ‪roundDataProvider(): array
728  {
729  return [
730  // floats
731  'down' => [1.0, 1.11, []],
732  'up' => [2.0, 1.51, []],
733  'rounds up from x.50' => [2.0, 1.50, []],
734  'down with decimals' => [0.12, 0.1231, ['decimals' => 2]],
735  'up with decimals' => [0.13, 0.1251, ['decimals' => 2]],
736  'ceil' => [1.0, 0.11, ['roundType' => 'ceil']],
737  'ceil does not accept decimals' => [
738  1.0,
739  0.111,
740  [
741  'roundType' => 'ceil',
742  'decimals' => 2,
743  ],
744  ],
745  'floor' => [2.0, 2.99, ['roundType' => 'floor']],
746  'floor does not accept decimals' => [
747  2.0,
748  2.999,
749  [
750  'roundType' => 'floor',
751  'decimals' => 2,
752  ],
753  ],
754  'round, down' => [1.0, 1.11, ['roundType' => 'round']],
755  'round, up' => [2.0, 1.55, ['roundType' => 'round']],
756  'round does accept decimals' => [
757  5.56,
758  5.5555,
759  [
760  'roundType' => 'round',
761  'decimals' => 2,
762  ],
763  ],
764  // strings
765  'emtpy string' => [0.0, '', []],
766  'word string' => [0.0, 'word', []],
767  'float string' => [1.0, '1.123456789', []],
768  // other types
769  'null' => [0.0, null, []],
770  'false' => [0.0, false, []],
771  'true' => [1.0, true, []],
772  ];
773  }
774 
790  #[DataProvider('roundDataProvider')]
791  #[Test]
792  public function ‪round(float $expect, mixed $content, array $conf): void
793  {
794  self::assertSame(
795  $expect,
796  $this->subject->_call('round', $content, $conf)
797  );
798  }
799 
800  #[Test]
802  {
803  $stdWrapConfiguration = [
804  'noTrimWrap' => '|| 123|',
805  'stdWrap.' => [
806  'wrap' => '<b>|</b>',
807  ],
808  ];
809  self::assertSame(
810  '<b>Test</b> 123',
811  $this->subject->stdWrap('Test', $stdWrapConfiguration)
812  );
813  }
814 
815  #[Test]
816  public function ‪recursiveStdWrapIsOnlyCalledOnce(): void
817  {
818  $stdWrapConfiguration = [
819  'append' => 'TEXT',
820  'append.' => [
821  'data' => 'register:Counter',
822  ],
823  'stdWrap.' => [
824  'append' => 'LOAD_REGISTER',
825  'append.' => [
826  'Counter.' => [
827  'prioriCalc' => 'intval',
828  'cObject' => 'TEXT',
829  'cObject.' => [
830  'data' => 'register:Counter',
831  'wrap' => '|+1',
832  ],
833  ],
834  ],
835  ],
836  ];
837  self::assertSame(
838  'Counter:1',
839  $this->subject->stdWrap('Counter:', $stdWrapConfiguration)
840  );
841  }
842 
848  public static function ‪numberFormatDataProvider(): array
849  {
850  return [
851  'testing decimals' => [
852  '0.80',
853  0.8,
854  ['decimals' => 2],
855  ],
856  'testing decimals with input as string' => [
857  '0.80',
858  '0.8',
859  ['decimals' => 2],
860  ],
861  'testing dec_point' => [
862  '0,8',
863  0.8,
864  ['decimals' => 1, 'dec_point' => ','],
865  ],
866  'testing thousands_sep' => [
867  '1.000',
868  999.99,
869  [
870  'decimals' => 0,
871  'thousands_sep.' => ['char' => 46],
872  ],
873  ],
874  'testing mixture' => [
875  '1.281.731,5',
876  1281731.45,
877  [
878  'decimals' => 1,
879  'dec_point.' => ['char' => 44],
880  'thousands_sep.' => ['char' => 46],
881  ],
882  ],
883  ];
884  }
885 
889  #[DataProvider('numberFormatDataProvider')]
890  #[Test]
891  public function ‪numberFormat(string $expects, mixed $content, array $conf): void
892  {
893  self::assertSame(
894  $expects,
895  $this->subject->numberFormat($content, $conf)
896  );
897  }
898 
904  public static function ‪replacementDataProvider(): array
905  {
906  return [
907  'multiple replacements, including regex' => [
908  'There is an animal, an animal and an animal around the block! Yeah!',
909  'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
910  [
911  '20.' => [
912  'search' => '_',
913  'replace.' => ['char' => '32'],
914  ],
915  '120.' => [
916  'search' => 'in da hood',
917  'replace' => 'around the block',
918  ],
919  '130.' => [
920  'search' => '#a (Cat|Dog|Tiger)#i',
921  'replace' => 'an animal',
922  'useRegExp' => '1',
923  ],
924  ],
925  ],
926  'replacement with optionSplit, normal pattern' => [
927  'There1is2a3cat,3a3dog3and3a3tiger3in3da3hood!3Yeah!',
928  'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
929  [
930  '10.' => [
931  'search' => '_',
932  'replace' => '1 || 2 || 3',
933  'useOptionSplitReplace' => '1',
934  'useRegExp' => '0',
935  ],
936  ],
937  ],
938  'replacement with optionSplit, using regex' => [
939  'There is a tiny cat, a midsized dog and a big tiger in da hood! Yeah!',
940  'There is a cat, a dog and a tiger in da hood! Yeah!',
941  [
942  '10.' => [
943  'search' => '#(a) (Cat|Dog|Tiger)#i',
944  'replace' => '${1} tiny ${2} || ${1} midsized ${2} || ${1} big ${2}',
945  'useOptionSplitReplace' => '1',
946  'useRegExp' => '1',
947  ],
948  ],
949  ],
950  ];
951  }
952 
960  #[DataProvider('replacementDataProvider')]
961  #[Test]
962  public function ‪replacement(string $expects, string $content, array $conf): void
963  {
964  self::assertSame(
965  $expects,
966  $this->subject->_call('replacement', $content, $conf)
967  );
968  }
969 
975  public static function ‪calcAgeDataProvider(): array
976  {
977  return [
978  'minutes' => [
979  '2 min',
980  120,
981  ' min| hrs| days| yrs',
982  ],
983  'hours' => [
984  '2 hrs',
985  7200,
986  ' min| hrs| days| yrs',
987  ],
988  'days' => [
989  '7 days',
990  604800,
991  ' min| hrs| days| yrs',
992  ],
993  'day with provided singular labels' => [
994  '1 day',
995  86400,
996  ' min| hrs| days| yrs| min| hour| day| year',
997  ],
998  'years' => [
999  '45 yrs',
1000  1417997800,
1001  ' min| hrs| days| yrs',
1002  ],
1003  'different labels' => [
1004  '2 Minutes',
1005  120,
1006  ' Minutes| Hrs| Days| Yrs',
1007  ],
1008  'negative values' => [
1009  '-7 days',
1010  -604800,
1011  ' min| hrs| days| yrs',
1012  ],
1013  'default label values for wrong label input' => [
1014  '2 min',
1015  121,
1016  '10',
1017  ],
1018  'default singular label values for wrong label input' => [
1019  '1 year',
1020  31536000,
1021  '10',
1022  ],
1023  ];
1024  }
1025 
1029  #[DataProvider('calcAgeDataProvider')]
1030  #[Test]
1031  public function ‪calcAge(string $expect, int $timestamp, string $labels): void
1032  {
1033  self::assertSame(
1034  $expect,
1035  $this->subject->calcAge($timestamp, $labels)
1036  );
1037  }
1038 
1039  public static function ‪stdWrapReturnsExpectationDataProvider(): array
1040  {
1041  return [
1042  'Prevent silent bool conversion' => [
1043  '1+1',
1044  [
1045  'prioriCalc.' => [
1046  'wrap' => '|',
1047  ],
1048  ],
1049  '1+1',
1050  ],
1051  ];
1052  }
1053 
1054  #[DataProvider('stdWrapReturnsExpectationDataProvider')]
1055  #[Test]
1056  public function ‪stdWrapReturnsExpectation(string $content, array $configuration, string $expectation): void
1057  {
1058  self::assertSame($expectation, $this->subject->stdWrap($content, $configuration));
1059  }
1060 
1062  {
1063  return [
1064  'ifEmpty is not called if content is present as an non-empty string' => [
1065  'content' => 'some content',
1066  'ifEmptyShouldBeCalled' => false,
1067  ],
1068  'ifEmpty is not called if content is present as the string "1"' => [
1069  'content' => '1',
1070  'ifEmptyShouldBeCalled' => false,
1071  ],
1072  'ifEmpty is called if content is present as an empty string' => [
1073  'content' => '',
1074  'ifEmptyShouldBeCalled' => true,
1075  ],
1076  'ifEmpty is called if content is present as the string "0"' => [
1077  'content' => '0',
1078  'ifEmptyShouldBeCalled' => true,
1079  ],
1080  ];
1081  }
1082 
1083  #[DataProvider('stdWrapDoesOnlyCallIfEmptyIfTheTrimmedContentIsEmptyOrZeroDataProvider')]
1084  #[Test]
1085  public function ‪stdWrapDoesOnlyCallIfEmptyIfTheTrimmedContentIsEmptyOrZero(string $content, bool $ifEmptyShouldBeCalled): void
1086  {
1087  $conf = [
1088  'ifEmpty.' => [
1089  'cObject' => 'TEXT',
1090  ],
1091  ];
1092 
1093  ‪$subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['stdWrap_ifEmpty']);
1094  $request = new ‪ServerRequest();
1095  ‪$subject->‪setRequest($request);
1096  ‪$subject->expects(self::exactly(($ifEmptyShouldBeCalled ? 1 : 0)))
1097  ->method('stdWrap_ifEmpty');
1098 
1099  ‪$subject->‪stdWrap($content, $conf);
1100  }
1101 
1107  public static function ‪substringDataProvider(): array
1108  {
1109  return [
1110  'sub -1' => ['g', 'substring', '-1'],
1111  'sub -1,0' => ['g', 'substring', '-1,0'],
1112  'sub -1,-1' => ['', 'substring', '-1,-1'],
1113  'sub -1,1' => ['g', 'substring', '-1,1'],
1114  'sub 0' => ['substring', 'substring', '0'],
1115  'sub 0,0' => ['substring', 'substring', '0,0'],
1116  'sub 0,-1' => ['substrin', 'substring', '0,-1'],
1117  'sub 0,1' => ['s', 'substring', '0,1'],
1118  'sub 1' => ['ubstring', 'substring', '1'],
1119  'sub 1,0' => ['ubstring', 'substring', '1,0'],
1120  'sub 1,-1' => ['ubstrin', 'substring', '1,-1'],
1121  'sub 1,1' => ['u', 'substring', '1,1'],
1122  'sub' => ['substring', 'substring', ''],
1123  ];
1124  }
1125 
1133  #[DataProvider('substringDataProvider')]
1134  #[Test]
1135  public function ‪substring(string $expect, string $content, string $conf): void
1136  {
1137  self::assertSame($expect, $this->subject->substring($content, $conf));
1138  }
1139 
1140  public static function ‪getDataWithTypeGpDataProvider(): array
1141  {
1142  return [
1143  'Value in get-data' => ['onlyInGet', 'GetValue'],
1144  'Value in post-data' => ['onlyInPost', 'PostValue'],
1145  'Value in post-data overriding get-data' => ['inGetAndPost', 'ValueInPost'],
1146  ];
1147  }
1148 
1152  #[DataProvider('getDataWithTypeGpDataProvider')]
1153  #[Test]
1154  public function ‪getDataWithTypeGp(string $key, string $expectedValue): void
1155  {
1156  $queryArguments = [
1157  'onlyInGet' => 'GetValue',
1158  'inGetAndPost' => 'ValueInGet',
1159  ];
1160  $postParameters = [
1161  'onlyInPost' => 'PostValue',
1162  'inGetAndPost' => 'ValueInPost',
1163  ];
1164  $request = new ‪ServerRequest('https://example.com');
1165  $request = $request->withQueryParams($queryArguments)->withParsedBody($postParameters);
1166  $pageInformation = new ‪PageInformation();
1167  $pageInformation->setPageRecord([]);
1168  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1169  $this->subject->setRequest($request);
1170  self::assertEquals($expectedValue, $this->subject->getData('gp:' . $key));
1171  }
1172 
1176  #[Test]
1177  public function ‪getDataWithTypeGetenv(): void
1178  {
1179  $envName = ‪StringUtility::getUniqueId('frontendtest');
1180  $value = ‪StringUtility::getUniqueId('someValue');
1181  putenv($envName . '=' . $value);
1182  $pageInformation = new ‪PageInformation();
1183  $pageInformation->setPageRecord([]);
1184  $request = new ‪ServerRequest('https://example.com');
1185  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1186  $this->subject->setRequest($request);
1187  self::assertEquals($value, $this->subject->getData('getenv:' . $envName));
1188  }
1189 
1193  #[Test]
1194  public function ‪getDataWithTypeGetindpenv(): void
1195  {
1196  $pageInformation = new ‪PageInformation();
1197  $pageInformation->setPageRecord([]);
1198  $request = new ‪ServerRequest('https://example.com');
1199  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1200  $this->subject->setRequest($request);
1201  $this->subject->expects(self::once())->method('getEnvironmentVariable')
1202  ->with(self::equalTo('SCRIPT_FILENAME'))->willReturn('dummyPath');
1203  self::assertEquals('dummyPath', $this->subject->getData('getindpenv:SCRIPT_FILENAME'));
1204  }
1205 
1209  #[Test]
1210  public function ‪getDataWithTypeField(): void
1211  {
1212  $key = 'someKey';
1213  $value = 'someValue';
1214  $field = [$key => $value];
1215  self::assertEquals($value, $this->subject->getData('field:' . $key, $field));
1216  }
1217 
1222  #[Test]
1224  {
1225  $key = 'somekey|level1|level2';
1226  $value = 'somevalue';
1227  $field = ['somekey' => ['level1' => ['level2' => 'somevalue']]];
1228 
1229  self::assertEquals($value, $this->subject->getData('field:' . $key, $field));
1230  }
1231 
1233  {
1234  return [
1235  'no whitespace' => [
1236  'typoScriptPath' => 'file:current:uid',
1237  ],
1238  'always whitespace' => [
1239  'typoScriptPath' => 'file : current : uid',
1240  ],
1241  'mixed whitespace' => [
1242  'typoScriptPath' => 'file:current : uid',
1243  ],
1244  ];
1245  }
1246 
1250  #[DataProvider('getDataWithTypeFileReturnsUidOfFileObjectDataProvider')]
1251  #[Test]
1252  public function ‪getDataWithTypeFileReturnsUidOfFileObject(string $typoScriptPath): void
1253  {
1254  $pageInformation = new ‪PageInformation();
1255  $pageInformation->setPageRecord([]);
1256  $request = new ‪ServerRequest('https://example.com');
1257  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1258  $this->subject->setRequest($request);
1260  $file = $this->createMock(File::class);
1261  $file->expects(self::once())->method('getUid')->willReturn(‪$uid);
1262  $this->subject->setCurrentFile($file);
1263  self::assertEquals(‪$uid, $this->subject->getData($typoScriptPath));
1264  }
1265 
1269  #[Test]
1270  public function ‪getDataWithTypeParameters(): void
1271  {
1272  $pageInformation = new ‪PageInformation();
1273  $pageInformation->setPageRecord([]);
1274  $request = new ‪ServerRequest('https://example.com');
1275  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1276  $this->subject->setRequest($request);
1277  $key = ‪StringUtility::getUniqueId('someKey');
1278  $value = ‪StringUtility::getUniqueId('someValue');
1279  $this->subject->parameters[$key] = $value;
1280  self::assertEquals($value, $this->subject->getData('parameters:' . $key));
1281  }
1282 
1286  #[Test]
1287  public function ‪getDataWithTypeRegister(): void
1288  {
1289  $pageInformation = new ‪PageInformation();
1290  $pageInformation->setPageRecord([]);
1291  $request = new ‪ServerRequest('https://example.com');
1292  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1293  $this->subject->setRequest($request);
1294  $key = ‪StringUtility::getUniqueId('someKey');
1295  $value = ‪StringUtility::getUniqueId('someValue');
1297  ‪$GLOBALS['TSFE']->register[$key] = $value;
1298  self::assertEquals($value, $this->subject->getData('register:' . $key));
1299  }
1300 
1304  #[Test]
1305  public function ‪getDataWithTypeSession(): void
1306  {
1307  $frontendUser = $this->getMockBuilder(FrontendUserAuthentication::class)
1308  ->onlyMethods(['getSessionData'])
1309  ->getMock();
1310  $frontendUser->expects(self::once())->method('getSessionData')->with('myext')->willReturn([
1311  'mydata' => [
1312  'someValue' => 42,
1313  ],
1314  ]);
1315  $request = (new ‪ServerRequest())->withAttribute('frontend.user', $frontendUser);
1316  $pageInformation = new ‪PageInformation();
1317  $pageInformation->setPageRecord([]);
1318  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1319  $this->subject->setRequest($request);
1320  self::assertEquals(42, $this->subject->getData('session:myext|mydata|someValue'));
1321  }
1322 
1326  #[Test]
1327  public function ‪getDataWithTypeLevel(): void
1328  {
1329  $pageInformation = new ‪PageInformation();
1330  $pageInformation->setPageRecord([]);
1331  $pageInformation->setLocalRootLine([
1332  0 => ['uid' => 1, 'title' => 'title1'],
1333  1 => ['uid' => 2, 'title' => 'title2'],
1334  2 => ['uid' => 3, 'title' => 'title3'],
1335  ]);
1336  $request = new ‪ServerRequest('https://example.com');
1337  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1338  $this->subject->setRequest($request);
1339  self::assertEquals(2, $this->subject->getData('level'));
1340  }
1341 
1345  #[Test]
1346  public function ‪getDataWithTypeLeveltitle(): void
1347  {
1348  $pageInformation = new ‪PageInformation();
1349  $pageInformation->setPageRecord([]);
1350  $pageInformation->setLocalRootLine([
1351  0 => ['uid' => 1, 'title' => 'title1'],
1352  1 => ['uid' => 2, 'title' => 'title2'],
1353  2 => ['uid' => 3, 'title' => ''],
1354  ]);
1355  $request = new ‪ServerRequest('https://example.com');
1356  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1357  $this->subject->setRequest($request);
1358  self::assertEquals('', $this->subject->getData('leveltitle:-1'));
1359  // since "title3" is not set, it will slide to "title2"
1360  self::assertEquals('title2', $this->subject->getData('leveltitle:-1,slide'));
1361  }
1362 
1366  #[Test]
1367  public function ‪getDataWithTypeLevelmedia(): void
1368  {
1369  $pageInformation = new ‪PageInformation();
1370  $pageInformation->setPageRecord([]);
1371  $pageInformation->setLocalRootLine([
1372  0 => ['uid' => 1, 'title' => 'title1', 'media' => 'media1'],
1373  1 => ['uid' => 2, 'title' => 'title2', 'media' => 'media2'],
1374  2 => ['uid' => 3, 'title' => 'title3', 'media' => ''],
1375  ]);
1376  $request = new ‪ServerRequest('https://example.com');
1377  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1378  $this->subject->setRequest($request);
1379  self::assertEquals('', $this->subject->getData('levelmedia:-1'));
1380  // since "title3" is not set, it will slide to "title2"
1381  self::assertEquals('media2', $this->subject->getData('levelmedia:-1,slide'));
1382  }
1383 
1387  #[Test]
1388  public function ‪getDataWithTypeLeveluid(): void
1389  {
1390  $pageInformation = new ‪PageInformation();
1391  $pageInformation->setPageRecord([]);
1392  $pageInformation->setLocalRootLine([
1393  0 => ['uid' => 1, 'title' => 'title1'],
1394  1 => ['uid' => 2, 'title' => 'title2'],
1395  2 => ['uid' => 3, 'title' => 'title3'],
1396  ]);
1397  $request = new ‪ServerRequest('https://example.com');
1398  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1399  $this->subject->setRequest($request);
1400  self::assertEquals(3, $this->subject->getData('leveluid:-1'));
1401  // every element will have a uid - so adding slide doesn't really make sense, just for completeness
1402  self::assertEquals(3, $this->subject->getData('leveluid:-1,slide'));
1403  }
1404 
1408  #[Test]
1409  public function ‪getDataWithTypeLevelfield(): void
1410  {
1411  $pageInformation = new ‪PageInformation();
1412  $pageInformation->setPageRecord([]);
1413  $pageInformation->setLocalRootLine([
1414  0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1415  1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1416  2 => ['uid' => 3, 'title' => 'title3', 'testfield' => ''],
1417  ]);
1418  $request = new ‪ServerRequest('https://example.com');
1419  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1420  $this->subject->setRequest($request);
1421  self::assertEquals('', $this->subject->getData('levelfield:-1,testfield'));
1422  self::assertEquals('field2', $this->subject->getData('levelfield:-1,testfield,slide'));
1423  }
1424 
1428  #[Test]
1429  public function ‪getDataWithTypeFullrootline(): void
1430  {
1431  $pageInformation = new ‪PageInformation();
1432  $pageInformation->setPageRecord([]);
1433  $pageInformation->setRootLine([
1434  0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1435  1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1436  2 => ['uid' => 3, 'title' => 'title3', 'testfield' => 'field3'],
1437  ]);
1438  $pageInformation->setLocalRootLine([
1439  0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1440  ]);
1441  $request = new ‪ServerRequest('https://example.com');
1442  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1443  $this->subject->setRequest($request);
1444  self::assertEquals('field2', $this->subject->getData('fullrootline:-1,testfield'));
1445  }
1446 
1450  #[Test]
1451  public function ‪getDataWithTypeDate(): void
1452  {
1453  $pageInformation = new ‪PageInformation();
1454  $pageInformation->setPageRecord([]);
1455  $request = new ‪ServerRequest('https://example.com');
1456  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1457  $this->subject->setRequest($request);
1458  $format = 'Y-M-D';
1459  $defaultFormat = 'd/m Y';
1460  self::assertEquals(date($format, ‪$GLOBALS['EXEC_TIME']), $this->subject->getData('date:' . $format));
1461  self::assertEquals(date($defaultFormat, ‪$GLOBALS['EXEC_TIME']), $this->subject->getData('date'));
1462  }
1463 
1467  #[Test]
1468  public function ‪getDataWithTypePage(): void
1469  {
1470  $pageInformation = new ‪PageInformation();
1471  ‪$uid = random_int(0, mt_getrandmax());
1472  $pageInformation->setPageRecord(['uid' => ‪$uid]);
1473  $request = new ‪ServerRequest('https://example.com');
1474  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1475  $this->subject->setRequest($request);
1476  self::assertEquals(‪$uid, $this->subject->getData('page:uid'));
1477  }
1478 
1482  #[Test]
1483  public function ‪getDataWithTypeCurrent(): void
1484  {
1485  $pageInformation = new ‪PageInformation();
1486  $pageInformation->setPageRecord([]);
1487  $request = new ‪ServerRequest('https://example.com');
1488  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1489  $this->subject->setRequest($request);
1490  $key = ‪StringUtility::getUniqueId('someKey');
1491  $value = ‪StringUtility::getUniqueId('someValue');
1492  $this->subject->data[$key] = $value;
1493  $this->subject->currentValKey = $key;
1494  self::assertEquals($value, $this->subject->getData('current'));
1495  }
1496 
1497  #[Test]
1499  {
1500  $pageInformation = new ‪PageInformation();
1501  $pageInformation->setPageRecord([]);
1502  $request = new ‪ServerRequest('https://example.com');
1503  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1504  $this->subject->setRequest($request);
1505  $dummyRecord = ['uid' => 5, 'title' => 'someTitle'];
1506  $pageRepository = $this->createMock(PageRepository::class);
1507  GeneralUtility::addInstance(PageRepository::class, $pageRepository);
1508  $pageRepository->expects(self::once())->method('getRawRecord')->with('tt_content', '106')->willReturn($dummyRecord);
1509  self::assertSame('someTitle', $this->subject->getData('db:tt_content:106:title'));
1510  }
1511 
1513  {
1514  return [
1515  'identifier with missing table, uid and column' => [
1516  'identifier' => 'db',
1517  ],
1518  'identifier with empty table, missing uid and column' => [
1519  'identifier' => 'db:',
1520  ],
1521  'identifier with missing table and column' => [
1522  'identifier' => 'db:tt_content',
1523  ],
1524  'identifier with empty table and missing uid and column' => [
1525  'identifier' => 'db:tt_content:',
1526  ],
1527  ];
1528  }
1529 
1530  #[DataProvider('getDataWithTypeDbReturnsEmptyStringOnInvalidIdentifiersDataProvider')]
1531  #[Test]
1533  {
1534  $pageInformation = new ‪PageInformation();
1535  $pageInformation->setPageRecord([]);
1536  $request = new ‪ServerRequest('https://example.com');
1537  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1538  $this->subject->setRequest($request);
1539  self::assertSame('', $this->subject->getData(‪$identifier));
1540  }
1541 
1543  {
1544  return [
1545  'identifier with empty uid and missing column' => [
1546  'identifier' => 'db:tt_content:106',
1547  ],
1548  'identifier with empty uid and column' => [
1549  'identifier' => 'db:tt_content:106:',
1550  ],
1551  'identifier with empty uid and not existing column' => [
1552  'identifier' => 'db:tt_content:106:not_existing_column',
1553  ],
1554  ];
1555  }
1556 
1557  #[DataProvider('getDataWithTypeDbReturnsEmptyStringOnInvalidIdentifiersCallsPageRepositoryOnceDataProvider')]
1558  #[Test]
1560  {
1561  $pageInformation = new ‪PageInformation();
1562  $pageInformation->setPageRecord([]);
1563  $request = new ‪ServerRequest('https://example.com');
1564  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1565  $this->subject->setRequest($request);
1566  $dummyRecord = ['uid' => 5, 'title' => 'someTitle'];
1567  $pageRepository = $this->createMock(PageRepository::class);
1568  GeneralUtility::addInstance(PageRepository::class, $pageRepository);
1569  $pageRepository->expects(self::once())->method('getRawRecord')->with('tt_content', '106')->willReturn($dummyRecord);
1570  self::assertSame('', $this->subject->getData(‪$identifier));
1571  }
1572 
1573  #[Test]
1574  public function ‪getDataWithTypeLll(): void
1575  {
1576  $pageInformation = new ‪PageInformation();
1577  $pageInformation->setPageRecord([]);
1578  $request = new ‪ServerRequest('https://example.com');
1579  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1580  $language = $this->createMock(SiteLanguage::class);
1581  $request = $request->withAttribute('language', $language);
1582  $this->subject->setRequest($request);
1583  $key = ‪StringUtility::getUniqueId('someKey');
1584  $value = ‪StringUtility::getUniqueId('someValue');
1585  $languageServiceFactory = $this->createMock(LanguageServiceFactory::class);
1586  $languageServiceMock = $this->createMock(LanguageService::class);
1587  $languageServiceFactory->expects(self::once())->method('createFromSiteLanguage')->with(self::anything())->willReturn($languageServiceMock);
1588  GeneralUtility::addInstance(LanguageServiceFactory::class, $languageServiceFactory);
1589  $languageServiceMock->expects(self::once())->method('sL')->with('LLL:' . $key)->willReturn($value);
1590  self::assertEquals($value, $this->subject->getData('lll:' . $key));
1591  }
1592 
1596  #[Test]
1597  public function ‪getDataWithTypePath(): void
1598  {
1599  $pageInformation = new ‪PageInformation();
1600  $pageInformation->setPageRecord([]);
1601  $request = new ‪ServerRequest('https://example.com');
1602  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1603  $this->subject->setRequest($request);
1604  $filenameIn = 'typo3/sysext/frontend/Public/Icons/Extension.svg';
1605  self::assertEquals($filenameIn, $this->subject->getData('path:' . $filenameIn));
1606  }
1607 
1611  #[Test]
1612  public function ‪getDataWithTypeContext(): void
1613  {
1614  $pageInformation = new ‪PageInformation();
1615  $pageInformation->setPageRecord([]);
1616  $request = new ‪ServerRequest('https://example.com');
1617  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1618  $this->subject->setRequest($request);
1619  $context = new ‪Context();
1620  $context->setAspect('workspace', new ‪WorkspaceAspect(3));
1621  $context->setAspect('frontend.user', new ‪UserAspect(new ‪FrontendUserAuthentication(), [0, -1]));
1622  GeneralUtility::setSingletonInstance(Context::class, $context);
1623  self::assertEquals(3, $this->subject->getData('context:workspace:id'));
1624  self::assertEquals('0,-1', $this->subject->getData('context:frontend.user:groupIds'));
1625  self::assertFalse($this->subject->getData('context:frontend.user:isLoggedIn'));
1626  self::assertSame('', $this->subject->getData('context:frontend.user:foozball'));
1627  }
1628 
1632  #[Test]
1633  public function ‪getDataWithTypeSite(): void
1634  {
1635  $pageInformation = new ‪PageInformation();
1636  $pageInformation->setPageRecord([]);
1637  $site = new ‪Site('my-site', 123, [
1638  'base' => 'http://example.com',
1639  'custom' => [
1640  'config' => [
1641  'nested' => 'yeah',
1642  ],
1643  ],
1644  ]);
1645  $request = (new ‪ServerRequest('https://example.com'))
1646  ->withAttribute('frontend.page.information', $pageInformation)
1647  ->withAttribute('site', $site);
1648  $this->subject->setRequest($request);
1649  self::assertEquals('http://example.com', $this->subject->getData('site:base'));
1650  self::assertEquals('yeah', $this->subject->getData('site:custom.config.nested'));
1651  }
1652 
1656  #[Test]
1658  {
1659  $pageInformation = new ‪PageInformation();
1660  $pageInformation->setPageRecord([]);
1661 
1662  $packageManager = new PackageManager(new ‪DependencyOrderingService());
1663  GeneralUtility::addInstance(ProviderConfigurationLoader::class, new ‪ProviderConfigurationLoader(
1664  $packageManager,
1665  new ‪NullFrontend('core'),
1666  'ExpressionLanguageProviders'
1667  ));
1668  GeneralUtility::addInstance(DefaultProvider::class, new ‪DefaultProvider(new ‪Typo3Version(), new ‪Context(), new ‪Features()));
1669 
1670  putenv('LOCAL_DEVELOPMENT=1');
1671 
1672  $site = new ‪Site('my-site', 123, [
1673  'base' => 'http://prod.com',
1674  'baseVariants' => [
1675  [
1676  'base' => 'http://staging.com',
1677  'condition' => 'applicationContext == "Production/Staging"',
1678  ],
1679  [
1680  'base' => 'http://dev.com',
1681  'condition' => 'getenv("LOCAL_DEVELOPMENT") == 1',
1682  ],
1683  ],
1684  ]);
1685 
1686  $request = (new ‪ServerRequest('https://example.com'))
1687  ->withAttribute('frontend.page.information', $pageInformation)
1688  ->withAttribute('site', $site);
1689  $this->subject->setRequest($request);
1690 
1691  self::assertEquals('http://dev.com', $this->subject->getData('site:base'));
1692  }
1693 
1697  #[Test]
1698  public function ‪getDataWithTypeSiteLanguage(): void
1699  {
1700  $pageInformation = new ‪PageInformation();
1701  $pageInformation->setPageRecord([]);
1702  $request = new ‪ServerRequest('https://example.com');
1703  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1704  $site = $this->‪createSiteWithLanguage([
1705  'base' => '/',
1706  'languageId' => 1,
1707  'locale' => 'de_DE',
1708  'title' => 'languageTitle',
1709  'navigationTitle' => 'German',
1710  ]);
1711  $language = $site->getLanguageById(1);
1712  $request = $request->withAttribute('language', $language);
1713  $this->subject->setRequest($request);
1714  self::assertEquals('German', $this->subject->getData('siteLanguage:navigationTitle'));
1715  self::assertEquals('de', $this->subject->getData('siteLanguage:twoLetterIsoCode'));
1716  self::assertEquals('de', $this->subject->getData('siteLanguage:locale:languageCode'));
1717  self::assertEquals('de-DE', $this->subject->getData('siteLanguage:hreflang'));
1718  self::assertEquals('de-DE', $this->subject->getData('siteLanguage:locale:full'));
1719  }
1720 
1724  #[Test]
1726  {
1727  $pageInformation = new ‪PageInformation();
1728  $pageInformation->setPageRecord([]);
1729  $request = new ‪ServerRequest('https://example.com');
1730  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1731  $this->subject->setRequest($request);
1732 
1733  $recordNumber = random_int(0, mt_getrandmax());
1734  $this->subject->parentRecordNumber = $recordNumber;
1735  self::assertEquals($recordNumber, $this->subject->getData('cobj:parentRecordNumber'));
1736  }
1737 
1741  #[Test]
1742  public function ‪getDataWithTypeDebugRootline(): void
1743  {
1744  $pageInformation = new ‪PageInformation();
1745  $pageInformation->setPageRecord([]);
1746  $pageInformation->setLocalRootLine([
1747  0 => ['uid' => 1, 'title' => 'title1'],
1748  1 => ['uid' => 2, 'title' => 'title2'],
1749  2 => ['uid' => 3, 'title' => ''],
1750  ]);
1751  $request = new ‪ServerRequest('https://example.com');
1752  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1753  $this->subject->setRequest($request);
1754  $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)';
1756  $result = $this->subject->getData('debug:rootLine');
1757  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1758 
1759  self::assertEquals($expectedResult, $cleanedResult);
1760  }
1761 
1765  #[Test]
1766  public function ‪getDataWithTypeDebugFullRootline(): void
1767  {
1768  $pageInformation = new ‪PageInformation();
1769  $pageInformation->setPageRecord([]);
1770  $rootline = [
1771  0 => ['uid' => 1, 'title' => 'title1'],
1772  1 => ['uid' => 2, 'title' => 'title2'],
1773  2 => ['uid' => 3, 'title' => ''],
1774  ];
1775  $pageInformation->setRootLine($rootline);
1776  $request = new ‪ServerRequest('https://example.com');
1777  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1778  $this->subject->setRequest($request);
1779 
1780  $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)';
1781 
1783  $result = $this->subject->getData('debug:fullRootLine');
1784  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1785 
1786  self::assertEquals($expectedResult, $cleanedResult);
1787  }
1788 
1792  #[Test]
1793  public function ‪getDataWithTypeDebugData(): void
1794  {
1795  $pageInformation = new ‪PageInformation();
1796  $pageInformation->setPageRecord([]);
1797  $request = new ‪ServerRequest('https://example.com');
1798  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1799  $this->subject->setRequest($request);
1800 
1801  $key = ‪StringUtility::getUniqueId('someKey');
1802  $value = ‪StringUtility::getUniqueId('someValue');
1803  $this->subject->data = [$key => $value];
1804 
1805  $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1806 
1808  $result = $this->subject->getData('debug:data');
1809  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1810 
1811  self::assertEquals($expectedResult, $cleanedResult);
1812  }
1813 
1817  #[Test]
1818  public function ‪getDataWithTypeDebugRegister(): void
1819  {
1820  $pageInformation = new ‪PageInformation();
1821  $pageInformation->setPageRecord([]);
1822  $request = new ‪ServerRequest('https://example.com');
1823  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1824  $this->subject->setRequest($request);
1825 
1826  $key = ‪StringUtility::getUniqueId('someKey');
1827  $value = ‪StringUtility::getUniqueId('someValue');
1829  ‪$GLOBALS['TSFE']->register = [$key => $value];
1830 
1831  $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1832 
1834  $result = $this->subject->getData('debug:register');
1835  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1836 
1837  self::assertEquals($expectedResult, $cleanedResult);
1838  }
1839 
1843  #[Test]
1844  public function ‪getDataWithTypeDebugPage(): void
1845  {
1846  $pageInformation = new ‪PageInformation();
1847  ‪$uid = random_int(0, mt_getrandmax());
1848  $pageInformation->setPageRecord(['uid' => ‪$uid]);
1849  $request = new ‪ServerRequest('https://example.com');
1850  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1851  $this->subject->setRequest($request);
1852  $expectedResult = 'array(1item)uid=>' . ‪$uid . '(integer)';
1854  $result = $this->subject->getData('debug:page');
1855  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1856  self::assertEquals($expectedResult, $cleanedResult);
1857  }
1858 
1862  #[Test]
1863  public function ‪getDataWithApplicationContext(): void
1864  {
1866  new ‪ApplicationContext('Production'),
1867  true,
1868  false,
1873  ‪Environment::getPublicPath() . '/index.php',
1874  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1875  );
1876  $pageInformation = new ‪PageInformation();
1877  $pageInformation->setPageRecord([]);
1878  $request = new ‪ServerRequest('https://example.com');
1879  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1880  $this->subject->setRequest($request);
1881  self::assertSame('Production', $this->subject->getData('applicationContext'));
1882  }
1883 
1884  #[Test]
1886  {
1887  $this->expectException(\LogicException::class);
1888  $this->expectExceptionCode(1414513947);
1889  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1890  $typoScript->setConfigArray([]);
1891  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1892  $this->subject->setRequest($request);
1893  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject, false);
1894  $this->subject->render($contentObjectFixture);
1895  }
1896 
1897  #[Test]
1899  {
1901  new ‪ApplicationContext('Production'),
1902  true,
1903  false,
1908  ‪Environment::getPublicPath() . '/index.php',
1909  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1910  );
1911  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1912  $typoScript->setConfigArray([]);
1913  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1914  $this->subject->setRequest($request);
1915  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1916  $this->subject->render($contentObjectFixture);
1917  }
1918 
1919  #[Test]
1921  {
1922  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1923  $configuration = [
1924  'exceptionHandler' => '1',
1925  ];
1926  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1927  $typoScript->setConfigArray([]);
1928  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1929  $this->subject->setRequest($request);
1930  $this->subject->render($contentObjectFixture, $configuration);
1931  }
1932 
1933  #[Test]
1935  {
1936  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1937  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1938  $typoScript->setConfigArray([
1939  'contentObjectExceptionHandler' => '1',
1940  ]);
1941  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1942  $this->subject->setRequest($request);
1943  $this->subject->render($contentObjectFixture);
1944  }
1945 
1946  #[Test]
1948  {
1949  $this->expectException(\LogicException::class);
1950  $this->expectExceptionCode(1414513947);
1951  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject, false);
1952  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1953  $typoScript->setConfigArray([
1954  'contentObjectExceptionHandler' => '1',
1955  ]);
1956  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1957  $this->subject->setRequest($request);
1958  $configuration = [
1959  'exceptionHandler' => '0',
1960  ];
1961  $this->subject->render($contentObjectFixture, $configuration);
1962  }
1963 
1964  #[Test]
1966  {
1967  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1968  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1969  $typoScript->setConfigArray([]);
1970  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1971  $this->subject->setRequest($request);
1972  $configuration = [
1973  'exceptionHandler' => '1',
1974  'exceptionHandler.' => [
1975  'errorMessage' => 'New message for testing',
1976  ],
1977  ];
1978  self::assertSame('New message for testing', $this->subject->render($contentObjectFixture, $configuration));
1979  }
1980 
1981  #[Test]
1983  {
1984  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1985  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
1986  $typoScript->setConfigArray([
1987  'contentObjectExceptionHandler.' => [
1988  'errorMessage' => 'Global message for testing',
1989  ],
1990  ]);
1991  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1992  $this->subject->setRequest($request);
1993  $configuration = [
1994  'exceptionHandler' => '1',
1995  'exceptionHandler.' => [
1996  'errorMessage' => 'New message for testing',
1997  ],
1998  ];
1999 
2000  self::assertSame('New message for testing', $this->subject->render($contentObjectFixture, $configuration));
2001  }
2002 
2003  #[Test]
2005  {
2006  $this->expectException(\LogicException::class);
2007  $this->expectExceptionCode(1414513947);
2008  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
2009  $configuration = [
2010  'exceptionHandler' => '1',
2011  'exceptionHandler.' => [
2012  'ignoreCodes.' => ['10.' => '1414513947'],
2013  ],
2014  ];
2015  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
2016  $typoScript->setConfigArray([]);
2017  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
2018  $this->subject->setRequest($request);
2019  $this->subject->render($contentObjectFixture, $configuration);
2020  }
2021 
2022  private function ‪createContentObjectThrowingExceptionFixture(‪ContentObjectRenderer ‪$subject, bool $addProductionExceptionHandlerInstance = true): ‪AbstractContentObject&MockObject
2023  {
2024  $contentObjectFixture = $this->getMockBuilder(AbstractContentObject::class)->getMock();
2025  $contentObjectFixture->expects(self::once())
2026  ->method('render')
2027  ->willReturnCallback(static function (array $conf = []): string {
2028  throw new \LogicException('Exception during rendering', 1414513947);
2029  });
2030  $contentObjectFixture->‪setContentObjectRenderer(‪$subject);
2031  if ($addProductionExceptionHandlerInstance) {
2032  GeneralUtility::addInstance(
2033  ProductionExceptionHandler::class,
2034  new ‪ProductionExceptionHandler(new ‪Context(), new ‪Random(), new NullLogger(), new ‪RequestId())
2035  );
2036  }
2037  return $contentObjectFixture;
2038  }
2039 
2040  public static function ‪_parseFuncReturnsCorrectHtmlDataProvider(): array
2041  {
2042  return [
2043  'Text without tag is wrapped with <p> tag' => [
2044  'Text without tag',
2046  '<p class="bodytext">Text without tag</p>',
2047  false,
2048  ],
2049  'Text wrapped with <p> tag remains the same' => [
2050  '<p class="myclass">Text with &lt;p&gt; tag</p>',
2052  '<p class="myclass">Text with &lt;p&gt; tag</p>',
2053  false,
2054  ],
2055  'Text with absolute external link' => [
2056  'Text with <link http://example.com/foo/>external link</link>',
2058  '<p class="bodytext">Text with <a href="http://example.com/foo/">external link</a></p>',
2059  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/foo/'))->withLinkText('external link'),
2060  ],
2061  'Empty lines are not duplicated' => [
2062  LF,
2064  '<p class="bodytext">&nbsp;</p>',
2065  false,
2066  ],
2067  'Multiple empty lines with no text' => [
2068  LF . LF . LF,
2070  '<p class="bodytext">&nbsp;</p>' . LF . '<p class="bodytext">&nbsp;</p>' . LF . '<p class="bodytext">&nbsp;</p>',
2071  false,
2072  ],
2073  'Empty lines are not duplicated at the end of content' => [
2074  'test' . LF . LF,
2076  '<p class="bodytext">test</p>' . LF . '<p class="bodytext">&nbsp;</p>',
2077  false,
2078  ],
2079  'Empty lines are not trimmed' => [
2080  LF . 'test' . LF,
2082  '<p class="bodytext">&nbsp;</p>' . LF . '<p class="bodytext">test</p>' . LF . '<p class="bodytext">&nbsp;</p>',
2083  false,
2084  ],
2085  // @todo documenting the current behavior of allowTags/denyTags=*
2086  // @todo probably denyTags should take precedence, which might be breaking
2087  'All tags are allowed, using allowTags=* and denyTags=*' => [
2088  '<p><em>Example</em> <u>underlined</u> text</p>',
2089  [
2090  'parseFunc' => '1',
2091  'parseFunc.' => [
2092  'allowTags' => '*',
2093  'denyTags' => '*',
2094  ],
2095  ],
2096  '<p><em>Example</em> <u>underlined</u> text</p>',
2097  false,
2098  ],
2099  'Only u tags are allowed, so all others are escaped' => [
2100  '<p><em>Example</em> <u>underlined</u> text</p>',
2101  [
2102  'parseFunc' => '1',
2103  'parseFunc.' => [
2104  'allowTags' => 'u',
2105  'denyTags' => '*',
2106  ],
2107  ],
2108  '&lt;p&gt;&lt;em&gt;Example&lt;/em&gt; <u>underlined</u> text&lt;/p&gt;',
2109  false,
2110  ],
2111  'No tags are allowed, so all are escaped' => [
2112  '<p><em>Example</em> <u>underlined</u> text</p>',
2113  [
2114  'parseFunc' => '1',
2115  'parseFunc.' => [
2116  'denyTags' => '*',
2117  ],
2118  ],
2119  '&lt;p&gt;&lt;em&gt;Example&lt;/em&gt; &lt;u&gt;underlined&lt;/u&gt; text&lt;/p&gt;',
2120  false,
2121  ],
2122  'No tags are denied, so all are escaped except the ones defined' => [
2123  '<p><em>Example</em> <u>underlined</u> text</p>',
2124  [
2125  'parseFunc' => '1',
2126  'parseFunc.' => [
2127  'allowTags' => 'u',
2128  ],
2129  ],
2130  '&lt;p&gt;&lt;em&gt;Example&lt;/em&gt; <u>underlined</u> text&lt;/p&gt;',
2131  false,
2132  ],
2133  'No tags are allowed, but some are explicitly denied' => [
2134  '<p><em>Example</em> <u>underlined</u> text</p>',
2135  [
2136  'parseFunc' => '1',
2137  'parseFunc.' => [
2138  'denyTags' => 'em',
2139  ],
2140  ],
2141  '<p>&lt;em&gt;Example&lt;/em&gt; <u>underlined</u> text</p>',
2142  false,
2143  ],
2144  'No tags are allowed or denied - allow everything, do not escape anything' => [
2145  '<p><em>Example</em> <u>underlined</u> text</p>',
2146  [
2147  'parseFunc' => '1',
2148  'parseFunc.' => [
2149  'somethingElse' => '',
2150  ],
2151  ],
2152  '<p><em>Example</em> <u>underlined</u> text</p>',
2153  false,
2154  ],
2155  'All tags are allowed' => [
2156  '<p><em>Example</em> <u>underlined</u> text</p>',
2157  [
2158  'parseFunc' => '1',
2159  'parseFunc.' => [
2160  'allowTags' => '*',
2161  ],
2162  ],
2163  '<p><em>Example</em> <u>underlined</u> text</p>',
2164  false,
2165  ],
2166  'All tags are allowed except a list of unwanted tags' => [
2167  '<p><em>Example</em> <u>underlined</u> text</p>',
2168  [
2169  'parseFunc' => '1',
2170  'parseFunc.' => [
2171  'allowTags' => '*',
2172  'denyTags' => 'em',
2173  ],
2174  ],
2175  '<p>&lt;em&gt;Example&lt;/em&gt; <u>underlined</u> text</p>',
2176  false,
2177  ],
2178  ];
2179  }
2180 
2181  #[DataProvider('_parseFuncReturnsCorrectHtmlDataProvider')]
2182  #[Test]
2183  public function ‪stdWrap_parseFuncReturnsParsedHtml(string $value, array $configuration, string $expectedResult, ‪LinkResultInterface|false $linkResult): void
2184  {
2185  if ($linkResult !== false) {
2186  $linkFactory = $this->getMockBuilder(LinkFactory::class)->disableOriginalConstructor()->getMock();
2187  $linkFactory->method('create')->willReturn($linkResult);
2188  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
2189  }
2190  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
2191  $typoScript->setConfigArray([]);
2192  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
2193  $this->subject->setRequest($request);
2194  self::assertEquals($expectedResult, $this->subject->stdWrap_parseFunc($value, $configuration));
2195  }
2196 
2204  {
2205  $defaultListItemParseFunc = [
2206  'parseFunc' => '',
2207  'parseFunc.' => [
2208  'tags.' => [
2209  'li' => 'TEXT',
2210  'li.' => [
2211  'wrap' => '<li>LI:|</li>',
2212  'current' => '1',
2213  ],
2214  ],
2215  ],
2216  ];
2217 
2218  return [
2219  'parent & child tags with same beginning are processed' => [
2220  '<div><any data-skip><anyother data-skip>content</anyother></any></div>',
2221  [
2222  'parseFunc' => '',
2223  'parseFunc.' => [
2224  'tags.' => [
2225  'any' => 'TEXT',
2226  'any.' => [
2227  'wrap' => '<any data-processed>|</any>',
2228  'current' => 1,
2229  ],
2230  'anyother' => 'TEXT',
2231  'anyother.' => [
2232  'wrap' => '<anyother data-processed>|</anyother>',
2233  'current' => 1,
2234  ],
2235  ],
2236  'htmlSanitize' => true,
2237  'htmlSanitize.' => [
2238  'build' => TestSanitizerBuilder::class,
2239  ],
2240  ],
2241  ],
2242  '<div><any data-processed><anyother data-processed>content</anyother></any></div>',
2243  ],
2244  'list with empty and filled li' => [
2245  '<ul>
2246  <li></li>
2247  <li>second</li>
2248 </ul>',
2249  $defaultListItemParseFunc,
2250  '<ul>
2251  <li>LI:</li>
2252  <li>LI:second</li>
2253 </ul>',
2254  ],
2255  'list with filled li wrapped by a div containing text' => [
2256  '<div>text<ul><li></li><li>second</li></ul></div>',
2257  $defaultListItemParseFunc,
2258  '<div>text<ul><li>LI:</li><li>LI:second</li></ul></div>',
2259  ],
2260  'link list with empty li modification' => [
2261  '<ul>
2262  <li>
2263  <ul>
2264  <li></li>
2265  </ul>
2266  </li>
2267 </ul>',
2268  $defaultListItemParseFunc,
2269  '<ul>
2270  <li>LI:
2271  <ul>
2272  <li>LI:</li>
2273  </ul>
2274  </li>
2275 </ul>',
2276  ],
2277 
2278  'link list with li modifications' => [
2279  '<ul>
2280  <li>first</li>
2281  <li>second
2282  <ul>
2283  <li>first sub</li>
2284  <li>second sub</li>
2285  </ul>
2286  </li>
2287 </ul>',
2288  $defaultListItemParseFunc,
2289  '<ul>
2290  <li>LI:first</li>
2291  <li>LI:second
2292  <ul>
2293  <li>LI:first sub</li>
2294  <li>LI:second sub</li>
2295  </ul>
2296  </li>
2297 </ul>',
2298  ],
2299  'link list with li modifications and no text' => [
2300  '<ul>
2301  <li>first</li>
2302  <li>
2303  <ul>
2304  <li>first sub</li>
2305  <li>second sub</li>
2306  </ul>
2307  </li>
2308 </ul>',
2309  $defaultListItemParseFunc,
2310  '<ul>
2311  <li>LI:first</li>
2312  <li>LI:
2313  <ul>
2314  <li>LI:first sub</li>
2315  <li>LI:second sub</li>
2316  </ul>
2317  </li>
2318 </ul>',
2319  ],
2320  'link list with li modifications on third level' => [
2321  '<ul>
2322  <li>first</li>
2323  <li>second
2324  <ul>
2325  <li>first sub
2326  <ul>
2327  <li>first sub sub</li>
2328  <li>second sub sub</li>
2329  </ul>
2330  </li>
2331  <li>second sub</li>
2332  </ul>
2333  </li>
2334 </ul>',
2335  $defaultListItemParseFunc,
2336  '<ul>
2337  <li>LI:first</li>
2338  <li>LI:second
2339  <ul>
2340  <li>LI:first sub
2341  <ul>
2342  <li>LI:first sub sub</li>
2343  <li>LI:second sub sub</li>
2344  </ul>
2345  </li>
2346  <li>LI:second sub</li>
2347  </ul>
2348  </li>
2349 </ul>',
2350  ],
2351  'link list with li modifications on third level no text' => [
2352  '<ul>
2353  <li>first</li>
2354  <li>
2355  <ul>
2356  <li>
2357  <ul>
2358  <li>first sub sub</li>
2359  <li>first sub sub</li>
2360  </ul>
2361  </li>
2362  <li>second sub</li>
2363  </ul>
2364  </li>
2365 </ul>',
2366  $defaultListItemParseFunc,
2367  '<ul>
2368  <li>LI:first</li>
2369  <li>LI:
2370  <ul>
2371  <li>LI:
2372  <ul>
2373  <li>LI:first sub sub</li>
2374  <li>LI:first sub sub</li>
2375  </ul>
2376  </li>
2377  <li>LI:second sub</li>
2378  </ul>
2379  </li>
2380 </ul>',
2381  ],
2382  'link list with ul and li modifications' => [
2383  '<ul>
2384  <li>first</li>
2385  <li>second
2386  <ul>
2387  <li>first sub</li>
2388  <li>second sub</li>
2389  </ul>
2390  </li>
2391 </ul>',
2392  [
2393  'parseFunc' => '',
2394  'parseFunc.' => [
2395  'tags.' => [
2396  'ul' => 'TEXT',
2397  'ul.' => [
2398  'wrap' => '<ul><li>intro</li>|<li>outro</li></ul>',
2399  'current' => '1',
2400  ],
2401  'li' => 'TEXT',
2402  'li.' => [
2403  'wrap' => '<li>LI:|</li>',
2404  'current' => '1',
2405  ],
2406  ],
2407  ],
2408  ],
2409  '<ul><li>intro</li>
2410  <li>LI:first</li>
2411  <li>LI:second
2412  <ul><li>intro</li>
2413  <li>LI:first sub</li>
2414  <li>LI:second sub</li>
2415  <li>outro</li></ul>
2416  </li>
2417 <li>outro</li></ul>',
2418  ],
2419 
2420  'link list with li containing p tag and sub list' => [
2421  '<ul>
2422  <li>first</li>
2423  <li>
2424  <ul>
2425  <li>
2426  <span>
2427  <ul>
2428  <li>first sub sub</li>
2429  <li>first sub sub</li>
2430  </ul>
2431  </span>
2432  </li>
2433  <li>second sub</li>
2434  </ul>
2435  </li>
2436 </ul>',
2437  $defaultListItemParseFunc,
2438  '<ul>
2439  <li>LI:first</li>
2440  <li>LI:
2441  <ul>
2442  <li>LI:
2443  <span>
2444  <ul>
2445  <li>LI:first sub sub</li>
2446  <li>LI:first sub sub</li>
2447  </ul>
2448  </span>
2449  </li>
2450  <li>LI:second sub</li>
2451  </ul>
2452  </li>
2453 </ul>',
2454  ],
2455  ];
2456  }
2457 
2458  #[DataProvider('_parseFuncParsesNestedTagsProperlyDataProvider')]
2459  #[Test]
2460  public function ‪parseFuncParsesNestedTagsProperly(string $value, array $configuration, string $expectedResult): void
2461  {
2462  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
2463  $typoScript->setConfigArray([]);
2464  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
2465  $this->subject->setRequest($request);
2466  self::assertEquals($expectedResult, $this->subject->stdWrap_parseFunc($value, $configuration));
2467  }
2468 
2469  public static function ‪httpMakelinksDataProvider(): array
2470  {
2471  return [
2472  'http link' => [
2473  'Some text with a link http://example.com',
2474  [
2475  ],
2476  'Some text with a link <a href="http://example.com">example.com</a>',
2477  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com'))->withLinkText('example.com'),
2478  ],
2479  'http link with path' => [
2480  'Some text with a link http://example.com/path/to/page',
2481  [
2482  ],
2483  'Some text with a link <a href="http://example.com/path/to/page">example.com</a>',
2484  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page'))->withLinkText('example.com'),
2485  ],
2486  'http link with query parameter' => [
2487  'Some text with a link http://example.com?foo=bar',
2488  [
2489  ],
2490  'Some text with a link <a href="http://example.com?foo=bar">example.com</a>',
2491  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com?foo=bar'))->withLinkText('example.com'),
2492  ],
2493  'http link with question mark' => [
2494  'Some text with a link http://example.com?',
2495  [
2496  ],
2497  'Some text with a link <a href="http://example.com">example.com</a>?',
2498  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com'))->withLinkText('example.com'),
2499  ],
2500  'http link with period' => [
2501  'Some text with a link http://example.com.',
2502  [
2503  ],
2504  'Some text with a link <a href="http://example.com">example.com</a>.',
2505  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com'))->withLinkText('example.com'),
2506  ],
2507  'http link with fragment' => [
2508  'Some text with a link http://example.com#',
2509  [
2510  ],
2511  'Some text with a link <a href="http://example.com#">example.com</a>',
2512  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com#'))->withLinkText('example.com'),
2513  ],
2514  'http link with query parameter and fragment' => [
2515  'Some text with a link http://example.com?foo=bar#top',
2516  [
2517  ],
2518  'Some text with a link <a href="http://example.com?foo=bar#top">example.com</a>',
2519  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com?foo=bar#top'))->withLinkText('example.com'),
2520  ],
2521  'http link with query parameter and keep scheme' => [
2522  'Some text with a link http://example.com/path/to/page?foo=bar',
2523  [
2524  'keep' => 'scheme',
2525  ],
2526  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">http://example.com</a>',
2527  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('http://example.com'),
2528  ],
2529  'http link with query parameter and keep path' => [
2530  'Some text with a link http://example.com/path/to/page?foo=bar',
2531  [
2532  'keep' => 'path',
2533  ],
2534  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">example.com/path/to/page</a>',
2535  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('example.com/path/to/page'),
2536  ],
2537  'http link with query parameter and keep path with trailing slash' => [
2538  'Some text with a link http://example.com/path/to/page/?foo=bar',
2539  [
2540  'keep' => 'path',
2541  ],
2542  'Some text with a link <a href="http://example.com/path/to/page/?foo=bar">example.com/path/to/page/</a>',
2543  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page/?foo=bar'))->withLinkText('example.com/path/to/page/'),
2544  ],
2545  'http link with trailing slash and keep path with trailing slash' => [
2546  'Some text with a link http://example.com/',
2547  [
2548  'keep' => 'path',
2549  ],
2550  'Some text with a link <a href="http://example.com/">example.com</a>',
2551  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/'))->withLinkText('example.com'),
2552  ],
2553  'http link with query parameter and keep scheme,path' => [
2554  'Some text with a link http://example.com/path/to/page?foo=bar',
2555  [
2556  'keep' => 'scheme,path',
2557  ],
2558  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">http://example.com/path/to/page</a>',
2559  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('http://example.com/path/to/page'),
2560  ],
2561  'http link with multiple query parameters' => [
2562  'Some text with a link http://example.com?foo=bar&fuz=baz',
2563  [
2564  'keep' => 'scheme,path,query',
2565  ],
2566  'Some text with a link <a href="http://example.com?foo=bar&amp;fuz=baz">http://example.com?foo=bar&fuz=baz</a>',
2567  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com?foo=bar&fuz=baz'))->withLinkText('http://example.com?foo=bar&fuz=baz'),
2568  ],
2569  'http link with query parameter and keep scheme,path,query' => [
2570  'Some text with a link http://example.com/path/to/page?foo=bar',
2571  [
2572  'keep' => 'scheme,path,query',
2573  ],
2574  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">http://example.com/path/to/page?foo=bar</a>',
2575  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('http://example.com/path/to/page?foo=bar'),
2576  ],
2577  'https link' => [
2578  'Some text with a link https://example.com',
2579  [
2580  ],
2581  'Some text with a link <a href="https://example.com">example.com</a>',
2582  (new LinkResult(‪LinkService::TYPE_URL, 'https://example.com'))->withLinkText('example.com'),
2583  ],
2584  'http link with www' => [
2585  'Some text with a link http://www.example.com',
2586  [
2587  ],
2588  'Some text with a link <a href="http://www.example.com">www.example.com</a>',
2589  (new LinkResult(‪LinkService::TYPE_URL, 'http://www.example.com'))->withLinkText('www.example.com'),
2590  ],
2591  'https link with www' => [
2592  'Some text with a link https://www.example.com',
2593  [
2594  ],
2595  'Some text with a link <a href="https://www.example.com">www.example.com</a>',
2596  (new LinkResult(‪LinkService::TYPE_URL, 'https://www.example.com'))->withLinkText('www.example.com'),
2597  ],
2598  ];
2599  }
2600 
2601  #[DataProvider('httpMakelinksDataProvider')]
2602  #[Test]
2603  public function ‪httpMakelinksReturnsLink(string $data, array $configuration, string $expectedResult, LinkResult $linkResult): void
2604  {
2605  $linkFactory = $this->getMockBuilder(LinkFactory::class)->disableOriginalConstructor()->getMock();
2606  $linkFactory->method('create')->willReturn($linkResult);
2607  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
2608 
2609  self::assertSame($expectedResult, $this->subject->_call('http_makelinks', $data, $configuration));
2610  }
2611 
2612  public static function ‪invalidHttpMakelinksDataProvider(): array
2613  {
2614  return [
2615  'only http protocol' => [
2616  'http://',
2617  [
2618  ],
2619  'http://',
2620  ],
2621  'only https protocol' => [
2622  'https://',
2623  [
2624  ],
2625  'https://',
2626  ],
2627  'ftp link' => [
2628  'ftp://user@password:example.com',
2629  [
2630  ],
2631  'ftp://user@password:example.com',
2632  ],
2633  ];
2634  }
2635 
2636  #[DataProvider('invalidHttpMakelinksDataProvider')]
2637  #[Test]
2638  public function ‪httpMakelinksReturnsNoLink(string $data, array $configuration, string $expectedResult): void
2639  {
2640  self::assertSame($expectedResult, $this->subject->_call('http_makelinks', $data, $configuration));
2641  }
2642 
2643  public static function ‪mailtoMakelinksDataProvider(): array
2644  {
2645  return [
2646  'mailto link' => [
2647  'Some text with an email address mailto:john@example.com',
2648  [
2649  ],
2650  'Some text with an email address <a href="mailto:john@example.com">john@example.com</a>',
2651  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2652  ],
2653  'mailto link with subject parameter' => [
2654  'Some text with an email address mailto:john@example.com?subject=hi',
2655  [
2656  ],
2657  'Some text with an email address <a href="mailto:john@example.com?subject=hi">john@example.com</a>',
2658  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com?subject=hi'))->withLinkText('john@example.com'),
2659  ],
2660  'mailto link with multiple parameters' => [
2661  'Some text with an email address mailto:john@example.com?subject=Greetings&body=Hi+John',
2662  [
2663  ],
2664  'Some text with an email address <a href="mailto:john@example.com?subject=Greetings&amp;body=Hi+John">john@example.com</a>',
2665  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com?subject=Greetings&body=Hi+John'))->withLinkText('john@example.com'),
2666  ],
2667  'mailto link with question mark' => [
2668  'Some text with an email address mailto:john@example.com?',
2669  [
2670  ],
2671  'Some text with an email address <a href="mailto:john@example.com">john@example.com</a>?',
2672  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2673  ],
2674  'mailto link with period' => [
2675  'Some text with an email address mailto:john@example.com.',
2676  [
2677  ],
2678  'Some text with an email address <a href="mailto:john@example.com">john@example.com</a>.',
2679  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2680  ],
2681  'mailto link with wrap' => [
2682  'Some text with an email address mailto:john@example.com.',
2683  [
2684  'wrap' => '<span>|</span>',
2685  ],
2686  'Some text with an email address <span><a href="mailto:john@example.com">john@example.com</a></span>.',
2687  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2688  ],
2689  'mailto link with ATagBeforeWrap' => [
2690  'Some text with an email address mailto:john@example.com.',
2691  [
2692  'wrap' => '<span>|</span>',
2693  'ATagBeforeWrap' => 1,
2694  ],
2695  'Some text with an email address <a href="mailto:john@example.com"><span>john@example.com</span></a>.',
2696  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2697  ],
2698  'mailto link with ATagParams' => [
2699  'Some text with an email address mailto:john@example.com.',
2700  [
2701  'ATagParams' => 'class="email"',
2702  ],
2703  'Some text with an email address <a href="mailto:john@example.com" class="email">john@example.com</a>.',
2704  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withAttribute('class', 'email')->withLinkText('john@example.com'),
2705  ],
2706  ];
2707  }
2708 
2709  #[DataProvider('mailtoMakelinksDataProvider')]
2710  #[Test]
2711  public function ‪mailtoMakelinksReturnsMailToLink(string $data, array $configuration, string $expectedResult, LinkResult $linkResult): void
2712  {
2713  $linkFactory = $this->getMockBuilder(LinkFactory::class)->disableOriginalConstructor()->getMock();
2714  $linkFactory->method('create')->willReturn($linkResult);
2715  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
2716 
2717  self::assertSame($expectedResult, $this->subject->_call('mailto_makelinks', $data, $configuration));
2718  }
2719 
2720  #[Test]
2722  {
2723  self::assertSame('mailto:', $this->subject->_call('mailto_makelinks', 'mailto:', []));
2724  }
2725 
2726  #[Test]
2727  public function ‪stdWrap_splitObjReturnsCount(): void
2728  {
2729  $conf = [
2730  'token' => ',',
2731  'returnCount' => 1,
2732  ];
2733  $expectedResult = 5;
2734  $amountOfEntries = $this->subject->splitObj('1, 2, 3, 4, 5', $conf);
2735  self::assertSame(
2736  $expectedResult,
2737  $amountOfEntries
2738  );
2739  }
2740 
2746  public static function ‪calculateCacheKeyDataProvider(): array
2747  {
2748  $value = ‪StringUtility::getUniqueId('value');
2749  $wrap = [‪StringUtility::getUniqueId('wrap')];
2750  $valueConf = ['key' => $value];
2751  $wrapConf = ['key.' => $wrap];
2752  $conf = array_merge($valueConf, $wrapConf);
2753  $will = ‪StringUtility::getUniqueId('stdWrap');
2754 
2755  return [
2756  'no conf' => [
2757  '',
2758  [],
2759  0,
2760  null,
2761  null,
2762  null,
2763  ],
2764  'value conf only' => [
2765  $value,
2766  $valueConf,
2767  0,
2768  null,
2769  null,
2770  null,
2771  ],
2772  'wrap conf only' => [
2773  $will,
2774  $wrapConf,
2775  1,
2776  '',
2777  $wrap,
2778  $will,
2779  ],
2780  'full conf' => [
2781  $will,
2782  $conf,
2783  1,
2784  $value,
2785  $wrap,
2786  $will,
2787  ],
2788  ];
2789  }
2790 
2804  #[DataProvider('calculateCacheKeyDataProvider')]
2805  #[Test]
2806  public function ‪calculateCacheKey(string $expect, array $conf, int $times, ?string $with, ?array $withWrap, ?string $will): void
2807  {
2808  ‪$subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['stdWrap']);
2809  ‪$subject->expects(self::exactly($times))
2810  ->method('stdWrap')
2811  ->with($with, $withWrap)
2812  ->willReturn($will);
2813 
2814  $result = ‪$subject->_call('calculateCacheKey', $conf);
2815  self::assertSame($expect, $result);
2816  }
2817 
2823  public static function ‪getFromCacheDataProvider(): array
2824  {
2825  $conf = [‪StringUtility::getUniqueId('conf')];
2826  return [
2827  'empty cache key' => [
2828  false,
2829  $conf,
2830  '',
2831  0,
2832  null,
2833  ],
2834  'non-empty cache key' => [
2835  'value',
2836  $conf,
2837  'non-empty-key',
2838  1,
2839  'value',
2840  ],
2841  ];
2842  }
2843 
2858  #[DataProvider('getFromCacheDataProvider')]
2859  #[Test]
2860  public function ‪getFromCache(string|bool $expect, array $conf, string $cacheKey, int $times, ?string $cached): void
2861  {
2862  $tags = [‪StringUtility::getUniqueId('tags')];
2863 
2864  ‪$subject = $this->getAccessibleMock(
2865  ContentObjectRenderer::class,
2866  [
2867  'calculateCacheKey',
2868  'getRequest',
2869  'getTypoScriptFrontendController',
2870  ]
2871  );
2872  ‪$subject
2873  ->expects(self::once())
2874  ->method('calculateCacheKey')
2875  ->with($conf)
2876  ->willReturn($cacheKey);
2877  $request = (new ‪ServerRequest())->withAttribute('frontend.cache.instruction', new ‪CacheInstruction());
2878  ‪$subject
2879  ->expects(self::once())
2880  ->method('getRequest')
2881  ->willReturn($request);
2882  $typoScriptFrontendController = $this->createMock(TypoScriptFrontendController::class);
2883  $typoScriptFrontendController
2884  ->expects(self::exactly($times))
2885  ->method('addCacheTags')
2886  ->with($tags);
2887  ‪$subject
2888  ->expects(self::exactly($times))
2889  ->method('getTypoScriptFrontendController')
2890  ->willReturn($typoScriptFrontendController);
2891  $cacheFrontend = $this->createMock(CacheFrontendInterface::class);
2892  $cacheFrontend
2893  ->expects(self::exactly($times))
2894  ->method('get')
2895  ->with($cacheKey)
2896  ->willReturn(['content' => $cached, 'cacheTags' => $tags]);
2897  $cacheManager = $this->createMock(CacheManager::class);
2898  $cacheManager
2899  ->method('getCache')
2900  ->willReturn($cacheFrontend);
2901  GeneralUtility::setSingletonInstance(
2902  CacheManager::class,
2903  $cacheManager
2904  );
2905  self::assertSame($expect, ‪$subject->_call('getFromCache', $conf));
2906  }
2907 
2913  public static function ‪getFieldValDataProvider(): array
2914  {
2915  return [
2916  'invalid single key' => [null, 'invalid'],
2917  'single key of null' => [null, 'null'],
2918  'single key of empty string' => ['', 'empty'],
2919  'single key of non-empty string' => ['string 1', 'string1'],
2920  'single key of boolean false' => [false, 'false'],
2921  'single key of boolean true' => [true, 'true'],
2922  'single key of integer 0' => [0, 'zero'],
2923  'single key of integer 1' => [1, 'one'],
2924  'single key to be trimmed' => ['string 1', ' string1 '],
2925 
2926  'split nothing' => ['', '//'],
2927  'split one before' => ['string 1', 'string1//'],
2928  'split one after' => ['string 1', '//string1'],
2929  'split two ' => ['string 1', 'string1//string2'],
2930  'split three ' => ['string 1', 'string1//string2//string3'],
2931  'split to be trimmed' => ['string 1', ' string1 // string2 '],
2932  '0 is not empty' => [0, '// zero'],
2933  '1 is not empty' => [1, '// one'],
2934  'true is not empty' => [true, '// true'],
2935  'false is empty' => ['', '// false'],
2936  'null is empty' => ['', '// null'],
2937  'empty string is empty' => ['', '// empty'],
2938  'string is not empty' => ['string 1', '// string1'],
2939  'first non-empty winns' => [0, 'false//empty//null//zero//one'],
2940  'empty string is fallback' => ['', 'false // empty // null'],
2941  ];
2942  }
2943 
2980  #[DataProvider('getFieldValDataProvider')]
2981  #[Test]
2982  public function ‪getFieldVal(mixed $expect, string ‪$fields): void
2983  {
2984  $data = [
2985  'string1' => 'string 1',
2986  'string2' => 'string 2',
2987  'string3' => 'string 3',
2988  'empty' => '',
2989  'null' => null,
2990  'false' => false,
2991  'true' => true,
2992  'zero' => 0,
2993  'one' => 1,
2994  ];
2995  $this->subject->_set('data', $data);
2996  self::assertSame($expect, $this->subject->getFieldVal(‪$fields));
2997  }
2998 
2999  public static function ‪caseshiftDataProvider(): array
3000  {
3001  return [
3002  'lower' => [
3003  'x y', // expected
3004  'X Y', // content
3005  'lower', // case
3006  ],
3007  'upper' => [
3008  'X Y',
3009  'x y',
3010  'upper',
3011  ],
3012  'capitalize' => [
3013  'One Two',
3014  'one two',
3015  'capitalize',
3016  ],
3017  'ucfirst' => [
3018  'One two',
3019  'one two',
3020  'ucfirst',
3021  ],
3022  'lcfirst' => [
3023  'oNE TWO',
3024  'ONE TWO',
3025  'lcfirst',
3026  ],
3027  'uppercamelcase' => [
3028  'CamelCase',
3029  'camel_case',
3030  'uppercamelcase',
3031  ],
3032  'lowercamelcase' => [
3033  'camelCase',
3034  'camel_case',
3035  'lowercamelcase',
3036  ],
3037  ];
3038  }
3039 
3040  #[DataProvider('caseshiftDataProvider')]
3041  #[Test]
3042  public function ‪caseshift(string $expected, string $content, string $case): void
3043  {
3044  self::assertSame($expected, $this->subject->caseshift($content, $case));
3045  }
3046 
3047  public static function ‪HTMLcaseshiftDataProvider(): array
3048  {
3049  return [
3050  'simple text' => [
3051  'text',
3052  'TEXT',
3053  ],
3054  'simple tag' => [
3055  '<i>text</i>',
3056  '<i>TEXT</i>',
3057  ],
3058  'multiple nested tags with classes' => [
3059  '<div class="typo3">'
3060  . '<p>A <b>bold<\b> word.</p>'
3061  . '<p>An <i>italic<\i> word.</p>'
3062  . '</div>',
3063  '<div class="typo3">'
3064  . '<p>A <b>BOLD<\b> WORD.</p>'
3065  . '<p>AN <i>ITALIC<\i> WORD.</p>'
3066  . '</div>',
3067  ],
3068  ];
3069  }
3070 
3071  #[DataProvider('HTMLcaseshiftDataProvider')]
3072  #[Test]
3073  public function ‪HTMLcaseshift(string $content, string $expected): void
3074  {
3075  self::assertSame($expected, (new ‪ContentObjectRenderer())->‪HTMLcaseshift($content, 'upper'));
3076  }
3077 
3083  public static function ‪stdWrap_HTMLparserDataProvider(): array
3084  {
3085  $content = ‪StringUtility::getUniqueId('content');
3086  $parsed = ‪StringUtility::getUniqueId('parsed');
3087  return [
3088  'no config' => [
3089  $content,
3090  $content,
3091  [],
3092  0,
3093  $parsed,
3094  ],
3095  'no array' => [
3096  $content,
3097  $content,
3098  ['HTMLparser.' => 1],
3099  0,
3100  $parsed,
3101  ],
3102  'empty array' => [
3103  $parsed,
3104  $content,
3105  ['HTMLparser.' => []],
3106  1,
3107  $parsed,
3108  ],
3109  'non-empty array' => [
3110  $parsed,
3111  $content,
3112  ['HTMLparser.' => [true]],
3113  1,
3114  $parsed,
3115  ],
3116  ];
3117  }
3118 
3139  #[DataProvider('stdWrap_HTMLparserDataProvider')]
3140  #[Test]
3141  public function ‪stdWrap_HTMLparser(
3142  string $expect,
3143  string $content,
3144  array $conf,
3145  int $times,
3146  string $will
3147  ): void {
3148  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3149  ->onlyMethods(['HTMLparser_TSbridge'])->getMock();
3150  ‪$subject
3151  ->expects(self::exactly($times))
3152  ->method('HTMLparser_TSbridge')
3153  ->with($content, $conf['HTMLparser.'] ?? [])
3154  ->willReturn($will);
3155  self::assertSame(
3156  $expect,
3157  ‪$subject->‪stdWrap_HTMLparser($content, $conf)
3158  );
3159  }
3160 
3162  {
3163  return [
3164  'No Tag' => [
3165  [],
3166  ['addPageCacheTags' => ''],
3167  ],
3168  'Two expectedTags' => [
3169  ['tag1', 'tag2'],
3170  ['addPageCacheTags' => 'tag1,tag2'],
3171  ],
3172  'Two expectedTags plus one with stdWrap' => [
3173  ['tag1', 'tag2', 'tag3'],
3174  [
3175  'addPageCacheTags' => 'tag1,tag2',
3176  'addPageCacheTags.' => ['wrap' => '|,tag3'],
3177  ],
3178  ],
3179  ];
3180  }
3181 
3182  #[DataProvider('stdWrap_addPageCacheTagsAddsPageTagsDataProvider')]
3183  #[Test]
3184  public function ‪stdWrap_addPageCacheTagsAddsPageTags(array $expectedTags, array $configuration): void
3185  {
3186  $this->subject->stdWrap_addPageCacheTags('', $configuration);
3187  self::assertEquals($expectedTags, $this->frontendControllerMock->_get('pageCacheTags'));
3188  }
3189 
3200  #[Test]
3201  public function ‪stdWrap_age(): void
3202  {
3203  $now = 10;
3204  $content = '9';
3205  $conf = ['age' => ‪StringUtility::getUniqueId('age')];
3206  $return = ‪StringUtility::getUniqueId('return');
3207  $difference = $now - (int)$content;
3208  ‪$GLOBALS['EXEC_TIME'] = $now;
3209  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3210  ->onlyMethods(['calcAge'])->getMock();
3211  ‪$subject
3212  ->expects(self::once())
3213  ->method('calcAge')
3214  ->with($difference, $conf['age'])
3215  ->willReturn($return);
3216  self::assertSame($return, ‪$subject->‪stdWrap_age($content, $conf));
3217  }
3218 
3230  #[Test]
3231  public function ‪stdWrap_append(): void
3232  {
3233  $debugKey = '/stdWrap/.append';
3234  $content = ‪StringUtility::getUniqueId('content');
3235  $conf = [
3236  'append' => ‪StringUtility::getUniqueId('append'),
3237  'append.' => [‪StringUtility::getUniqueId('append.')],
3238  ];
3239  $return = ‪StringUtility::getUniqueId('return');
3240  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3241  ->onlyMethods(['cObjGetSingle'])->getMock();
3242  ‪$subject
3243  ->expects(self::once())
3244  ->method('cObjGetSingle')
3245  ->with($conf['append'], $conf['append.'], $debugKey)
3246  ->willReturn($return);
3247  self::assertSame(
3248  $content . $return,
3249  ‪$subject->‪stdWrap_append($content, $conf)
3250  );
3251  }
3252 
3258  public static function ‪stdWrapBrDataProvider(): array
3259  {
3260  return [
3261  'no xhtml with LF in between' => [
3262  'one<br>' . LF . 'two',
3263  'one' . LF . 'two',
3264  null,
3265  ],
3266  'no xhtml with LF in between and around' => [
3267  '<br>' . LF . 'one<br>' . LF . 'two<br>' . LF,
3268  LF . 'one' . LF . 'two' . LF,
3269  null,
3270  ],
3271  'xhtml with LF in between' => [
3272  'one<br />' . LF . 'two',
3273  'one' . LF . 'two',
3274  'xhtml_strict',
3275  ],
3276  'xhtml with LF in between and around' => [
3277  '<br />' . LF . 'one<br />' . LF . 'two<br />' . LF,
3278  LF . 'one' . LF . 'two' . LF,
3279  'xhtml_strict',
3280  ],
3281  ];
3282  }
3283 
3291  #[DataProvider('stdWrapBrDataProvider')]
3292  #[Test]
3293  public function ‪stdWrap_br(string $expected, string $input, ?string $doctype): void
3294  {
3295  $pageRenderer = $this->getMockBuilder(PageRenderer::class)->disableOriginalConstructor()->onlyMethods([])->getMock();
3296  $pageRenderer->setLanguage(new ‪Locale());
3297  $pageRenderer->setDocType(DocType::createFromConfigurationKey($doctype));
3298  GeneralUtility::setSingletonInstance(PageRenderer::class, $pageRenderer);
3299  self::assertSame($expected, $this->subject->stdWrap_br($input));
3300  }
3301 
3305  public static function ‪stdWrapBrTagDataProvider(): array
3306  {
3307  $noConfig = [];
3308  $config1 = ['brTag' => '<br/>'];
3309  $config2 = ['brTag' => '<br>'];
3310  return [
3311  'no config: one break at the beginning' => [LF . 'one' . LF . 'two', 'onetwo', $noConfig],
3312  'no config: multiple breaks at the beginning' => [LF . LF . 'one' . LF . 'two', 'onetwo', $noConfig],
3313  'no config: one break at the end' => ['one' . LF . 'two' . LF, 'onetwo', $noConfig],
3314  'no config: multiple breaks at the end' => ['one' . LF . 'two' . LF . LF, 'onetwo', $noConfig],
3315 
3316  'config1: one break at the beginning' => [LF . 'one' . LF . 'two', '<br/>one<br/>two', $config1],
3317  'config1: multiple breaks at the beginning' => [
3318  LF . LF . 'one' . LF . 'two',
3319  '<br/><br/>one<br/>two',
3320  $config1,
3321  ],
3322  'config1: one break at the end' => ['one' . LF . 'two' . LF, 'one<br/>two<br/>', $config1],
3323  'config1: multiple breaks at the end' => ['one' . LF . 'two' . LF . LF, 'one<br/>two<br/><br/>', $config1],
3324 
3325  'config2: one break at the beginning' => [LF . 'one' . LF . 'two', '<br>one<br>two', $config2],
3326  'config2: multiple breaks at the beginning' => [
3327  LF . LF . 'one' . LF . 'two',
3328  '<br><br>one<br>two',
3329  $config2,
3330  ],
3331  'config2: one break at the end' => ['one' . LF . 'two' . LF, 'one<br>two<br>', $config2],
3332  'config2: multiple breaks at the end' => ['one' . LF . 'two' . LF . LF, 'one<br>two<br><br>', $config2],
3333  ];
3334  }
3335 
3339  #[DataProvider('stdWrapBrTagDataProvider')]
3340  #[Test]
3341  public function ‪stdWrap_brTag(string $input, string $expected, array $config): void
3342  {
3343  self::assertEquals($expected, $this->subject->stdWrap_brTag($input, $config));
3344  }
3345 
3357  #[Test]
3358  public function ‪stdWrap_cObject(): void
3359  {
3360  $debugKey = '/stdWrap/.cObject';
3361  $content = ‪StringUtility::getUniqueId('content');
3362  $conf = [
3363  'cObject' => ‪StringUtility::getUniqueId('cObject'),
3364  'cObject.' => [‪StringUtility::getUniqueId('cObject.')],
3365  ];
3366  $return = ‪StringUtility::getUniqueId('return');
3367  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3368  ->onlyMethods(['cObjGetSingle'])->getMock();
3369  ‪$subject
3370  ->expects(self::once())
3371  ->method('cObjGetSingle')
3372  ->with($conf['cObject'], $conf['cObject.'], $debugKey)
3373  ->willReturn($return);
3374  self::assertSame(
3375  $return,
3376  ‪$subject->‪stdWrap_cObject($content, $conf)
3377  );
3378  }
3379 
3380  public static function ‪stdWrap_orderedStdWrapDataProvider(): array
3381  {
3382  return [
3383  'standard case: given order 1, 2' => [
3384  [
3385  'orderedStdWrap.' => [
3386  '1.' => [
3387  'wrap' => '<inner>|</inner>',
3388  ],
3389  '2.' => [
3390  'wrap' => '<outer>|</outer>',
3391  ],
3392  ],
3393  ],
3394  '<outer><inner>someContent</inner></outer>',
3395  ],
3396  'inverted: given order 2, 1' => [
3397  [
3398  'orderedStdWrap.' => [
3399  '2.' => [
3400  'wrap' => '<outer>|</outer>',
3401  ],
3402  '1.' => [
3403  'wrap' => '<inner>|</inner>',
3404  ],
3405  ],
3406  ],
3407  '<outer><inner>someContent</inner></outer>',
3408  ],
3409  '0 as integer: given order 0, 2' => [
3410  [
3411  'orderedStdWrap.' => [
3412  '0.' => [
3413  'wrap' => '<inner>|</inner>',
3414  ],
3415  '2.' => [
3416  'wrap' => '<outer>|</outer>',
3417  ],
3418  ],
3419  ],
3420  '<outer><inner>someContent</inner></outer>',
3421  ],
3422  'negative integers: given order 2, -2' => [
3423  [
3424  'orderedStdWrap.' => [
3425  '2.' => [
3426  'wrap' => '<outer>|</outer>',
3427  ],
3428  '-2.' => [
3429  'wrap' => '<inner>|</inner>',
3430  ],
3431  ],
3432  ],
3433  '<outer><inner>someContent</inner></outer>',
3434  ],
3435  'chars are casted to key 0, that is not in the array' => [
3436  [
3437  'orderedStdWrap.' => [
3438  '2.' => [
3439  'wrap' => '<inner>|</inner>',
3440  ],
3441  'xxx.' => [
3442  'wrap' => '<invalid>|</invalid>',
3443  ],
3444  ],
3445  ],
3446  '<inner>someContent</inner>',
3447  ],
3448  ];
3449  }
3450 
3451  #[DataProvider('stdWrap_orderedStdWrapDataProvider')]
3452  #[Test]
3453  public function ‪stdWrap_orderedStdWrap(array $config, string $expected): void
3454  {
3455  self::assertSame($expected, (new ‪ContentObjectRenderer())->‪stdWrap_orderedStdWrap('someContent', $config));
3456  }
3457 
3463  public static function ‪stdWrap_cacheReadDataProvider(): array
3464  {
3465  $cacheConf = [‪StringUtility::getUniqueId('cache.')];
3466  $conf = ['cache.' => $cacheConf];
3467  return [
3468  'no conf' => [
3469  'content',
3470  'content',
3471  [],
3472  0,
3473  null,
3474  null,
3475  ],
3476  'no cache. conf' => [
3477  'content',
3478  'content',
3479  ['otherConf' => 1],
3480  0,
3481  null,
3482  null,
3483  ],
3484  'non-cached simulation' => [
3485  'content',
3486  'content',
3487  $conf,
3488  1,
3489  $cacheConf,
3490  false,
3491  ],
3492  'cached simulation' => [
3493  'cachedContent',
3494  'content',
3495  $conf,
3496  1,
3497  $cacheConf,
3498  'cachedContent',
3499  ],
3500  ];
3501  }
3502 
3517  #[DataProvider('stdWrap_cacheReadDataProvider')]
3518  #[Test]
3519  public function ‪stdWrap_cacheRead(
3520  string $expect,
3521  string $input,
3522  array $conf,
3523  int $times,
3524  ?array $with,
3525  string|bool|null $will
3526  ): void {
3527  ‪$subject = $this->getAccessibleMock(
3528  ContentObjectRenderer::class,
3529  ['getFromCache']
3530  );
3531  ‪$subject
3532  ->expects(self::exactly($times))
3533  ->method('getFromCache')
3534  ->with($with)
3535  ->willReturn($will);
3536  self::assertSame(
3537  $expect,
3538  ‪$subject->‪stdWrap_cacheRead($input, $conf)
3539  );
3540  }
3541 
3547  public static function ‪stdWrap_cacheStoreDataProvider(): array
3548  {
3549  return [
3550  'Return immediate with no conf' => [
3551  null,
3552  0,
3553  null,
3554  ],
3555  'Return immediate with empty key' => [
3556  [‪StringUtility::getUniqueId('cache.')],
3557  1,
3558  '0',
3559  0,
3560  ],
3561  ];
3562  }
3563 
3577  #[DataProvider('stdWrap_cacheStoreDataProvider')]
3578  #[Test]
3579  public function ‪stdWrap_cacheStore(
3580  ?array $confCache,
3581  int $times,
3582  mixed $key,
3583  ): void {
3584  $content = ‪StringUtility::getUniqueId('content');
3585  $conf = [];
3586  $conf['cache.'] = $confCache;
3587  ‪$subject = $this->getAccessibleMock(
3588  ContentObjectRenderer::class,
3589  [
3590  'calculateCacheKey',
3591  'calculateCacheTags',
3592  'calculateCacheLifetime',
3593  'getTypoScriptFrontendController',
3594  ]
3595  );
3596  ‪$subject->expects(self::exactly($times))->method('calculateCacheKey')->with($confCache)->willReturn($key);
3597  self::assertSame(
3598  $content,
3599  ‪$subject->‪stdWrap_cacheStore($content, $conf)
3600  );
3601  }
3602 
3614  #[Test]
3616  {
3617  $beforeStdWrapContentStoredInCacheEvent = null;
3618  $modifiedContent = '---modified-content---';
3619 
3621  $container = GeneralUtility::getContainer();
3622  $container->set(
3623  'before-stdWrap-content-stored-in-cache-listener',
3624  static function (‪BeforeStdWrapContentStoredInCacheEvent $event) use (&$beforeStdWrapContentStoredInCacheEvent, $modifiedContent) {
3625  $beforeStdWrapContentStoredInCacheEvent = $event;
3626  $event->‪setContent($modifiedContent);
3627  }
3628  );
3629 
3630  $listenerProvider = new ‪ListenerProvider($container);
3631  $listenerProvider->addListener(BeforeStdWrapContentStoredInCacheEvent::class, 'before-stdWrap-content-stored-in-cache-listener');
3632  $container->set(ListenerProvider::class, $listenerProvider);
3633  $container->set(EventDispatcherInterface::class, new ‪EventDispatcher($listenerProvider));
3634 
3635  $content = ‪StringUtility::getUniqueId('content');
3636  $tags = [‪StringUtility::getUniqueId('tags')];
3637  $key = ‪StringUtility::getUniqueId('key');
3638  $lifetime = 100;
3639  $cacheConfig = [
3641  ];
3642  $configuration = [
3643  'cache.' => $cacheConfig,
3644  ];
3645 
3646  ‪$subject = $this->getAccessibleMock(
3647  ContentObjectRenderer::class,
3648  [
3649  'calculateCacheKey',
3650  'calculateCacheTags',
3651  'calculateCacheLifetime',
3652  'getTypoScriptFrontendController',
3653  ]
3654  );
3655  ‪$subject->expects(self::once())->method('calculateCacheKey')->with($cacheConfig)->willReturn($key);
3656  ‪$subject->expects(self::once())->method('calculateCacheTags')->with($cacheConfig)->willReturn($tags);
3657  ‪$subject->expects(self::once())->method('calculateCacheLifetime')->with($cacheConfig)->willReturn($lifetime);
3658  $typoScriptFrontendController = $this->createMock(TypoScriptFrontendController::class);
3659  $typoScriptFrontendController->expects(self::once())->method('addCacheTags')->with($tags);
3660  ‪$subject->expects(self::once())->method('getTypoScriptFrontendController')->willReturn($typoScriptFrontendController);
3661  $cacheFrontend = $this->createMock(CacheFrontendInterface::class);
3662  $cacheFrontend
3663  ->expects(self::once())
3664  ->method('set')
3665  ->with($key, ['content' => $modifiedContent, 'cacheTags' => $tags], $tags, $lifetime)
3666  ->willReturn(null);
3667  $cacheManager = $this->createMock(CacheManager::class);
3668  $cacheManager
3669  ->method('getCache')
3670  ->willReturn($cacheFrontend);
3671  GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager);
3672 
3673  $result = ‪$subject->‪stdWrap_cacheStore($content, $configuration);
3674 
3675  self::assertSame($modifiedContent, $result);
3676  self::assertInstanceOf(BeforeStdWrapContentStoredInCacheEvent::class, $beforeStdWrapContentStoredInCacheEvent);
3677  self::assertSame($modifiedContent, $beforeStdWrapContentStoredInCacheEvent->getContent());
3678  self::assertSame($tags, $beforeStdWrapContentStoredInCacheEvent->getTags());
3679  self::assertSame($key, $beforeStdWrapContentStoredInCacheEvent->getKey());
3680  self::assertSame($lifetime, $beforeStdWrapContentStoredInCacheEvent->getLifetime());
3681  self::assertSame($configuration, $beforeStdWrapContentStoredInCacheEvent->getConfiguration());
3682  self::assertSame(‪$subject, $beforeStdWrapContentStoredInCacheEvent->getContentObjectRenderer());
3683  }
3684 
3695  #[Test]
3696  public function ‪stdWrap_case(): void
3697  {
3698  $content = ‪StringUtility::getUniqueId();
3699  $conf = [
3700  'case' => ‪StringUtility::getUniqueId('used'),
3701  'case.' => [‪StringUtility::getUniqueId('discarded')],
3702  ];
3703  $return = ‪StringUtility::getUniqueId();
3704  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3705  ->onlyMethods(['HTMLcaseshift'])->getMock();
3706  ‪$subject
3707  ->expects(self::once())
3708  ->method('HTMLcaseshift')
3709  ->with($content, $conf['case'])
3710  ->willReturn($return);
3711  self::assertSame(
3712  $return,
3713  ‪$subject->‪stdWrap_case($content, $conf)
3714  );
3715  }
3716 
3720  #[Test]
3721  public function ‪stdWrap_char(): void
3722  {
3723  $input = 'discarded';
3724  $expected = 'C';
3725  self::assertEquals($expected, $this->subject->stdWrap_char($input, ['char' => '67']));
3726  }
3727 
3738  #[Test]
3739  public function ‪stdWrap_crop(): void
3740  {
3741  $content = ‪StringUtility::getUniqueId('content');
3742  $conf = [
3743  'crop' => ‪StringUtility::getUniqueId('crop'),
3744  'crop.' => ‪StringUtility::getUniqueId('not used'),
3745  ];
3746  $return = ‪StringUtility::getUniqueId('return');
3747  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3748  ->onlyMethods(['crop'])->getMock();
3749  ‪$subject
3750  ->expects(self::once())
3751  ->method('crop')
3752  ->with($content, $conf['crop'])
3753  ->willReturn($return);
3754  self::assertSame(
3755  $return,
3756  ‪$subject->‪stdWrap_crop($content, $conf)
3757  );
3758  }
3759 
3770  #[Test]
3771  public function ‪stdWrap_cropHTML(): void
3772  {
3773  $content = ‪StringUtility::getUniqueId('content');
3774  $conf = [
3775  'cropHTML' => ‪StringUtility::getUniqueId('cropHTML'),
3776  'cropHTML.' => ‪StringUtility::getUniqueId('not used'),
3777  ];
3778  $return = ‪StringUtility::getUniqueId('return');
3779  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3780  ->onlyMethods(['cropHTML'])->getMock();
3781  ‪$subject
3782  ->expects(self::once())
3783  ->method('cropHTML')
3784  ->with($content, $conf['cropHTML'])
3785  ->willReturn($return);
3786  self::assertSame(
3787  $return,
3788  ‪$subject->‪stdWrap_cropHTML($content, $conf)
3789  );
3790  }
3791 
3792  public static function ‪stdWrap_formattedDateProvider(): \Generator
3793  {
3794  yield 'regular formatting - no locale' => [
3795  '2023.02.02 AD at 13:05:00 UTC',
3796  "yyyy.MM.dd G 'at' HH:mm:ss zzz",
3797  ];
3798  yield 'full - no locale' => [
3799  'Thursday, February 2, 2023 at 13:05:00 Coordinated Universal Time',
3800  'FULL',
3801  ];
3802  yield 'long - no locale' => [
3803  'February 2, 2023 at 13:05:00 UTC',
3804  'LONG',
3805  ];
3806  yield 'medium - no locale' => [
3807  'Feb 2, 2023, 13:05:00',
3808  'MEDIUM',
3809  ];
3810  yield 'medium with int - no locale' => [
3811  'Feb 2, 2023, 13:05:00',
3812  \IntlDateFormatter::MEDIUM,
3813  ];
3814  yield 'short - no locale' => [
3815  '2/2/23, 13:05',
3816  'SHORT',
3817  ];
3818  yield 'regular formatting - german locale' => [
3819  '2023.02.02 n. Chr. um 13:05:00 UTC',
3820  "yyyy.MM.dd G 'um' HH:mm:ss zzz",
3821  'de-DE',
3822  ];
3823  yield 'full - german locale' => [
3824  'Donnerstag, 2. Februar 2023 um 13:05:00 Koordinierte Weltzeit',
3825  'FULL',
3826  'de-DE',
3827  ];
3828  yield 'long - german locale' => [
3829  '2. Februar 2023 um 13:05:00 UTC',
3830  'LONG',
3831  'de-DE',
3832  ];
3833  yield 'medium - german locale' => [
3834  '02.02.2023, 13:05:00',
3835  'MEDIUM',
3836  'de-DE',
3837  ];
3838  yield 'short - german locale' => [
3839  '02.02.23, 13:05',
3840  'SHORT',
3841  'de-DE',
3842  ];
3843  yield 'custom date only - german locale' => [
3844  '02. Februar 2023',
3845  'dd. MMMM yyyy',
3846  'de-DE',
3847  ];
3848  yield 'custom time only - german locale' => [
3849  '13:05:00',
3850  'HH:mm:ss',
3851  'de-DE',
3852  ];
3853  yield 'given date and time - german locale' => [
3854  'Freitag, 20. Februar 1998 um 03:00:00 Koordinierte Weltzeit',
3855  'FULL',
3856  'de-DE',
3857  '1998-02-20 3:00:00',
3858  ];
3859  }
3860 
3861  #[DataProvider('stdWrap_formattedDateProvider')]
3862  #[Test]
3863  public function ‪stdWrap_formattedDate(string $expected, mixed $pattern, ?string $locale = null, ?string $givenDate = null): void
3864  {
3865  $context = new ‪Context();
3866  $context->setAspect('date', new ‪DateTimeAspect(new \DateTimeImmutable('2023-02-02 13:05:00')));
3867  GeneralUtility::setSingletonInstance(Context::class, $context);
3869  $site = $this->‪createSiteWithLanguage([
3870  'base' => '/',
3871  'languageId' => 2,
3872  'locale' => 'en_UK',
3873  ]);
3874  $request = (new ‪ServerRequest())->withAttribute('language', $site->getLanguageById(2));
3875  ‪$subject->‪setRequest($request);
3876  $conf = ['formattedDate' => $pattern];
3877  if ($locale !== null) {
3878  $conf['formattedDate.']['locale'] = $locale;
3879  }
3880  self::assertEquals($expected, ‪$subject->‪stdWrap_formattedDate((string)$givenDate, $conf));
3881  }
3882 
3888  public static function ‪stdWrap_csConvDataProvider(): array
3889  {
3890  return [
3891  'empty string from ISO-8859-15' => [
3892  '',
3893  mb_convert_encoding('', 'ISO-8859-15', 'UTF-8'),
3894  ['csConv' => 'ISO-8859-15'],
3895  ],
3896  'empty string from BIG-5' => [
3897  '',
3898  mb_convert_encoding('', 'BIG-5'),
3899  ['csConv' => 'BIG-5'],
3900  ],
3901  '"0" from ISO-8859-15' => [
3902  '0',
3903  mb_convert_encoding('0', 'ISO-8859-15', 'UTF-8'),
3904  ['csConv' => 'ISO-8859-15'],
3905  ],
3906  '"0" from BIG-5' => [
3907  '0',
3908  mb_convert_encoding('0', 'BIG-5'),
3909  ['csConv' => 'BIG-5'],
3910  ],
3911  'euro symbol from ISO-88859-15' => [
3912  '€',
3913  mb_convert_encoding('€', 'ISO-8859-15', 'UTF-8'),
3914  ['csConv' => 'ISO-8859-15'],
3915  ],
3916  'good morning from BIG-5' => [
3917  '早安',
3918  mb_convert_encoding('早安', 'BIG-5'),
3919  ['csConv' => 'BIG-5'],
3920  ],
3921  ];
3922  }
3923 
3931  #[DataProvider('stdWrap_csConvDataProvider')]
3932  #[Test]
3933  public function ‪stdWrap_csConv(string $expected, string $input, array $conf): void
3934  {
3935  self::assertSame(
3936  $expected,
3937  $this->subject->stdWrap_csConv($input, $conf)
3938  );
3939  }
3940 
3950  #[Test]
3951  public function ‪stdWrap_current(): void
3952  {
3953  $data = [
3954  'currentValue_kidjls9dksoje' => 'default',
3955  'currentValue_new' => 'new',
3956  ];
3957  $this->subject->_set('data', $data);
3958  self::assertSame(
3959  'currentValue_kidjls9dksoje',
3960  $this->subject->_get('currentValKey')
3961  );
3962  self::assertSame(
3963  'default',
3964  $this->subject->stdWrap_current('discarded', ['discarded'])
3965  );
3966  $this->subject->_set('currentValKey', 'currentValue_new');
3967  self::assertSame(
3968  'new',
3969  $this->subject->stdWrap_current('discarded', ['discarded'])
3970  );
3971  }
3972 
3978  public static function ‪stdWrap_dataDataProvider(): array
3979  {
3980  $data = [‪StringUtility::getUniqueId('data')];
3981  return [
3982  'default' => [$data, $data, ''],
3983  ];
3984  }
3985 
3998  #[DataProvider('stdWrap_dataDataProvider')]
3999  #[Test]
4000  public function ‪stdWrap_data(array $expect, array $data): void
4001  {
4002  $conf = ['data' => ‪StringUtility::getUniqueId('conf.data')];
4003  $return = ‪StringUtility::getUniqueId('return');
4004  ‪$subject = $this->getAccessibleMock(
4005  ContentObjectRenderer::class,
4006  ['getData']
4007  );
4008  ‪$subject->_set('data', $data);
4009  ‪$subject
4010  ->expects(self::once())
4011  ->method('getData')
4012  ->with($conf['data'], $expect)
4013  ->willReturn($return);
4014  self::assertSame($return, ‪$subject->‪stdWrap_data('discard', $conf));
4015  }
4016 
4027  #[Test]
4028  public function ‪stdWrap_dataWrap(): void
4029  {
4030  $content = ‪StringUtility::getUniqueId('content');
4031  $conf = [
4032  'dataWrap' => ‪StringUtility::getUniqueId('dataWrap'),
4033  'dataWrap.' => [‪StringUtility::getUniqueId('not used')],
4034  ];
4035  $return = ‪StringUtility::getUniqueId('return');
4036  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
4037  ->onlyMethods(['dataWrap'])->getMock();
4038  ‪$subject
4039  ->expects(self::once())
4040  ->method('dataWrap')
4041  ->with($content, $conf['dataWrap'])
4042  ->willReturn($return);
4043  self::assertSame(
4044  $return,
4045  ‪$subject->‪stdWrap_dataWrap($content, $conf)
4046  );
4047  }
4048 
4054  public static function ‪stdWrap_dateDataProvider(): array
4055  {
4056  // Fictive execution time: 2015-10-02 12:00
4057  $now = 1443780000;
4058  return [
4059  'given timestamp' => [
4060  '02.10.2015',
4061  $now,
4062  ['date' => 'd.m.Y'],
4063  $now,
4064  ],
4065  'empty string' => [
4066  '02.10.2015',
4067  '',
4068  ['date' => 'd.m.Y'],
4069  $now,
4070  ],
4071  'testing null' => [
4072  '02.10.2015',
4073  null,
4074  ['date' => 'd.m.Y'],
4075  $now,
4076  ],
4077  'given timestamp return GMT' => [
4078  '02.10.2015 10:00:00',
4079  $now,
4080  [
4081  'date' => 'd.m.Y H:i:s',
4082  'date.' => ['GMT' => true],
4083  ],
4084  $now,
4085  ],
4086  ];
4087  }
4088 
4097  #[DataProvider('stdWrap_dateDataProvider')]
4098  #[Test]
4099  public function ‪stdWrap_date(string $expected, mixed $content, array $conf, int $now): void
4100  {
4101  ‪$GLOBALS['EXEC_TIME'] = $now;
4102  self::assertEquals(
4103  $expected,
4104  $this->subject->stdWrap_date($content, $conf)
4105  );
4106  }
4107 
4111  #[Test]
4112  public function ‪stdWrap_debug(): void
4113  {
4114  $expect = '<pre>&lt;p class=&quot;class&quot;&gt;&lt;br/&gt;'
4115  . '&lt;/p&gt;</pre>';
4116  $content = '<p class="class"><br/></p>';
4117  self::assertSame($expect, $this->subject->stdWrap_debug($content));
4118  }
4119 
4138  #[Test]
4139  public function ‪stdWrap_debugData(): void
4140  {
4141  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*';
4142  $content = ‪StringUtility::getUniqueId('content');
4143  $key = ‪StringUtility::getUniqueId('key');
4144  $value = ‪StringUtility::getUniqueId('value');
4145  $altValue = ‪StringUtility::getUniqueId('value alt');
4146  $this->subject->data = [$key => $value];
4147  ob_start();
4148  $result = $this->subject->stdWrap_debugData($content);
4149  $out = ob_get_clean();
4150  self::assertSame($result, $content);
4151  self::assertStringContainsString('$cObj->data', $out);
4152  self::assertStringContainsString($value, $out);
4153  self::assertStringNotContainsString($altValue, $out);
4154  }
4155 
4161  public static function ‪stdWrap_debugFuncDataProvider(): array
4162  {
4163  return [
4164  'expect array by string' => [true, '2'],
4165  'expect array by integer' => [true, 2],
4166  'do not expect array' => [false, ''],
4167  ];
4168  }
4169 
4189  #[DataProvider('stdWrap_debugFuncDataProvider')]
4190  #[Test]
4191  public function ‪stdWrap_debugFunc(bool $expectArray, mixed $confDebugFunc): void
4192  {
4193  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*';
4194  $content = ‪StringUtility::getUniqueId('content');
4195  $conf = ['debugFunc' => $confDebugFunc];
4196  ob_start();
4197  $result = $this->subject->stdWrap_debugFunc($content, $conf);
4198  $out = ob_get_clean();
4199  self::assertSame($result, $content);
4200  self::assertStringContainsString($content, $out);
4201  if ($expectArray) {
4202  self::assertStringContainsString('=>', $out);
4203  } else {
4204  self::assertStringNotContainsString('=>', $out);
4205  }
4206  }
4207 
4213  public static function ‪stdWrapDoubleBrTagDataProvider(): array
4214  {
4215  return [
4216  'no config: void input' => [
4217  '',
4218  '',
4219  [],
4220  ],
4221  'no config: single break' => [
4222  'one' . LF . 'two',
4223  'one' . LF . 'two',
4224  [],
4225  ],
4226  'no config: double break' => [
4227  'onetwo',
4228  'one' . LF . LF . 'two',
4229  [],
4230  ],
4231  'no config: double break with whitespace' => [
4232  'onetwo',
4233  'one' . LF . "\t" . ' ' . "\t" . ' ' . LF . 'two',
4234  [],
4235  ],
4236  'no config: single break around' => [
4237  LF . 'one' . LF,
4238  LF . 'one' . LF,
4239  [],
4240  ],
4241  'no config: double break around' => [
4242  'one',
4243  LF . LF . 'one' . LF . LF,
4244  [],
4245  ],
4246  'empty string: double break around' => [
4247  'one',
4248  LF . LF . 'one' . LF . LF,
4249  ['doubleBrTag' => ''],
4250  ],
4251  'br tag: double break' => [
4252  'one<br/>two',
4253  'one' . LF . LF . 'two',
4254  ['doubleBrTag' => '<br/>'],
4255  ],
4256  'br tag: double break around' => [
4257  '<br/>one<br/>',
4258  LF . LF . 'one' . LF . LF,
4259  ['doubleBrTag' => '<br/>'],
4260  ],
4261  'double br tag: double break around' => [
4262  '<br/><br/>one<br/><br/>',
4263  LF . LF . 'one' . LF . LF,
4264  ['doubleBrTag' => '<br/><br/>'],
4265  ],
4266  ];
4267  }
4268 
4276  #[DataProvider('stdWrapDoubleBrTagDataProvider')]
4277  #[Test]
4278  public function ‪stdWrap_doubleBrTag(string $expected, string $input, array $config): void
4279  {
4280  self::assertEquals($expected, $this->subject->stdWrap_doubleBrTag($input, $config));
4281  }
4282 
4293  #[Test]
4294  public function ‪stdWrap_encapsLines(): void
4295  {
4296  $content = ‪StringUtility::getUniqueId('content');
4297  $conf = [
4298  'encapsLines' => [‪StringUtility::getUniqueId('not used')],
4299  'encapsLines.' => [‪StringUtility::getUniqueId('encapsLines.')],
4300  ];
4301  $return = ‪StringUtility::getUniqueId('return');
4302  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
4303  ->onlyMethods(['encaps_lineSplit'])->getMock();
4304  ‪$subject
4305  ->expects(self::once())
4306  ->method('encaps_lineSplit')
4307  ->with($content, $conf['encapsLines.'])
4308  ->willReturn($return);
4309  self::assertSame(
4310  $return,
4311  ‪$subject->‪stdWrap_encapsLines($content, $conf)
4312  );
4313  }
4314 
4320  #[DataProvider('html5SelfClosingTagsDataprovider')]
4321  #[Test]
4322  public function ‪stdWrap_encapsLines_HTML5SelfClosingTags(string $input, string $expected): void
4323  {
4324  $rteParseFunc = ‪self::getLibParseFunc_RTE();
4325 
4326  $conf = [
4327  'encapsLines' => $rteParseFunc['parseFunc.']['nonTypoTagStdWrap.']['encapsLines'] ?? null,
4328  'encapsLines.' => $rteParseFunc['parseFunc.']['nonTypoTagStdWrap.']['encapsLines.'] ?? null,
4329  ];
4330  // don't add an &nbsp; to tag without content
4331  $conf['encapsLines.']['innerStdWrap_all.']['ifBlank'] = '';
4332  $additionalEncapsTags = ['a', 'b', 'span'];
4333 
4334  // We want to allow any tag to be an encapsulating tag
4335  // since this is possible and we don't want an additional tag to be wrapped around.
4336  $conf['encapsLines.']['encapsTagList'] .= ',' . implode(',', $additionalEncapsTags);
4337  $conf['encapsLines.']['encapsTagList'] .= ',' . implode(',', [$input]);
4338 
4339  // Check if we get a self-closing tag for
4340  // empty tags where this is allowed according to HTML5
4341  $content = '<' . $input . ' id="myId" class="bodytext" />';
4342  $result = $this->subject->stdWrap_encapsLines($content, $conf);
4343  self::assertSame($expected, $result);
4344  }
4345 
4346  public static function ‪html5SelfClosingTagsDataprovider(): array
4347  {
4348  return [
4349  'areaTag_selfclosing' => [
4350  'input' => 'area',
4351  'expected' => '<area id="myId" class="bodytext" />',
4352  ],
4353  'base_selfclosing' => [
4354  'input' => 'base',
4355  'expected' => '<base id="myId" class="bodytext" />',
4356  ],
4357  'br_selfclosing' => [
4358  'input' => 'br',
4359  'expected' => '<br id="myId" class="bodytext" />',
4360  ],
4361  'col_selfclosing' => [
4362  'input' => 'col',
4363  'expected' => '<col id="myId" class="bodytext" />',
4364  ],
4365  'embed_selfclosing' => [
4366  'input' => 'embed',
4367  'expected' => '<embed id="myId" class="bodytext" />',
4368  ],
4369  'hr_selfclosing' => [
4370  'input' => 'hr',
4371  'expected' => '<hr id="myId" class="bodytext" />',
4372  ],
4373  'img_selfclosing' => [
4374  'input' => 'img',
4375  'expected' => '<img id="myId" class="bodytext" />',
4376  ],
4377  'input_selfclosing' => [
4378  'input' => 'input',
4379  'expected' => '<input id="myId" class="bodytext" />',
4380  ],
4381  'keygen_selfclosing' => [
4382  'input' => 'keygen',
4383  'expected' => '<keygen id="myId" class="bodytext" />',
4384  ],
4385  'link_selfclosing' => [
4386  'input' => 'link',
4387  'expected' => '<link id="myId" class="bodytext" />',
4388  ],
4389  'meta_selfclosing' => [
4390  'input' => 'meta',
4391  'expected' => '<meta id="myId" class="bodytext" />',
4392  ],
4393  'param_selfclosing' => [
4394  'input' => 'param',
4395  'expected' => '<param id="myId" class="bodytext" />',
4396  ],
4397  'source_selfclosing' => [
4398  'input' => 'source',
4399  'expected' => '<source id="myId" class="bodytext" />',
4400  ],
4401  'track_selfclosing' => [
4402  'input' => 'track',
4403  'expected' => '<track id="myId" class="bodytext" />',
4404  ],
4405  'wbr_selfclosing' => [
4406  'input' => 'wbr',
4407  'expected' => '<wbr id="myId" class="bodytext" />',
4408  ],
4409  'p_notselfclosing' => [
4410  'input' => 'p',
4411  'expected' => '<p id="myId" class="bodytext"></p>',
4412  ],
4413  'a_notselfclosing' => [
4414  'input' => 'a',
4415  'expected' => '<a id="myId" class="bodytext"></a>',
4416  ],
4417  'strong_notselfclosing' => [
4418  'input' => 'strong',
4419  'expected' => '<strong id="myId" class="bodytext"></strong>',
4420  ],
4421  'span_notselfclosing' => [
4422  'input' => 'span',
4423  'expected' => '<span id="myId" class="bodytext"></span>',
4424  ],
4425  ];
4426  }
4427 
4433  public static function ‪stdWrap_encodeForJavaScriptValueDataProvider(): array
4434  {
4435  return [
4436  'double quote in string' => [
4437  '\'double\u0020quote\u0022\'',
4438  'double quote"',
4439  ],
4440  'backslash in string' => [
4441  '\'backslash\u0020\u005C\'',
4442  'backslash \\',
4443  ],
4444  'exclamation mark' => [
4445  '\'exclamation\u0021\'',
4446  'exclamation!',
4447  ],
4448  'whitespace tab, newline and carriage return' => [
4449  '\'white\u0009space\u000As\u000D\'',
4450  "white\tspace\ns\r",
4451  ],
4452  'single quote in string' => [
4453  '\'single\u0020quote\u0020\u0027\'',
4454  'single quote \'',
4455  ],
4456  'tag' => [
4457  '\'\u003Ctag\u003E\'',
4458  '<tag>',
4459  ],
4460  'ampersand in string' => [
4461  '\'amper\u0026sand\'',
4462  'amper&sand',
4463  ],
4464  ];
4465  }
4466 
4473  #[DataProvider('stdWrap_encodeForJavaScriptValueDataProvider')]
4474  #[Test]
4475  public function ‪stdWrap_encodeForJavaScriptValue(string $expect, string $content): void
4476  {
4477  self::assertSame(
4478  $expect,
4479  $this->subject->stdWrap_encodeForJavaScriptValue($content)
4480  );
4481  }
4482 
4488  public static function ‪stdWrap_expandListDataProvider(): array
4489  {
4490  return [
4491  'numbers' => ['1,2,3', '1,2,3'],
4492  'range' => ['3,4,5', '3-5'],
4493  'numbers and range' => ['1,3,4,5,7', '1,3-5,7'],
4494  ];
4495  }
4496 
4508  #[DataProvider('stdWrap_expandListDataProvider')]
4509  #[Test]
4510  public function ‪stdWrap_expandList(string $expected, string $content): void
4511  {
4512  self::assertEquals(
4513  $expected,
4514  $this->subject->stdWrap_expandList($content)
4515  );
4516  }
4517 
4526  #[Test]
4527  public function ‪stdWrap_field(): void
4528  {
4529  $expect = ‪StringUtility::getUniqueId('expect');
4530  $conf = ['field' => ‪StringUtility::getUniqueId('field')];
4531  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
4532  ->onlyMethods(['getFieldVal'])->getMock();
4533  ‪$subject
4534  ->expects(self::once())
4535  ->method('getFieldVal')
4536  ->with($conf['field'])
4537  ->willReturn($expect);
4538  self::assertSame(
4539  $expect,
4540  ‪$subject->‪stdWrap_field('discarded', $conf)
4541  );
4542  }
4543 
4549  public static function ‪stdWrap_fieldRequiredDataProvider(): array
4550  {
4551  $content = ‪StringUtility::getUniqueId('content');
4552  return [
4553  // resulting in boolean false
4554  'false is false' => [
4555  '',
4556  true,
4557  $content,
4558  ['fieldRequired' => 'false'],
4559  ],
4560  'null is false' => [
4561  '',
4562  true,
4563  $content,
4564  ['fieldRequired' => 'null'],
4565  ],
4566  'empty string is false' => [
4567  '',
4568  true,
4569  $content,
4570  ['fieldRequired' => 'empty'],
4571  ],
4572  'whitespace is false' => [
4573  '',
4574  true,
4575  $content,
4576  ['fieldRequired' => 'whitespace'],
4577  ],
4578  'string zero is false' => [
4579  '',
4580  true,
4581  $content,
4582  ['fieldRequired' => 'stringZero'],
4583  ],
4584  'string zero with whitespace is false' => [
4585  '',
4586  true,
4587  $content,
4588  ['fieldRequired' => 'stringZeroWithWhiteSpace'],
4589  ],
4590  'zero is false' => [
4591  '',
4592  true,
4593  $content,
4594  ['fieldRequired' => 'zero'],
4595  ],
4596  // resulting in boolean true
4597  'true is true' => [
4598  $content,
4599  false,
4600  $content,
4601  ['fieldRequired' => 'true'],
4602  ],
4603  'string is true' => [
4604  $content,
4605  false,
4606  $content,
4607  ['fieldRequired' => 'string'],
4608  ],
4609  'one is true' => [
4610  $content,
4611  false,
4612  $content,
4613  ['fieldRequired' => 'one'],
4614  ],
4615  ];
4616  }
4617 
4635  #[DataProvider('stdWrap_fieldRequiredDataProvider')]
4636  #[Test]
4637  public function ‪stdWrap_fieldRequired(string $expect, bool $stop, string $content, array $conf): void
4638  {
4639  $data = [
4640  'null' => null,
4641  'false' => false,
4642  'empty' => '',
4643  'whitespace' => "\t" . ' ',
4644  'stringZero' => '0',
4645  'stringZeroWithWhiteSpace' => "\t" . ' 0 ' . "\t",
4646  'zero' => 0,
4647  'string' => 'string',
4648  'true' => true,
4649  'one' => 1,
4650  ];
4652  ‪$subject->_set('data', $data);
4653  ‪$subject->_set('stdWrapRecursionLevel', 1);
4654  ‪$subject->_set('stopRendering', [1 => false]);
4655  self::assertSame(
4656  $expect,
4657  ‪$subject->‪stdWrap_fieldRequired($content, $conf)
4658  );
4659  self::assertSame($stop, ‪$subject->_get('stopRendering')[1]);
4660  }
4661 
4667  public static function ‪hashDataProvider(): array
4668  {
4669  return [
4670  'md5' => [
4671  'bacb98acf97e0b6112b1d1b650b84971',
4672  'joh316',
4673  ['hash' => 'md5'],
4674  ],
4675  'sha1' => [
4676  '063b3d108bed9f88fa618c6046de0dccadcf3158',
4677  'joh316',
4678  ['hash' => 'sha1'],
4679  ],
4680  'stdWrap capability' => [
4681  'bacb98acf97e0b6112b1d1b650b84971',
4682  'joh316',
4683  [
4684  'hash' => '5',
4685  'hash.' => ['wrap' => 'md|'],
4686  ],
4687  ],
4688  'non-existing hashing algorithm' => [
4689  '',
4690  'joh316',
4691  ['hash' => 'non-existing'],
4692  ],
4693  ];
4694  }
4695 
4709  #[DataProvider('hashDataProvider')]
4710  #[Test]
4711  public function ‪stdWrap_hash(string $expect, string $content, array $conf): void
4712  {
4713  self::assertSame(
4714  $expect,
4715  $this->subject->stdWrap_hash($content, $conf)
4716  );
4717  }
4718 
4724  public static function ‪stdWrap_htmlSpecialCharsDataProvider(): array
4725  {
4726  return [
4727  'void conf' => [
4728  '&lt;span&gt;1 &amp;lt; 2&lt;/span&gt;',
4729  '<span>1 &lt; 2</span>',
4730  [],
4731  ],
4732  'void preserveEntities' => [
4733  '&lt;span&gt;1 &amp;lt; 2&lt;/span&gt;',
4734  '<span>1 &lt; 2</span>',
4735  ['htmlSpecialChars.' => []],
4736  ],
4737  'false preserveEntities' => [
4738  '&lt;span&gt;1 &amp;lt; 2&lt;/span&gt;',
4739  '<span>1 &lt; 2</span>',
4740  ['htmlSpecialChars.' => ['preserveEntities' => 0]],
4741  ],
4742  'true preserveEntities' => [
4743  '&lt;span&gt;1 &lt; 2&lt;/span&gt;',
4744  '<span>1 &lt; 2</span>',
4745  ['htmlSpecialChars.' => ['preserveEntities' => 1]],
4746  ],
4747  ];
4748  }
4749 
4757  #[DataProvider('stdWrap_htmlSpecialCharsDataProvider')]
4758  #[Test]
4759  public function ‪stdWrap_htmlSpecialChars(string $expected, string $input, array $conf): void
4760  {
4761  self::assertSame(
4762  $expected,
4763  $this->subject->stdWrap_htmlSpecialChars($input, $conf)
4764  );
4765  }
4766 
4772  public static function ‪stdWrap_ifDataProvider(): array
4773  {
4774  $content = ‪StringUtility::getUniqueId('content');
4775  $conf = ['if.' => [‪StringUtility::getUniqueId('if.')]];
4776  return [
4777  // evals to true
4778  'empty config' => [
4779  $content,
4780  false,
4781  $content,
4782  [],
4783  0,
4784  false,
4785  ],
4786  'if. is empty array' => [
4787  $content,
4788  false,
4789  $content,
4790  ['if.' => []],
4791  0,
4792  false,
4793  ],
4794  'if. is null' => [
4795  $content,
4796  false,
4797  $content,
4798  ['if.' => null],
4799  0,
4800  false,
4801  ],
4802  'if. is false' => [
4803  $content,
4804  false,
4805  $content,
4806  ['if.' => false],
4807  0,
4808  false,
4809  ],
4810  'if. is 0' => [
4811  $content,
4812  false,
4813  $content,
4814  ['if.' => false],
4815  0,
4816  false,
4817  ],
4818  'if. is "0"' => [
4819  $content,
4820  false,
4821  $content,
4822  ['if.' => '0'],
4823  0,
4824  false,
4825  ],
4826  'checkIf returning true' => [
4827  $content,
4828  false,
4829  $content,
4830  $conf,
4831  1,
4832  true,
4833  ],
4834  // evals to false
4835  'checkIf returning false' => [
4836  '',
4837  true,
4838  $content,
4839  $conf,
4840  1,
4841  false,
4842  ],
4843  ];
4844  }
4845 
4864  #[DataProvider('stdWrap_ifDataProvider')]
4865  #[Test]
4866  public function ‪stdWrap_if(string $expect, bool $stop, string $content, array $conf, int $times, bool $will): void
4867  {
4868  ‪$subject = $this->getAccessibleMock(
4869  ContentObjectRenderer::class,
4870  ['checkIf']
4871  );
4872  ‪$subject->_set('stdWrapRecursionLevel', 1);
4873  ‪$subject->_set('stopRendering', [1 => false]);
4874  ‪$subject
4875  ->expects(self::exactly($times))
4876  ->method('checkIf')
4877  ->with($conf['if.'] ?? null)
4878  ->willReturn($will);
4879  self::assertSame($expect, ‪$subject->‪stdWrap_if($content, $conf));
4880  self::assertSame($stop, ‪$subject->_get('stopRendering')[1]);
4881  }
4882 
4888  public static function ‪checkIfDataProvider(): array
4889  {
4890  return [
4891  'true bitAnd the same' => [true, ['bitAnd' => '4', 'value' => '4']],
4892  'true bitAnd included' => [true, ['bitAnd' => '6', 'value' => '4']],
4893  'false bitAnd' => [false, ['bitAnd' => '4', 'value' => '3']],
4894  'negate true bitAnd the same' => [false, ['bitAnd' => '4', 'value' => '4', 'negate' => '1']],
4895  'negate true bitAnd included' => [false, ['bitAnd' => '6', 'value' => '4', 'negate' => '1']],
4896  'negate false bitAnd' => [true, ['bitAnd' => '3', 'value' => '4', 'negate' => '1']],
4897  'contains matches' => [true, ['contains' => 'long text', 'value' => 'this is a long text']],
4898  'contains does not match' => [false, ['contains' => 'short text', 'value' => 'this is a long text']],
4899  'negate contains does not match' => [false, ['contains' => 'long text', 'value' => 'this is a long text', 'negate' => '1']],
4900  'negate contains does not match but matches' => [true, ['contains' => 'short text', 'value' => 'this is a long text', 'negate' => '1']],
4901  'startsWith matches' => [true, ['startsWith' => 'this is', 'value' => 'this is a long text']],
4902  'startsWith does not match' => [false, ['startsWith' => 'a long text', 'value' => 'this is a long text']],
4903  'negate startsWith does not match' => [false, ['startsWith' => 'this is', 'value' => 'this is a long text', 'negate' => '1']],
4904  'negate startsWith does not match but matches' => [true, ['startsWith' => 'a long text', 'value' => 'this is a long text', 'negate' => '1']],
4905  'endsWith matches' => [true, ['endsWith' => 'a long text', 'value' => 'this is a long text']],
4906  'endsWith does not match' => [false, ['endsWith' => 'this is', 'value' => 'this is a long text']],
4907  'negate endsWith does not match' => [false, ['endsWith' => 'a long text', 'value' => 'this is a long text', 'negate' => '1']],
4908  'negate endsWith does not match but matches' => [true, ['endsWith' => 'this is', 'value' => 'this is a long text', 'negate' => '1']],
4909  ];
4910  }
4911 
4918  #[DataProvider('checkIfDataProvider')]
4919  #[Test]
4920  public function ‪checkIf(bool $expect, array $conf): void
4921  {
4922  ‪$subject = $this->getAccessibleMock(
4923  ContentObjectRenderer::class,
4924  ['stdWrap']
4925  );
4926  self::assertSame($expect, ‪$subject->‪checkIf($conf));
4927  }
4928 
4934  public static function ‪stdWrap_ifBlankDataProvider(): array
4935  {
4936  $alt = ‪StringUtility::getUniqueId('alternative content');
4937  $conf = ['ifBlank' => $alt];
4938  return [
4939  // blank cases
4940  'null is blank' => [$alt, null, $conf],
4941  'false is blank' => [$alt, false, $conf],
4942  'empty string is blank' => [$alt, '', $conf],
4943  'whitespace is blank' => [$alt, "\t" . '', $conf],
4944  // non-blank cases
4945  'string is not blank' => ['string', 'string', $conf],
4946  'zero is not blank' => [0, 0, $conf],
4947  'zero string is not blank' => ['0', '0', $conf],
4948  'zero float is not blank' => [0.0, 0.0, $conf],
4949  'true is not blank' => [true, true, $conf],
4950  ];
4951  }
4952 
4967  #[DataProvider('stdWrap_ifBlankDataProvider')]
4968  #[Test]
4969  public function ‪stdWrap_ifBlank(mixed $expect, mixed $content, array $conf): void
4970  {
4971  $result = $this->subject->stdWrap_ifBlank($content, $conf);
4972  self::assertSame($expect, $result);
4973  }
4974 
4980  public static function ‪stdWrap_ifEmptyDataProvider(): array
4981  {
4982  $alt = ‪StringUtility::getUniqueId('alternative content');
4983  $conf = ['ifEmpty' => $alt];
4984  return [
4985  // empty cases
4986  'null is empty' => [$alt, null, $conf],
4987  'false is empty' => [$alt, false, $conf],
4988  'zero is empty' => [$alt, 0, $conf],
4989  'float zero is empty' => [$alt, 0.0, $conf],
4990  'whitespace is empty' => [$alt, "\t" . ' ', $conf],
4991  'empty string is empty' => [$alt, '', $conf],
4992  'zero string is empty' => [$alt, '0', $conf],
4993  'zero string is empty with whitespace' => [
4994  $alt,
4995  "\t" . ' 0 ' . "\t",
4996  $conf,
4997  ],
4998  // non-empty cases
4999  'string is not empty' => ['string', 'string', $conf],
5000  '1 is not empty' => [1, 1, $conf],
5001  '-1 is not empty' => [-1, -1, $conf],
5002  '0.1 is not empty' => [0.1, 0.1, $conf],
5003  '-0.1 is not empty' => [-0.1, -0.1, $conf],
5004  'true is not empty' => [true, true, $conf],
5005  ];
5006  }
5007 
5021  #[DataProvider('stdWrap_ifEmptyDataProvider')]
5022  #[Test]
5023  public function ‪stdWrap_ifEmpty(mixed $expect, mixed $content, array $conf): void
5024  {
5025  $result = $this->subject->stdWrap_ifEmpty($content, $conf);
5026  self::assertSame($expect, $result);
5027  }
5028 
5034  public static function ‪stdWrap_ifNullDataProvider(): array
5035  {
5036  $alt = ‪StringUtility::getUniqueId('alternative content');
5037  $conf = ['ifNull' => $alt];
5038  return [
5039  'only null is null' => [$alt, null, $conf],
5040  'zero is not null' => [0, 0, $conf],
5041  'float zero is not null' => [0.0, 0.0, $conf],
5042  'false is not null' => [false, false, $conf],
5043  'zero string is not null' => ['0', '0', $conf],
5044  'empty string is not null' => ['', '', $conf],
5045  'whitespace is not null' => ["\t" . '', "\t" . '', $conf],
5046  ];
5047  }
5048 
5062  #[DataProvider('stdWrap_ifNullDataProvider')]
5063  #[Test]
5064  public function ‪stdWrap_ifNull(mixed $expect, mixed $content, array $conf): void
5065  {
5066  $result = $this->subject->stdWrap_ifNull($content, $conf);
5067  self::assertSame($expect, $result);
5068  }
5069 
5075  public static function ‪stdWrap_innerWrapDataProvider(): array
5076  {
5077  return [
5078  'no conf' => [
5079  'XXX',
5080  'XXX',
5081  [],
5082  ],
5083  'simple' => [
5084  '<wrap>XXX</wrap>',
5085  'XXX',
5086  ['innerWrap' => '<wrap>|</wrap>'],
5087  ],
5088  'missing pipe puts wrap before' => [
5089  '<pre>XXX',
5090  'XXX',
5091  ['innerWrap' => '<pre>'],
5092  ],
5093  'trims whitespace' => [
5094  '<wrap>XXX</wrap>',
5095  'XXX',
5096  ['innerWrap' => '<wrap>' . "\t" . ' | ' . "\t" . '</wrap>'],
5097  ],
5098  'split char change is not possible' => [
5099  '<wrap> # </wrap>XXX',
5100  'XXX',
5101  [
5102  'innerWrap' => '<wrap> # </wrap>',
5103  'innerWrap.' => ['splitChar' => '#'],
5104  ],
5105  ],
5106  ];
5107  }
5108 
5116  #[DataProvider('stdWrap_innerWrapDataProvider')]
5117  #[Test]
5118  public function ‪stdWrap_innerWrap(string $expected, string $input, array $conf): void
5119  {
5120  self::assertSame(
5121  $expected,
5122  $this->subject->stdWrap_innerWrap($input, $conf)
5123  );
5124  }
5125 
5131  public static function ‪stdWrap_innerWrap2DataProvider(): array
5132  {
5133  return [
5134  'no conf' => [
5135  'XXX',
5136  'XXX',
5137  [],
5138  ],
5139  'simple' => [
5140  '<wrap>XXX</wrap>',
5141  'XXX',
5142  ['innerWrap2' => '<wrap>|</wrap>'],
5143  ],
5144  'missing pipe puts wrap before' => [
5145  '<pre>XXX',
5146  'XXX',
5147  ['innerWrap2' => '<pre>'],
5148  ],
5149  'trims whitespace' => [
5150  '<wrap>XXX</wrap>',
5151  'XXX',
5152  ['innerWrap2' => '<wrap>' . "\t" . ' | ' . "\t" . '</wrap>'],
5153  ],
5154  'split char change is not possible' => [
5155  '<wrap> # </wrap>XXX',
5156  'XXX',
5157  [
5158  'innerWrap2' => '<wrap> # </wrap>',
5159  'innerWrap2.' => ['splitChar' => '#'],
5160  ],
5161  ],
5162  ];
5163  }
5164 
5172  #[DataProvider('stdWrap_innerWrap2DataProvider')]
5173  #[Test]
5174  public function ‪stdWrap_innerWrap2(string $expected, string $input, array $conf): void
5175  {
5176  self::assertSame(
5177  $expected,
5178  $this->subject->stdWrap_innerWrap2($input, $conf)
5179  );
5180  }
5181 
5191  #[Test]
5192  public function ‪stdWrap_insertData(): void
5193  {
5194  $content = ‪StringUtility::getUniqueId('content');
5195  $return = ‪StringUtility::getUniqueId('return');
5196  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5197  ->onlyMethods(['insertData'])->getMock();
5198  ‪$subject->expects(self::once())->method('insertData')
5199  ->with($content)->willReturn($return);
5200  self::assertSame(
5201  $return,
5203  );
5204  }
5205 
5211  public static function ‪stdWrap_insertDataProvider(): array
5212  {
5213  return [
5214  'empty' => ['', ''],
5215  'notFoundData' => ['any=1', 'any{$string}=1'],
5216  'queryParameter' => ['any{#string}=1', 'any{#string}=1'],
5217  ];
5218  }
5219 
5226  #[DataProvider('stdWrap_insertDataProvider')]
5227  #[Test]
5228  public function ‪stdWrap_insertDataAndInputExamples(mixed $expect, string $content): void
5229  {
5230  self::assertSame($expect, $this->subject->stdWrap_insertData($content));
5231  }
5232 
5238  public static function ‪stdWrap_intvalDataProvider(): array
5239  {
5240  return [
5241  // numbers
5242  'int' => [123, 123],
5243  'float' => [123, 123.45],
5244  'float does not round up' => [123, 123.55],
5245  // negative numbers
5246  'negative int' => [-123, -123],
5247  'negative float' => [-123, -123.45],
5248  'negative float does not round down' => [-123, -123.55],
5249  // strings
5250  'word string' => [0, 'string'],
5251  'empty string' => [0, ''],
5252  'zero string' => [0, '0'],
5253  'int string' => [123, '123'],
5254  'float string' => [123, '123.55'],
5255  'negative float string' => [-123, '-123.55'],
5256  // other types
5257  'null' => [0, null],
5258  'true' => [1, true],
5259  'false' => [0, false],
5260  ];
5261  }
5262 
5280  #[DataProvider('stdWrap_intvalDataProvider')]
5281  #[Test]
5282  public function ‪stdWrap_intval(int $expect, mixed $content): void
5283  {
5284  self::assertSame($expect, $this->subject->stdWrap_intval($content));
5285  }
5286 
5292  public static function ‪stdWrapKeywordsDataProvider(): array
5293  {
5294  return [
5295  'empty string' => ['', ''],
5296  'blank' => ['', ' '],
5297  'tab' => ['', "\t"],
5298  'single semicolon' => [',', ' ; '],
5299  'single comma' => [',', ' , '],
5300  'single nl' => [',', ' ' . PHP_EOL . ' '],
5301  'double semicolon' => [',,', ' ; ; '],
5302  'double comma' => [',,', ' , , '],
5303  'double nl' => [',,', ' ' . PHP_EOL . ' ' . PHP_EOL . ' '],
5304  'simple word' => ['one', ' one '],
5305  'simple word trimmed' => ['one', 'one'],
5306  ', separated' => ['one,two', ' one , two '],
5307  '; separated' => ['one,two', ' one ; two '],
5308  'nl separated' => ['one,two', ' one ' . PHP_EOL . ' two '],
5309  ', typical' => ['one,two,three', 'one, two, three'],
5310  '; typical' => ['one,two,three', ' one; two; three'],
5311  'nl typical' => [
5312  'one,two,three',
5313  'one' . PHP_EOL . 'two' . PHP_EOL . 'three',
5314  ],
5315  ', sourounded' => [',one,two,', ' , one , two , '],
5316  '; sourounded' => [',one,two,', ' ; one ; two ; '],
5317  'nl sourounded' => [
5318  ',one,two,',
5319  ' ' . PHP_EOL . ' one ' . PHP_EOL . ' two ' . PHP_EOL . ' ',
5320  ],
5321  'mixed' => [
5322  'one,two,three,four',
5323  ' one, two; three' . PHP_EOL . 'four',
5324  ],
5325  'keywods with blanks in words' => [
5326  'one plus,two minus',
5327  ' one plus , two minus ',
5328  ],
5329  ];
5330  }
5331 
5338  #[DataProvider('stdWrapKeywordsDataProvider')]
5339  #[Test]
5340  public function ‪stdWrap_keywords(string $expected, string $input): void
5341  {
5342  self::assertSame($expected, $this->subject->stdWrap_keywords($input));
5343  }
5344 
5350  public static function ‪stdWrap_langDataProvider(): array
5351  {
5352  return [
5353  'empty conf' => [
5354  'original',
5355  'original',
5356  [],
5357  'de_DE',
5358  ],
5359  'translation de' => [
5360  'Übersetzung',
5361  'original',
5362  [
5363  'lang.' => [
5364  'de' => 'Übersetzung',
5365  'it' => 'traduzione',
5366  ],
5367  ],
5368  'de_DE',
5369  ],
5370  'translation it' => [
5371  'traduzione',
5372  'original',
5373  [
5374  'lang.' => [
5375  'de' => 'Übersetzung',
5376  'it' => 'traduzione',
5377  ],
5378  ],
5379  'it_IT',
5380  ],
5381  'no translation' => [
5382  'original',
5383  'original',
5384  [
5385  'lang.' => [
5386  'de' => 'Übersetzung',
5387  'it' => 'traduzione',
5388  ],
5389  ],
5390  'en',
5391  ],
5392  'missing label' => [
5393  'original',
5394  'original',
5395  [
5396  'lang.' => [
5397  'de' => 'Übersetzung',
5398  'it' => 'traduzione',
5399  ],
5400  ],
5401  'fr_FR',
5402  ],
5403  ];
5404  }
5405 
5414  #[DataProvider('stdWrap_langDataProvider')]
5415  #[Test]
5416  public function ‪stdWrap_langViaSiteLanguage(string $expected, string $input, array $conf, string $language): void
5417  {
5418  $site = $this->‪createSiteWithLanguage([
5419  'base' => '/',
5420  'languageId' => 2,
5421  'locale' => $language,
5422  ]);
5423  $request = new ‪ServerRequest();
5424  $request = $request->withAttribute('language', $site->getLanguageById(2));
5425  $this->subject->setRequest($request);
5426  self::assertSame(
5427  $expected,
5428  $this->subject->stdWrap_lang($input, $conf)
5429  );
5430  }
5431 
5443  #[Test]
5444  public function ‪stdWrap_listNum(): void
5445  {
5446  $content = ‪StringUtility::getUniqueId('content');
5447  $conf = [
5448  'listNum' => ‪StringUtility::getUniqueId('listNum'),
5449  'listNum.' => [
5450  'splitChar' => ‪StringUtility::getUniqueId('splitChar'),
5451  ],
5452  ];
5453  $return = ‪StringUtility::getUniqueId('return');
5454  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5455  ->onlyMethods(['listNum'])->getMock();
5456  ‪$subject
5457  ->expects(self::once())
5458  ->method('listNum')
5459  ->with(
5460  $content,
5461  $conf['listNum'],
5462  $conf['listNum.']['splitChar']
5463  )
5464  ->willReturn($return);
5465  self::assertSame(
5466  $return,
5467  ‪$subject->‪stdWrap_listNum($content, $conf)
5468  );
5469  }
5470 
5476  public static function ‪stdWrap_noTrimWrapDataProvider(): array
5477  {
5478  return [
5479  'Standard case' => [
5480  ' left middle right ',
5481  'middle',
5482  [
5483  'noTrimWrap' => '| left | right |',
5484  ],
5485  ],
5486  'Tabs as whitespace' => [
5487  "\t" . 'left' . "\t" . 'middle' . "\t" . 'right' . "\t",
5488  'middle',
5489  [
5490  'noTrimWrap' =>
5491  '|' . "\t" . 'left' . "\t" . '|' . "\t" . 'right' . "\t" . '|',
5492  ],
5493  ],
5494  'Split char is 0' => [
5495  ' left middle right ',
5496  'middle',
5497  [
5498  'noTrimWrap' => '0 left 0 right 0',
5499  'noTrimWrap.' => ['splitChar' => '0'],
5500  ],
5501  ],
5502  'Split char is pipe (default)' => [
5503  ' left middle right ',
5504  'middle',
5505  [
5506  'noTrimWrap' => '| left | right |',
5507  'noTrimWrap.' => ['splitChar' => '|'],
5508  ],
5509  ],
5510  'Split char is a' => [
5511  ' left middle right ',
5512  'middle',
5513  [
5514  'noTrimWrap' => 'a left a right a',
5515  'noTrimWrap.' => ['splitChar' => 'a'],
5516  ],
5517  ],
5518  'Split char is a word (ab)' => [
5519  ' left middle right ',
5520  'middle',
5521  [
5522  'noTrimWrap' => 'ab left ab right ab',
5523  'noTrimWrap.' => ['splitChar' => 'ab'],
5524  ],
5525  ],
5526  'Split char accepts stdWrap' => [
5527  ' left middle right ',
5528  'middle',
5529  [
5530  'noTrimWrap' => 'abc left abc right abc',
5531  'noTrimWrap.' => [
5532  'splitChar' => 'b',
5533  'splitChar.' => ['wrap' => 'a|c'],
5534  ],
5535  ],
5536  ],
5537  ];
5538  }
5539 
5547  #[DataProvider('stdWrap_noTrimWrapDataProvider')]
5548  #[Test]
5549  public function ‪stdWrap_noTrimWrap(string $expect, string $content, array $conf): void
5550  {
5551  self::assertSame(
5552  $expect,
5553  $this->subject->stdWrap_noTrimWrap($content, $conf)
5554  );
5555  }
5556 
5566  #[Test]
5567  public function ‪stdWrap_numRows(): void
5568  {
5569  $conf = [
5570  'numRows' => ‪StringUtility::getUniqueId('numRows'),
5571  'numRows.' => [‪StringUtility::getUniqueId('numRows')],
5572  ];
5573  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5574  ->onlyMethods(['numRows'])->getMock();
5575  ‪$subject->expects(self::once())->method('numRows')
5576  ->with($conf['numRows.'])->willReturn('return');
5577  self::assertSame(
5578  'return',
5579  ‪$subject->‪stdWrap_numRows('discard', $conf)
5580  );
5581  }
5582 
5593  #[Test]
5594  public function ‪stdWrap_numberFormat(): void
5595  {
5596  $content = ‪StringUtility::getUniqueId('content');
5597  $conf = [
5598  'numberFormat' => ‪StringUtility::getUniqueId('not used'),
5599  'numberFormat.' => [‪StringUtility::getUniqueId('numberFormat.')],
5600  ];
5601  $return = ‪StringUtility::getUniqueId('return');
5602  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5603  ->onlyMethods(['numberFormat'])->getMock();
5604  ‪$subject
5605  ->expects(self::once())
5606  ->method('numberFormat')
5607  ->with((float)$content, $conf['numberFormat.'])
5608  ->willReturn($return);
5609  self::assertSame(
5610  $return,
5611  ‪$subject->‪stdWrap_numberFormat($content, $conf)
5612  );
5613  }
5614 
5620  public static function ‪stdWrap_outerWrapDataProvider(): array
5621  {
5622  return [
5623  'no conf' => [
5624  'XXX',
5625  'XXX',
5626  [],
5627  ],
5628  'simple' => [
5629  '<wrap>XXX</wrap>',
5630  'XXX',
5631  ['outerWrap' => '<wrap>|</wrap>'],
5632  ],
5633  'missing pipe puts wrap before' => [
5634  '<pre>XXX',
5635  'XXX',
5636  ['outerWrap' => '<pre>'],
5637  ],
5638  'trims whitespace' => [
5639  '<wrap>XXX</wrap>',
5640  'XXX',
5641  ['outerWrap' => '<wrap>' . "\t" . ' | ' . "\t" . '</wrap>'],
5642  ],
5643  'split char change is not possible' => [
5644  '<wrap> # </wrap>XXX',
5645  'XXX',
5646  [
5647  'outerWrap' => '<wrap> # </wrap>',
5648  'outerWrap.' => ['splitChar' => '#'],
5649  ],
5650  ],
5651  ];
5652  }
5653 
5661  #[DataProvider('stdWrap_outerWrapDataProvider')]
5662  #[Test]
5663  public function ‪stdWrap_outerWrap(string $expected, string $input, array $conf): void
5664  {
5665  self::assertSame(
5666  $expected,
5667  $this->subject->stdWrap_outerWrap($input, $conf)
5668  );
5669  }
5670 
5676  public static function ‪stdWrap_overrideDataProvider(): array
5677  {
5678  return [
5679  'standard case' => [
5680  'override',
5681  'content',
5682  ['override' => 'override'],
5683  ],
5684  'empty conf does not override' => [
5685  'content',
5686  'content',
5687  [],
5688  ],
5689  'empty string does not override' => [
5690  'content',
5691  'content',
5692  ['override' => ''],
5693  ],
5694  'whitespace does not override' => [
5695  'content',
5696  'content',
5697  ['override' => ' ' . "\t"],
5698  ],
5699  'zero does not override' => [
5700  'content',
5701  'content',
5702  ['override' => 0],
5703  ],
5704  'false does not override' => [
5705  'content',
5706  'content',
5707  ['override' => false],
5708  ],
5709  'null does not override' => [
5710  'content',
5711  'content',
5712  ['override' => null],
5713  ],
5714  'one does override' => [
5715  1,
5716  'content',
5717  ['override' => 1],
5718  ],
5719  'minus one does override' => [
5720  -1,
5721  'content',
5722  ['override' => -1],
5723  ],
5724  'float does override' => [
5725  -0.1,
5726  'content',
5727  ['override' => -0.1],
5728  ],
5729  'true does override' => [
5730  true,
5731  'content',
5732  ['override' => true],
5733  ],
5734  'the value is not trimmed' => [
5735  "\t" . 'override',
5736  'content',
5737  ['override' => "\t" . 'override'],
5738  ],
5739  ];
5740  }
5741 
5747  #[DataProvider('stdWrap_overrideDataProvider')]
5748  #[Test]
5749  public function ‪stdWrap_override(mixed $expect, string $content, array $conf): void
5750  {
5751  self::assertSame(
5752  $expect,
5753  $this->subject->stdWrap_override($content, $conf)
5754  );
5755  }
5756 
5768  #[Test]
5769  public function ‪stdWrap_parseFunc(): void
5770  {
5771  $content = ‪StringUtility::getUniqueId('content');
5772  $conf = [
5773  'parseFunc' => ‪StringUtility::getUniqueId('parseFunc'),
5774  'parseFunc.' => [‪StringUtility::getUniqueId('parseFunc.')],
5775  ];
5776  $return = ‪StringUtility::getUniqueId('return');
5777  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5778  ->onlyMethods(['parseFunc'])->getMock();
5779  ‪$subject
5780  ->expects(self::once())
5781  ->method('parseFunc')
5782  ->with($content, $conf['parseFunc.'], $conf['parseFunc'])
5783  ->willReturn($return);
5784  self::assertSame(
5785  $return,
5786  ‪$subject->‪stdWrap_parseFunc($content, $conf)
5787  );
5788  }
5789 
5801  #[Test]
5802  public function ‪stdWrap_postCObject(): void
5803  {
5804  $debugKey = '/stdWrap/.postCObject';
5805  $content = ‪StringUtility::getUniqueId('content');
5806  $conf = [
5807  'postCObject' => ‪StringUtility::getUniqueId('postCObject'),
5808  'postCObject.' => [‪StringUtility::getUniqueId('postCObject.')],
5809  ];
5810  $return = ‪StringUtility::getUniqueId('return');
5811  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5812  ->onlyMethods(['cObjGetSingle'])->getMock();
5813  ‪$subject
5814  ->expects(self::once())
5815  ->method('cObjGetSingle')
5816  ->with($conf['postCObject'], $conf['postCObject.'], $debugKey)
5817  ->willReturn($return);
5818  self::assertSame(
5819  $content . $return,
5820  ‪$subject->‪stdWrap_postCObject($content, $conf)
5821  );
5822  }
5823 
5833  #[Test]
5834  public function ‪stdWrap_postUserFunc(): void
5835  {
5836  $content = ‪StringUtility::getUniqueId('content');
5837  $conf = [
5838  'postUserFunc' => ‪StringUtility::getUniqueId('postUserFunc'),
5839  'postUserFunc.' => [‪StringUtility::getUniqueId('postUserFunc.')],
5840  ];
5841  $return = ‪StringUtility::getUniqueId('return');
5842  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5843  ->onlyMethods(['callUserFunction'])->getMock();
5844  ‪$subject
5845  ->expects(self::once())
5846  ->method('callUserFunction')
5847  ->with($conf['postUserFunc'], $conf['postUserFunc.'])
5848  ->willReturn($return);
5849  self::assertSame(
5850  $return,
5851  ‪$subject->‪stdWrap_postUserFunc($content, $conf)
5852  );
5853  }
5854 
5871  #[Test]
5872  public function ‪stdWrap_postUserFuncInt(): void
5873  {
5874  $uniqueHash = ‪StringUtility::getUniqueId('uniqueHash');
5875  $substKey = 'INT_SCRIPT.' . $uniqueHash;
5876  $content = ‪StringUtility::getUniqueId('content');
5877  $conf = [
5878  'postUserFuncInt' => ‪StringUtility::getUniqueId('function'),
5879  'postUserFuncInt.' => [‪StringUtility::getUniqueId('function array')],
5880  ];
5881  $expect = '<!--' . $substKey . '-->';
5882  $frontend = $this->getMockBuilder(TypoScriptFrontendController::class)
5883  ->disableOriginalConstructor()->onlyMethods(['uniqueHash'])
5884  ->getMock();
5885  $frontend->expects(self::once())->method('uniqueHash')
5886  ->with()->willReturn($uniqueHash);
5887  $frontend->config = ['INTincScript' => []];
5888  ‪$subject = $this->getAccessibleMock(
5889  ContentObjectRenderer::class,
5890  null,
5891  [$frontend]
5892  );
5893  self::assertSame(
5894  $expect,
5895  ‪$subject->‪stdWrap_postUserFuncInt($content, $conf)
5896  );
5897  $array = [
5898  'content' => $content,
5899  'postUserFunc' => $conf['postUserFuncInt'],
5900  'conf' => $conf['postUserFuncInt.'],
5901  'type' => 'POSTUSERFUNC',
5902  'cObj' => serialize(‪$subject),
5903  ];
5904  self::assertSame(
5905  $array,
5906  $frontend->config['INTincScript'][$substKey]
5907  );
5908  }
5909 
5921  #[Test]
5922  public function ‪stdWrap_preCObject(): void
5923  {
5924  $debugKey = '/stdWrap/.preCObject';
5925  $content = ‪StringUtility::getUniqueId('content');
5926  $conf = [
5927  'preCObject' => ‪StringUtility::getUniqueId('preCObject'),
5928  'preCObject.' => [‪StringUtility::getUniqueId('preCObject.')],
5929  ];
5930  $return = ‪StringUtility::getUniqueId('return');
5931  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5932  ->onlyMethods(['cObjGetSingle'])->getMock();
5933  ‪$subject
5934  ->expects(self::once())
5935  ->method('cObjGetSingle')
5936  ->with($conf['preCObject'], $conf['preCObject.'], $debugKey)
5937  ->willReturn($return);
5938  self::assertSame(
5939  $return . $content,
5940  ‪$subject->‪stdWrap_preCObject($content, $conf)
5941  );
5942  }
5943 
5955  #[Test]
5956  public function ‪stdWrap_preIfEmptyListNum(): void
5957  {
5958  $content = ‪StringUtility::getUniqueId('content');
5959  $conf = [
5960  'preIfEmptyListNum' => ‪StringUtility::getUniqueId('preIfEmptyListNum'),
5961  'preIfEmptyListNum.' => [
5962  'splitChar' => ‪StringUtility::getUniqueId('splitChar'),
5963  ],
5964  ];
5965  $return = ‪StringUtility::getUniqueId('return');
5966  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5967  ->onlyMethods(['listNum'])->getMock();
5968  ‪$subject
5969  ->expects(self::once())
5970  ->method('listNum')
5971  ->with(
5972  $content,
5973  $conf['preIfEmptyListNum'],
5974  $conf['preIfEmptyListNum.']['splitChar']
5975  )
5976  ->willReturn($return);
5977  self::assertSame(
5978  $return,
5979  ‪$subject->‪stdWrap_preIfEmptyListNum($content, $conf)
5980  );
5981  }
5982 
5988  public static function ‪stdWrap_prefixCommentDataProvider(): array
5989  {
5990  $content = ‪StringUtility::getUniqueId('content');
5991  $will = ‪StringUtility::getUniqueId('will');
5992  $conf = [];
5993  $conf['prefixComment'] = ‪StringUtility::getUniqueId('prefixComment');
5994  $emptyConf1 = [];
5995  $emptyConf2 = [];
5996  $emptyConf2['prefixComment'] = '';
5997  return [
5998  'standard case' => [$will, $content, $conf, false, 1, $will],
5999  'emptyConf1' => [$content, $content, $emptyConf1, false, 0, $will],
6000  'emptyConf2' => [$content, $content, $emptyConf2, false, 0, $will],
6001  'disabled by bool' => [$content, $content, $conf, true, 0, $will],
6002  'disabled by int' => [$content, $content, $conf, 1, 0, $will],
6003  ];
6004  }
6005 
6020  #[DataProvider('stdWrap_prefixCommentDataProvider')]
6021  #[Test]
6022  public function ‪stdWrap_prefixComment(
6023  string $expect,
6024  string $content,
6025  array $conf,
6026  int|bool $disable,
6027  int $times,
6028  string $will
6029  ): void {
6030  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), [], [], []);
6031  $typoScript->setConfigArray([
6032  'disablePrefixComment' => $disable,
6033  ]);
6034  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
6035  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)->onlyMethods(['prefixComment'])->getMock();
6036  ‪$subject->‪setRequest($request);
6037  ‪$subject->expects(self::exactly($times))
6038  ->method('prefixComment')
6039  ->with($conf['prefixComment'] ?? null, [], $content)
6040  ->willReturn($will);
6041  self::assertSame(
6042  $expect,
6043  ‪$subject->‪stdWrap_prefixComment($content, $conf)
6044  );
6045  }
6046 
6058  #[Test]
6059  public function ‪stdWrap_prepend(): void
6060  {
6061  $debugKey = '/stdWrap/.prepend';
6062  $content = ‪StringUtility::getUniqueId('content');
6063  $conf = [
6064  'prepend' => ‪StringUtility::getUniqueId('prepend'),
6065  'prepend.' => [‪StringUtility::getUniqueId('prepend.')],
6066  ];
6067  $return = ‪StringUtility::getUniqueId('return');
6068  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6069  ->onlyMethods(['cObjGetSingle'])->getMock();
6070  ‪$subject
6071  ->expects(self::once())
6072  ->method('cObjGetSingle')
6073  ->with($conf['prepend'], $conf['prepend.'], $debugKey)
6074  ->willReturn($return);
6075  self::assertSame(
6076  $return . $content,
6077  ‪$subject->‪stdWrap_prepend($content, $conf)
6078  );
6079  }
6080 
6086  public static function ‪stdWrap_prioriCalcDataProvider(): array
6087  {
6088  return [
6089  'priority of *' => ['7', '1 + 2 * 3', []],
6090  'priority of parentheses' => ['9', '(1 + 2) * 3', []],
6091  'float' => ['1.5', '3/2', []],
6092  'intval casts to int' => [1, '3/2', ['prioriCalc' => 'intval']],
6093  'intval does not round' => [2, '2.7', ['prioriCalc' => 'intval']],
6094  ];
6095  }
6096 
6115  #[DataProvider('stdWrap_prioriCalcDataProvider')]
6116  #[Test]
6117  public function ‪stdWrap_prioriCalc(mixed $expect, string $content, array $conf): void
6118  {
6119  $result = $this->subject->stdWrap_prioriCalc($content, $conf);
6120  self::assertSame($expect, $result);
6121  }
6122 
6134  #[Test]
6135  public function ‪stdWrap_preUserFunc(): void
6136  {
6137  $content = ‪StringUtility::getUniqueId('content');
6138  $conf = [
6139  'preUserFunc' => ‪StringUtility::getUniqueId('preUserFunc'),
6140  'preUserFunc.' => [‪StringUtility::getUniqueId('preUserFunc.')],
6141  ];
6142  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6143  ->onlyMethods(['callUserFunction'])->getMock();
6144  ‪$subject->expects(self::once())->method('callUserFunction')
6145  ->with($conf['preUserFunc'], $conf['preUserFunc.'], $content)
6146  ->willReturn('return');
6147  self::assertSame(
6148  'return',
6149  ‪$subject->‪stdWrap_preUserFunc($content, $conf)
6150  );
6151  }
6152 
6158  public static function ‪stdWrap_rawUrlEncodeDataProvider(): array
6159  {
6160  return [
6161  'https://typo3.org?id=10' => [
6162  'https%3A%2F%2Ftypo3.org%3Fid%3D10',
6163  'https://typo3.org?id=10',
6164  ],
6165  'https://typo3.org?id=10&foo=bar' => [
6166  'https%3A%2F%2Ftypo3.org%3Fid%3D10%26foo%3Dbar',
6167  'https://typo3.org?id=10&foo=bar',
6168  ],
6169  ];
6170  }
6171 
6178  #[DataProvider('stdWrap_rawUrlEncodeDataProvider')]
6179  #[Test]
6180  public function ‪stdWrap_rawUrlEncode(string $expect, string $content): void
6181  {
6182  self::assertSame(
6183  $expect,
6184  $this->subject->stdWrap_rawUrlEncode($content)
6185  );
6186  }
6187 
6198  #[Test]
6199  public function ‪stdWrap_replacement(): void
6200  {
6201  $content = ‪StringUtility::getUniqueId('content');
6202  $conf = [
6203  'replacement' => ‪StringUtility::getUniqueId('not used'),
6204  'replacement.' => [‪StringUtility::getUniqueId('replacement.')],
6205  ];
6206  $return = ‪StringUtility::getUniqueId('return');
6207  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6208  ->onlyMethods(['replacement'])->getMock();
6209  ‪$subject
6210  ->expects(self::once())
6211  ->method('replacement')
6212  ->with($content, $conf['replacement.'])
6213  ->willReturn($return);
6214  self::assertSame(
6215  $return,
6216  ‪$subject->‪stdWrap_replacement($content, $conf)
6217  );
6218  }
6219 
6225  public static function ‪stdWrap_requiredDataProvider(): array
6226  {
6227  return [
6228  // empty content
6229  'empty string is empty' => ['', true, ''],
6230  'null is empty' => ['', true, null],
6231  'false is empty' => ['', true, false],
6232 
6233  // non-empty content
6234  'blank is not empty' => [' ', false, ' '],
6235  'tab is not empty' => ["\t", false, "\t"],
6236  'linebreak is not empty' => [PHP_EOL, false, PHP_EOL],
6237  '"0" is not empty' => ['0', false, '0'],
6238  '0 is not empty' => [0, false, 0],
6239  '1 is not empty' => [1, false, 1],
6240  'true is not empty' => [true, false, true],
6241  ];
6242  }
6243 
6257  #[DataProvider('stdWrap_requiredDataProvider')]
6258  #[Test]
6259  public function ‪stdWrap_required(mixed $expect, bool $stop, mixed $content): void
6260  {
6262  ‪$subject->_set('stdWrapRecursionLevel', 1);
6263  ‪$subject->_set('stopRendering', [1 => false]);
6264  self::assertSame($expect, ‪$subject->‪stdWrap_required($content));
6265  self::assertSame($stop, ‪$subject->_get('stopRendering')[1]);
6266  }
6267 
6278  #[Test]
6279  public function ‪stdWrap_round(): void
6280  {
6281  $content = ‪StringUtility::getUniqueId('content');
6282  $conf = [
6283  'round' => ‪StringUtility::getUniqueId('not used'),
6284  'round.' => [‪StringUtility::getUniqueId('round.')],
6285  ];
6286  $return = ‪StringUtility::getUniqueId('return');
6287  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6288  ->onlyMethods(['round'])->getMock();
6289  ‪$subject
6290  ->expects(self::once())
6291  ->method('round')
6292  ->with($content, $conf['round.'])
6293  ->willReturn($return);
6294  self::assertSame($return, ‪$subject->‪stdWrap_round($content, $conf));
6295  }
6296 
6300  #[Test]
6301  public function ‪stdWrap_setContentToCurrent(): void
6302  {
6303  $pageInformation = new ‪PageInformation();
6304  $pageInformation->setPageRecord([]);
6305  $request = new ‪ServerRequest('https://example.com');
6306  $request = $request->withAttribute('frontend.page.information', $pageInformation);
6307  $this->subject->setRequest($request);
6308 
6309  $content = ‪StringUtility::getUniqueId('content');
6310  self::assertNotSame($content, $this->subject->getData('current'));
6311  self::assertSame(
6312  $content,
6313  $this->subject->stdWrap_setContentToCurrent($content)
6314  );
6315  self::assertSame($content, $this->subject->getData('current'));
6316  }
6317 
6323  public static function ‪stdWrap_setCurrentDataProvider(): array
6324  {
6325  return [
6326  'no conf' => [
6327  'content',
6328  [],
6329  ],
6330  'empty string' => [
6331  'content',
6332  ['setCurrent' => ''],
6333  ],
6334  'non-empty string' => [
6335  'content',
6336  ['setCurrent' => 'xxx'],
6337  ],
6338  'integer null' => [
6339  'content',
6340  ['setCurrent' => 0],
6341  ],
6342  'integer not null' => [
6343  'content',
6344  ['setCurrent' => 1],
6345  ],
6346  'boolean true' => [
6347  'content',
6348  ['setCurrent' => true],
6349  ],
6350  'boolean false' => [
6351  'content',
6352  ['setCurrent' => false],
6353  ],
6354  ];
6355  }
6356 
6363  #[DataProvider('stdWrap_setCurrentDataProvider')]
6364  #[Test]
6365  public function ‪stdWrap_setCurrent(string $input, array $conf): void
6366  {
6367  $pageInformation = new ‪PageInformation();
6368  $pageInformation->setPageRecord([]);
6369  $request = new ‪ServerRequest('https://example.com');
6370  $request = $request->withAttribute('frontend.page.information', $pageInformation);
6371  $this->subject->setRequest($request);
6372 
6373  if (isset($conf['setCurrent'])) {
6374  self::assertNotSame($conf['setCurrent'], $this->subject->getData('current'));
6375  }
6376  self::assertSame($input, $this->subject->stdWrap_setCurrent($input, $conf));
6377  if (isset($conf['setCurrent'])) {
6378  self::assertSame($conf['setCurrent'], $this->subject->getData('current'));
6379  }
6380  }
6381 
6392  #[Test]
6393  public function ‪stdWrap_split(): void
6394  {
6395  $content = ‪StringUtility::getUniqueId('content');
6396  $conf = [
6397  'split' => ‪StringUtility::getUniqueId('not used'),
6398  'split.' => [‪StringUtility::getUniqueId('split.')],
6399  ];
6400  $return = ‪StringUtility::getUniqueId('return');
6401  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6402  ->onlyMethods(['splitObj'])->getMock();
6403  ‪$subject
6404  ->expects(self::once())
6405  ->method('splitObj')
6406  ->with($content, $conf['split.'])
6407  ->willReturn($return);
6408  self::assertSame(
6409  $return,
6410  ‪$subject->‪stdWrap_split($content, $conf)
6411  );
6412  }
6413 
6423  #[Test]
6424  public function ‪stdWrap_stdWrap(): void
6425  {
6426  $content = ‪StringUtility::getUniqueId('content');
6427  $conf = [
6428  'stdWrap' => ‪StringUtility::getUniqueId('not used'),
6429  'stdWrap.' => [‪StringUtility::getUniqueId('stdWrap.')],
6430  ];
6431  $return = ‪StringUtility::getUniqueId('return');
6432  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6433  ->onlyMethods(['stdWrap'])->getMock();
6434  ‪$subject
6435  ->expects(self::once())
6436  ->method('stdWrap')
6437  ->with($content, $conf['stdWrap.'])
6438  ->willReturn($return);
6439  self::assertSame($return, ‪$subject->‪stdWrap_stdWrap($content, $conf));
6440  }
6441 
6445  public static function ‪stdWrap_stdWrapValueDataProvider(): array
6446  {
6447  return [
6448  'only key returns value' => [
6449  'ifNull',
6450  [
6451  'ifNull' => '1',
6452  ],
6453  '',
6454  '1',
6455  ],
6456  'array without key returns empty string' => [
6457  'ifNull',
6458  [
6459  'ifNull.' => '1',
6460  ],
6461  '',
6462  '',
6463  ],
6464  'array without key returns default' => [
6465  'ifNull',
6466  [
6467  'ifNull.' => '1',
6468  ],
6469  'default',
6470  'default',
6471  ],
6472  'non existing key returns default' => [
6473  'ifNull',
6474  [
6475  'noTrimWrap' => 'test',
6476  'noTrimWrap.' => '1',
6477  ],
6478  'default',
6479  'default',
6480  ],
6481  'default value null is returned' => [
6482  'ifNull',
6483  [],
6484  null,
6485  null,
6486  ],
6487  'existing key and array returns stdWrap' => [
6488  'test',
6489  [
6490  'test' => 'value',
6491  'test.' => ['case' => 'upper'],
6492  ],
6493  'default',
6494  'VALUE',
6495  ],
6496  'the string "0" from stdWrap will be returned' => [
6497  'test',
6498  [
6499  'test' => '',
6500  'test.' => [
6501  'wrap' => '|0',
6502  ],
6503  ],
6504  '100',
6505  '0',
6506  ],
6507  ];
6508  }
6509 
6510  #[DataProvider('stdWrap_stdWrapValueDataProvider')]
6511  #[Test]
6512  public function ‪stdWrap_stdWrapValue(
6513  string $key,
6514  array $configuration,
6515  ?string $defaultValue,
6516  ?string $expected
6517  ): void {
6518  $result = $this->subject->stdWrapValue($key, $configuration, $defaultValue);
6519  self::assertSame($expected, $result);
6520  }
6521 
6527  public static function ‪stdWrap_strPadDataProvider(): array
6528  {
6529  return [
6530  'pad string with default settings and length 10' => [
6531  'Alien ',
6532  'Alien',
6533  [
6534  'length' => '10',
6535  ],
6536  ],
6537  'pad string with default settings and length 10 and multibyte character' => [
6538  'Älien ',
6539  'Älien',
6540  [
6541  'length' => '10',
6542  ],
6543  ],
6544  'pad string with padWith -= and type left and length 10' => [
6545  '-=-=-Alien',
6546  'Alien',
6547  [
6548  'length' => '10',
6549  'padWith' => '-=',
6550  'type' => 'left',
6551  ],
6552  ],
6553  'pad string with padWith äö and type left and length 10 and multibyte characters' => [
6554  'äöäöäÄlien',
6555  'Älien',
6556  [
6557  'length' => '10',
6558  'padWith' => 'äö',
6559  'type' => 'left',
6560  ],
6561  ],
6562  'pad string with padWith _ and type both and length 10' => [
6563  '__Alien___',
6564  'Alien',
6565  [
6566  'length' => '10',
6567  'padWith' => '_',
6568  'type' => 'both',
6569  ],
6570  ],
6571  'pad string with padWith 0 and type both and length 10' => [
6572  '00Alien000',
6573  'Alien',
6574  [
6575  'length' => '10',
6576  'padWith' => '0',
6577  'type' => 'both',
6578  ],
6579  ],
6580  'pad string with padWith ___ and type both and length 6' => [
6581  'Alien_',
6582  'Alien',
6583  [
6584  'length' => '6',
6585  'padWith' => '___',
6586  'type' => 'both',
6587  ],
6588  ],
6589  'pad string with padWith _ and type both and length 12, using stdWrap for length' => [
6590  '___Alien____',
6591  'Alien',
6592  [
6593  'length' => '1',
6594  'length.' => [
6595  'wrap' => '|2',
6596  ],
6597  'padWith' => '_',
6598  'type' => 'both',
6599  ],
6600  ],
6601  'pad string with padWith _ and type both and length 12, using stdWrap for padWidth' => [
6602  '-_=Alien-_=-',
6603  'Alien',
6604  [
6605  'length' => '12',
6606  'padWith' => '_',
6607  'padWith.' => [
6608  'wrap' => '-|=',
6609  ],
6610  'type' => 'both',
6611  ],
6612  ],
6613  'pad string with padWith _ and type both and length 12, using stdWrap for type' => [
6614  '_______Alien',
6615  'Alien',
6616  [
6617  'length' => '12',
6618  'padWith' => '_',
6619  'type' => 'both',
6620  // make type become "left"
6621  'type.' => [
6622  'substring' => '2,1',
6623  'wrap' => 'lef|',
6624  ],
6625  ],
6626  ],
6627  ];
6628  }
6629 
6637  #[DataProvider('stdWrap_strPadDataProvider')]
6638  #[Test]
6639  public function ‪stdWrap_strPad(string $expect, string $content, array $conf): void
6640  {
6641  $conf = ['strPad.' => $conf];
6642  $result = $this->subject->stdWrap_strPad($content, $conf);
6643  self::assertSame($expect, $result);
6644  }
6645 
6651  public static function ‪stdWrap_strftimeDataProvider(): array
6652  {
6653  // Fictive execution time is 2012-09-01 12:00 in UTC/GMT.
6654  $now = 1346500800;
6655  return [
6656  'given timestamp' => [
6657  '01-09-2012',
6658  $now,
6659  ['strftime' => '%d-%m-%Y'],
6660  $now,
6661  ],
6662  'empty string' => [
6663  '01-09-2012',
6664  '',
6665  ['strftime' => '%d-%m-%Y'],
6666  $now,
6667  ],
6668  'testing null' => [
6669  '01-09-2012',
6670  null,
6671  ['strftime' => '%d-%m-%Y'],
6672  $now,
6673  ],
6674  ];
6675  }