‪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;
50 use TYPO3\CMS\Core\Package\PackageManager;
95 use TYPO3\CMS\Frontend\Typolink\LinkResult;
97 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
98 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
99 
100 final class ‪ContentObjectRendererTest extends UnitTestCase
101 {
102  protected bool ‪$resetSingletonInstances = true;
103  protected bool ‪$backupEnvironment = true;
104 
105  private ‪ContentObjectRenderer&MockObject&AccessibleObjectInterface ‪$subject;
106  private ‪TypoScriptFrontendController&MockObject&AccessibleObjectInterface ‪$frontendControllerMock;
108 
112  private array ‪$contentObjectMap = [
113  'TEXT' => TextContentObject::class,
114  'CASE' => CaseContentObject::class,
115  'COBJ_ARRAY' => ContentObjectArrayContentObject::class,
116  'COA' => ContentObjectArrayContentObject::class,
117  'COA_INT' => ContentObjectArrayInternalContentObject::class,
118  'USER' => UserContentObject::class,
119  'USER_INT' => UserInternalContentObject::class,
120  'FILES' => FilesContentObject::class,
121  'IMAGE' => ImageContentObject::class,
122  'IMG_RESOURCE' => ImageResourceContentObject::class,
123  'CONTENT' => ContentContentObject::class,
124  'RECORDS' => RecordsContentObject::class,
125  'HMENU' => HierarchicalMenuContentObject::class,
126  'CASEFUNC' => CaseContentObject::class,
127  'LOAD_REGISTER' => LoadRegisterContentObject::class,
128  'RESTORE_REGISTER' => RestoreRegisterContentObject::class,
129  'FLUIDTEMPLATE' => FluidTemplateContentObject::class,
130  'SVG' => ScalableVectorGraphicsContentObject::class,
131  ];
132 
133  protected function ‪setUp(): void
134  {
135  parent::setUp();
136 
137  ‪$GLOBALS['SIM_ACCESS_TIME'] = 1534278180;
138  $this->frontendControllerMock =
139  $this->getAccessibleMock(
140  TypoScriptFrontendController::class,
141  ['sL'],
142  [],
143  '',
144  false
145  );
146  $this->frontendControllerMock->_set('context', GeneralUtility::makeInstance(Context::class));
147  $this->frontendControllerMock->config = [];
148 
149  $this->cacheManagerMock = $this->getMockBuilder(CacheManager::class)->disableOriginalConstructor()->getMock();
150  GeneralUtility::setSingletonInstance(CacheManager::class, $this->cacheManagerMock);
151 
152  $this->subject = $this->getAccessibleMock(
153  ContentObjectRenderer::class,
154  ['getResourceFactory', 'getEnvironmentVariable'],
155  [$this->frontendControllerMock]
156  );
157 
158  $logger = new NullLogger();
159  $this->subject->setLogger($logger);
160  $request = new ‪ServerRequest();
161  $this->subject->setRequest($request);
162 
163  $contentObjectFactoryMock = $this->createContentObjectFactoryMock();
164  $cObj = ‪$this->subject;
165  foreach ($this->contentObjectMap as $name => $className) {
166  $contentObjectFactoryMock->addGetContentObjectCallback(
167  $name,
168  $className,
169  $request,
170  $cObj,
171  $this->getMockBuilder(DataProcessorRegistry::class)->disableOriginalConstructor()->getMock()
172  );
173  }
174  $container = new Container();
175  $container->set(ContentObjectFactory::class, $contentObjectFactoryMock);
176  $container->set(EventDispatcherInterface::class, new ‪NoopEventDispatcher());
177  GeneralUtility::setContainer($container);
178 
179  $this->subject->start([], 'tt_content');
180  }
181 
183  // Utility functions
185 
191  private function ‪handleCharset(string &‪$subject, string &$expected): void
192  {
193  ‪$subject = mb_convert_encoding(‪$subject, 'utf-8', 'iso-8859-1');
194  $expected = mb_convert_encoding($expected, 'utf-8', 'iso-8859-1');
195  }
196 
197  private static function ‪getLibParseFunc_RTE(): array
198  {
199  return [
200  'parseFunc' => '',
201  'parseFunc.' => [
202  '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',
203  'constants' => '1',
204  'denyTags' => '*',
205  'externalBlocks' => 'article, aside, blockquote, div, dd, dl, footer, header, nav, ol, section, table, ul, pre',
206  'externalBlocks.' => [
207  'article.' => [
208  'callRecursive' => '1',
209  'stripNL' => '1',
210  ],
211  'aside.' => [
212  'callRecursive' => '1',
213  'stripNL' => '1',
214  ],
215  'blockquote.' => [
216  'callRecursive' => '1',
217  'stripNL' => '1',
218  ],
219  'dd.' => [
220  'callRecursive' => '1',
221  'stripNL' => '1',
222  ],
223  'div.' => [
224  'callRecursive' => '1',
225  'stripNL' => '1',
226  ],
227  'dl.' => [
228  'callRecursive' => '1',
229  'stripNL' => '1',
230  ],
231  'footer.' => [
232  'callRecursive' => '1',
233  'stripNL' => '1',
234  ],
235  'header.' => [
236  'callRecursive' => '1',
237  'stripNL' => '1',
238  ],
239  'nav.' => [
240  'callRecursive' => '1',
241  'stripNL' => '1',
242  ],
243  'ol.' => [
244  'callRecursive' => '1',
245  'stripNL' => '1',
246  ],
247  'section.' => [
248  'callRecursive' => '1',
249  'stripNL' => '1',
250  ],
251  'table.' => [
252  'HTMLtableCells' => '1',
253  'HTMLtableCells.' => [
254  'addChr10BetweenParagraphs' => '1',
255  'default.' => [
256  'stdWrap.' => [
257  'parseFunc' => '=< lib.parseFunc_RTE',
258  'parseFunc.' => [
259  'nonTypoTagStdWrap.' => [
260  'encapsLines.' => [
261  'nonWrappedTag' => '',
262  ],
263  ],
264  ],
265  ],
266  ],
267  ],
268  'stdWrap.' => [
269  'HTMLparser' => '1',
270  'HTMLparser.' => [
271  'keepNonMatchedTags' => '1',
272  'tags.' => [
273  'table.' => [
274  'fixAttrib.' => [
275  'class.' => [
276  'always' => '1',
277  'default' => 'contenttable',
278  'list' => 'contenttable',
279  ],
280  ],
281  ],
282  ],
283  ],
284  ],
285  'stripNL' => '1',
286  ],
287  'ul.' => [
288  'callRecursive' => '1',
289  'stripNL' => '1',
290  ],
291  ],
292  'makelinks' => '1',
293  'makelinks.' => [
294  'http.' => [
295  'extTarget.' => [
296  'override' => '_blank',
297  ],
298  'keep' => 'path',
299  ],
300  ],
301  'nonTypoTagStdWrap.' => [
302  'encapsLines.' => [
303  'addAttributes.' => [
304  'P.' => [
305  'class' => 'bodytext',
306  'class.' => [
307  'setOnly' => 'blank',
308  ],
309  ],
310  ],
311  'encapsTagList' => 'p,pre,h1,h2,h3,h4,h5,h6,hr,dt,li',
312  'innerStdWrap_all.' => [
313  'ifBlank' => '&nbsp;',
314  ],
315  'nonWrappedTag' => 'P',
316  'remapTag.' => [
317  'DIV' => 'P',
318  ],
319  ],
320  'HTMLparser' => '1',
321  'HTMLparser.' => [
322  'htmlSpecialChars' => '2',
323  'keepNonMatchedTags' => '1',
324  ],
325  ],
326  'sword' => '<span class="csc-sword">|</span>',
327  'tags.' => [
328  'link' => 'TEXT',
329  'link.' => [
330  'current' => '1',
331  'parseFunc.' => [
332  'constants' => '1',
333  ],
334  'typolink.' => [
335  'directImageLink' => false,
336  'extTarget.' => [
337  'override' => '',
338  ],
339  'parameter.' => [
340  'data' => 'parameters : allParams',
341  ],
342  'target.' => [
343  'override' => '',
344  ],
345  ],
346  ],
347  ],
348  ],
349  ];
350  }
351 
352  private function ‪createSiteWithLanguage(array $languageConfiguration): ‪Site
353  {
354  return new ‪Site('test', 1, [
355  'identifier' => 'test',
356  'rootPageId' => 1,
357  'base' => '/',
358  'languages' => [
359  array_merge(
360  $languageConfiguration,
361  [
362  'base' => '/',
363  ]
364  ),
365  ],
366  ]);
367  }
368 
370  // Tests related to getContentObject
372 
375  #[Test]
377  {
378  $object = $this->subject->getContentObject('FOO');
379  self::assertNull($object);
380  }
381 
383  // Tests concerning crop
385 
389  #[Test]
390  public function ‪cropIsMultibyteSafe(): void
391  {
392  self::assertEquals('бла', $this->subject->crop('бла', '3|...'));
393  }
394 
396  // Tests concerning cropHTML
398 
405  public static function ‪cropHTMLDataProvider(): array
406  {
407  $plainText = 'Kasper Sk' . chr(229) . 'rh' . chr(248)
408  . 'j implemented the original version of the crop function.';
409  $textWithMarkup = '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
410  . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> the '
411  . 'original version of the crop function.';
412  $textWithEntities = 'Kasper Sk&aring;rh&oslash;j implemented the; '
413  . 'original version of the crop function.';
414  $textWithLinebreaks = "Lorem ipsum dolor sit amet,\n"
415  . "consetetur sadipscing elitr,\n"
416  . 'sed diam nonumy eirmod tempor invidunt ut labore e'
417  . 't dolore magna aliquyam';
418  $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;';
419  $textWith1000AmpHtmlEntity = str_repeat('&amp;', 1000);
420  $textWith2000AmpHtmlEntity = str_repeat('&amp;', 2000);
421 
422  return [
423  'plain text; 11|...' => [
424  'Kasper Sk' . chr(229) . 'r...',
425  $plainText,
426  '11|...',
427  ],
428  'plain text; -58|...' => [
429  '...h' . chr(248) . 'j implemented the original version of '
430  . 'the crop function.',
431  $plainText,
432  '-58|...',
433  ],
434  'plain text; 4|...|1' => [
435  'Kasp...',
436  $plainText,
437  '4|...|1',
438  ],
439  'plain text; 20|...|1' => [
440  'Kasper Sk' . chr(229) . 'rh' . chr(248) . 'j...',
441  $plainText,
442  '20|...|1',
443  ],
444  'plain text; -5|...|1' => [
445  '...tion.',
446  $plainText,
447  '-5|...|1',
448  ],
449  'plain text; -49|...|1' => [
450  '...the original version of the crop function.',
451  $plainText,
452  '-49|...|1',
453  ],
454  'text with markup; 11|...' => [
455  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
456  . chr(229) . 'r...</a></strong>',
457  $textWithMarkup,
458  '11|...',
459  ],
460  'text with markup; 13|...' => [
461  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
462  . chr(229) . 'rh' . chr(248) . '...</a></strong>',
463  $textWithMarkup,
464  '13|...',
465  ],
466  'text with markup; 14|...' => [
467  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
468  . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
469  $textWithMarkup,
470  '14|...',
471  ],
472  'text with markup; 15|...' => [
473  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
474  . chr(229) . 'rh' . chr(248) . 'j</a> ...</strong>',
475  $textWithMarkup,
476  '15|...',
477  ],
478  'text with markup; 29|...' => [
479  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
480  . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong> '
481  . 'th...',
482  $textWithMarkup,
483  '29|...',
484  ],
485  'text with markup; -58|...' => [
486  '<strong><a href="mailto:kasper@typo3.org">...h' . chr(248)
487  . 'j</a> implemented</strong> the original version of the crop '
488  . 'function.',
489  $textWithMarkup,
490  '-58|...',
491  ],
492  'text with markup 4|...|1' => [
493  '<strong><a href="mailto:kasper@typo3.org">Kasp...</a>'
494  . '</strong>',
495  $textWithMarkup,
496  '4|...|1',
497  ],
498  'text with markup; 11|...|1' => [
499  '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
500  . '</strong>',
501  $textWithMarkup,
502  '11|...|1',
503  ],
504  'text with markup; 13|...|1' => [
505  '<strong><a href="mailto:kasper@typo3.org">Kasper...</a>'
506  . '</strong>',
507  $textWithMarkup,
508  '13|...|1',
509  ],
510  'text with markup; 14|...|1' => [
511  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
512  . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
513  $textWithMarkup,
514  '14|...|1',
515  ],
516  'text with markup; 15|...|1' => [
517  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
518  . chr(229) . 'rh' . chr(248) . 'j</a>...</strong>',
519  $textWithMarkup,
520  '15|...|1',
521  ],
522  'text with markup; 29|...|1' => [
523  '<strong><a href="mailto:kasper@typo3.org">Kasper Sk'
524  . chr(229) . 'rh' . chr(248) . 'j</a> implemented</strong>...',
525  $textWithMarkup,
526  '29|...|1',
527  ],
528  'text with markup; -66|...|1' => [
529  '<strong><a href="mailto:kasper@typo3.org">...Sk' . chr(229)
530  . 'rh' . chr(248) . 'j</a> implemented</strong> the original v'
531  . 'ersion of the crop function.',
532  $textWithMarkup,
533  '-66|...|1',
534  ],
535  'text with entities 9|...' => [
536  'Kasper Sk...',
537  $textWithEntities,
538  '9|...',
539  ],
540  'text with entities 10|...' => [
541  'Kasper Sk&aring;...',
542  $textWithEntities,
543  '10|...',
544  ],
545  'text with entities 11|...' => [
546  'Kasper Sk&aring;r...',
547  $textWithEntities,
548  '11|...',
549  ],
550  'text with entities 13|...' => [
551  'Kasper Sk&aring;rh&oslash;...',
552  $textWithEntities,
553  '13|...',
554  ],
555  'text with entities 14|...' => [
556  'Kasper Sk&aring;rh&oslash;j...',
557  $textWithEntities,
558  '14|...',
559  ],
560  'text with entities 15|...' => [
561  'Kasper Sk&aring;rh&oslash;j ...',
562  $textWithEntities,
563  '15|...',
564  ],
565  'text with entities 16|...' => [
566  'Kasper Sk&aring;rh&oslash;j i...',
567  $textWithEntities,
568  '16|...',
569  ],
570  'text with entities -57|...' => [
571  '...j implemented the; original version of the crop function.',
572  $textWithEntities,
573  '-57|...',
574  ],
575  'text with entities -58|...' => [
576  '...&oslash;j implemented the; original version of the crop '
577  . 'function.',
578  $textWithEntities,
579  '-58|...',
580  ],
581  'text with entities -59|...' => [
582  '...h&oslash;j implemented the; original version of the crop '
583  . 'function.',
584  $textWithEntities,
585  '-59|...',
586  ],
587  'text with entities 4|...|1' => [
588  'Kasp...',
589  $textWithEntities,
590  '4|...|1',
591  ],
592  'text with entities 9|...|1' => [
593  'Kasper...',
594  $textWithEntities,
595  '9|...|1',
596  ],
597  'text with entities 10|...|1' => [
598  'Kasper...',
599  $textWithEntities,
600  '10|...|1',
601  ],
602  'text with entities 11|...|1' => [
603  'Kasper...',
604  $textWithEntities,
605  '11|...|1',
606  ],
607  'text with entities 13|...|1' => [
608  'Kasper...',
609  $textWithEntities,
610  '13|...|1',
611  ],
612  'text with entities 14|...|1' => [
613  'Kasper Sk&aring;rh&oslash;j...',
614  $textWithEntities,
615  '14|...|1',
616  ],
617  'text with entities 15|...|1' => [
618  'Kasper Sk&aring;rh&oslash;j...',
619  $textWithEntities,
620  '15|...|1',
621  ],
622  'text with entities 16|...|1' => [
623  'Kasper Sk&aring;rh&oslash;j...',
624  $textWithEntities,
625  '16|...|1',
626  ],
627  'text with entities -57|...|1' => [
628  '...implemented the; original version of the crop function.',
629  $textWithEntities,
630  '-57|...|1',
631  ],
632  'text with entities -58|...|1' => [
633  '...implemented the; original version of the crop function.',
634  $textWithEntities,
635  '-58|...|1',
636  ],
637  'text with entities -59|...|1' => [
638  '...implemented the; original version of the crop function.',
639  $textWithEntities,
640  '-59|...|1',
641  ],
642  'text with dash in html-element 28|...|1' => [
643  'Some text with a link to <link email.address@example.org - '
644  . 'mail "Open email window">my...</link>',
645  'Some text with a link to <link email.address@example.org - m'
646  . 'ail "Open email window">my email.address@example.org<'
647  . '/link> and text after it',
648  '28|...|1',
649  ],
650  'html elements with dashes in attributes' => [
651  '<em data-foo="x">foobar</em>foo',
652  '<em data-foo="x">foobar</em>foo',
653  '9',
654  ],
655  'html elements with iframe embedded 24|...|1' => [
656  'Text with iframe <iframe src="//what.ever/"></iframe> and...',
657  'Text with iframe <iframe src="//what.ever/">'
658  . '</iframe> and text after it',
659  '24|...|1',
660  ],
661  'html elements with script tag embedded 24|...|1' => [
662  'Text with script <script>alert(\'foo\');</script> and...',
663  'Text with script <script>alert(\'foo\');</script> '
664  . 'and text after it',
665  '24|...|1',
666  ],
667  'text with linebreaks' => [
668  "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\ns"
669  . 'ed diam nonumy eirmod tempor invidunt ut labore e'
670  . 't dolore magna',
671  $textWithLinebreaks,
672  '121',
673  ],
674  'long text under the crop limit' => [
675  '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' . ' ...',
676  $textWith2000Chars,
677  '962|...',
678  ],
679  'long text above the crop limit' => [
680  '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' . '...',
681  $textWith2000Chars,
682  '1000|...',
683  ],
684  'long text above the crop limit #2' => [
685  '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;' . '...',
686  $textWith2000Chars . $textWith2000Chars,
687  '2000|...',
688  ],
689  // ensure that large number of html entities do not break the the regexp splittin
690  'long text with large number of html entities' => [
691  $textWith1000AmpHtmlEntity . '...',
692  $textWith2000AmpHtmlEntity,
693  '1000|...',
694  ],
695  ];
696  }
697 
710  #[DataProvider('cropHTMLDataProvider')]
711  #[Test]
712  public function ‪cropHTML(string $expect, string $content, string $conf): void
713  {
714  $this->‪handleCharset($content, $expect);
715  self::assertSame(
716  $expect,
717  $this->subject->cropHTML($content, $conf)
718  );
719  }
720 
726  public static function ‪roundDataProvider(): array
727  {
728  return [
729  // floats
730  'down' => [1.0, 1.11, []],
731  'up' => [2.0, 1.51, []],
732  'rounds up from x.50' => [2.0, 1.50, []],
733  'down with decimals' => [0.12, 0.1231, ['decimals' => 2]],
734  'up with decimals' => [0.13, 0.1251, ['decimals' => 2]],
735  'ceil' => [1.0, 0.11, ['roundType' => 'ceil']],
736  'ceil does not accept decimals' => [
737  1.0,
738  0.111,
739  [
740  'roundType' => 'ceil',
741  'decimals' => 2,
742  ],
743  ],
744  'floor' => [2.0, 2.99, ['roundType' => 'floor']],
745  'floor does not accept decimals' => [
746  2.0,
747  2.999,
748  [
749  'roundType' => 'floor',
750  'decimals' => 2,
751  ],
752  ],
753  'round, down' => [1.0, 1.11, ['roundType' => 'round']],
754  'round, up' => [2.0, 1.55, ['roundType' => 'round']],
755  'round does accept decimals' => [
756  5.56,
757  5.5555,
758  [
759  'roundType' => 'round',
760  'decimals' => 2,
761  ],
762  ],
763  // strings
764  'emtpy string' => [0.0, '', []],
765  'word string' => [0.0, 'word', []],
766  'float string' => [1.0, '1.123456789', []],
767  // other types
768  'null' => [0.0, null, []],
769  'false' => [0.0, false, []],
770  'true' => [1.0, true, []],
771  ];
772  }
773 
789  #[DataProvider('roundDataProvider')]
790  #[Test]
791  public function ‪round(float $expect, mixed $content, array $conf): void
792  {
793  self::assertSame(
794  $expect,
795  $this->subject->_call('round', $content, $conf)
796  );
797  }
798 
799  #[Test]
801  {
802  $stdWrapConfiguration = [
803  'noTrimWrap' => '|| 123|',
804  'stdWrap.' => [
805  'wrap' => '<b>|</b>',
806  ],
807  ];
808  self::assertSame(
809  '<b>Test</b> 123',
810  $this->subject->stdWrap('Test', $stdWrapConfiguration)
811  );
812  }
813 
814  #[Test]
815  public function ‪recursiveStdWrapIsOnlyCalledOnce(): void
816  {
817  $stdWrapConfiguration = [
818  'append' => 'TEXT',
819  'append.' => [
820  'data' => 'register:Counter',
821  ],
822  'stdWrap.' => [
823  'append' => 'LOAD_REGISTER',
824  'append.' => [
825  'Counter.' => [
826  'prioriCalc' => 'intval',
827  'cObject' => 'TEXT',
828  'cObject.' => [
829  'data' => 'register:Counter',
830  'wrap' => '|+1',
831  ],
832  ],
833  ],
834  ],
835  ];
836  self::assertSame(
837  'Counter:1',
838  $this->subject->stdWrap('Counter:', $stdWrapConfiguration)
839  );
840  }
841 
847  public static function ‪numberFormatDataProvider(): array
848  {
849  return [
850  'testing decimals' => [
851  '0.80',
852  0.8,
853  ['decimals' => 2],
854  ],
855  'testing decimals with input as string' => [
856  '0.80',
857  '0.8',
858  ['decimals' => 2],
859  ],
860  'testing dec_point' => [
861  '0,8',
862  0.8,
863  ['decimals' => 1, 'dec_point' => ','],
864  ],
865  'testing thousands_sep' => [
866  '1.000',
867  999.99,
868  [
869  'decimals' => 0,
870  'thousands_sep.' => ['char' => 46],
871  ],
872  ],
873  'testing mixture' => [
874  '1.281.731,5',
875  1281731.45,
876  [
877  'decimals' => 1,
878  'dec_point.' => ['char' => 44],
879  'thousands_sep.' => ['char' => 46],
880  ],
881  ],
882  ];
883  }
884 
888  #[DataProvider('numberFormatDataProvider')]
889  #[Test]
890  public function ‪numberFormat(string $expects, mixed $content, array $conf): void
891  {
892  self::assertSame(
893  $expects,
894  $this->subject->numberFormat($content, $conf)
895  );
896  }
897 
903  public static function ‪replacementDataProvider(): array
904  {
905  return [
906  'multiple replacements, including regex' => [
907  'There is an animal, an animal and an animal around the block! Yeah!',
908  'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
909  [
910  '20.' => [
911  'search' => '_',
912  'replace.' => ['char' => '32'],
913  ],
914  '120.' => [
915  'search' => 'in da hood',
916  'replace' => 'around the block',
917  ],
918  '130.' => [
919  'search' => '#a (Cat|Dog|Tiger)#i',
920  'replace' => 'an animal',
921  'useRegExp' => '1',
922  ],
923  ],
924  ],
925  'replacement with optionSplit, normal pattern' => [
926  'There1is2a3cat,3a3dog3and3a3tiger3in3da3hood!3Yeah!',
927  'There_is_a_cat,_a_dog_and_a_tiger_in_da_hood!_Yeah!',
928  [
929  '10.' => [
930  'search' => '_',
931  'replace' => '1 || 2 || 3',
932  'useOptionSplitReplace' => '1',
933  'useRegExp' => '0',
934  ],
935  ],
936  ],
937  'replacement with optionSplit, using regex' => [
938  'There is a tiny cat, a midsized dog and a big tiger in da hood! Yeah!',
939  'There is a cat, a dog and a tiger in da hood! Yeah!',
940  [
941  '10.' => [
942  'search' => '#(a) (Cat|Dog|Tiger)#i',
943  'replace' => '${1} tiny ${2} || ${1} midsized ${2} || ${1} big ${2}',
944  'useOptionSplitReplace' => '1',
945  'useRegExp' => '1',
946  ],
947  ],
948  ],
949  ];
950  }
951 
959  #[DataProvider('replacementDataProvider')]
960  #[Test]
961  public function ‪replacement(string $expects, string $content, array $conf): void
962  {
963  self::assertSame(
964  $expects,
965  $this->subject->_call('replacement', $content, $conf)
966  );
967  }
968 
974  public static function ‪calcAgeDataProvider(): array
975  {
976  return [
977  'minutes' => [
978  '2 min',
979  120,
980  ' min| hrs| days| yrs',
981  ],
982  'hours' => [
983  '2 hrs',
984  7200,
985  ' min| hrs| days| yrs',
986  ],
987  'days' => [
988  '7 days',
989  604800,
990  ' min| hrs| days| yrs',
991  ],
992  'day with provided singular labels' => [
993  '1 day',
994  86400,
995  ' min| hrs| days| yrs| min| hour| day| year',
996  ],
997  'years' => [
998  '45 yrs',
999  1417997800,
1000  ' min| hrs| days| yrs',
1001  ],
1002  'different labels' => [
1003  '2 Minutes',
1004  120,
1005  ' Minutes| Hrs| Days| Yrs',
1006  ],
1007  'negative values' => [
1008  '-7 days',
1009  -604800,
1010  ' min| hrs| days| yrs',
1011  ],
1012  'default label values for wrong label input' => [
1013  '2 min',
1014  121,
1015  '10',
1016  ],
1017  'default singular label values for wrong label input' => [
1018  '1 year',
1019  31536000,
1020  '10',
1021  ],
1022  ];
1023  }
1024 
1028  #[DataProvider('calcAgeDataProvider')]
1029  #[Test]
1030  public function ‪calcAge(string $expect, int $timestamp, string $labels): void
1031  {
1032  self::assertSame(
1033  $expect,
1034  $this->subject->calcAge($timestamp, $labels)
1035  );
1036  }
1037 
1038  public static function ‪stdWrapReturnsExpectationDataProvider(): array
1039  {
1040  return [
1041  'Prevent silent bool conversion' => [
1042  '1+1',
1043  [
1044  'prioriCalc.' => [
1045  'wrap' => '|',
1046  ],
1047  ],
1048  '1+1',
1049  ],
1050  ];
1051  }
1052 
1053  #[DataProvider('stdWrapReturnsExpectationDataProvider')]
1054  #[Test]
1055  public function ‪stdWrapReturnsExpectation(string $content, array $configuration, string $expectation): void
1056  {
1057  self::assertSame($expectation, $this->subject->stdWrap($content, $configuration));
1058  }
1059 
1061  {
1062  return [
1063  'ifEmpty is not called if content is present as an non-empty string' => [
1064  'content' => 'some content',
1065  'ifEmptyShouldBeCalled' => false,
1066  ],
1067  'ifEmpty is not called if content is present as the string "1"' => [
1068  'content' => '1',
1069  'ifEmptyShouldBeCalled' => false,
1070  ],
1071  'ifEmpty is called if content is present as an empty string' => [
1072  'content' => '',
1073  'ifEmptyShouldBeCalled' => true,
1074  ],
1075  'ifEmpty is called if content is present as the string "0"' => [
1076  'content' => '0',
1077  'ifEmptyShouldBeCalled' => true,
1078  ],
1079  ];
1080  }
1081 
1082  #[DataProvider('stdWrapDoesOnlyCallIfEmptyIfTheTrimmedContentIsEmptyOrZeroDataProvider')]
1083  #[Test]
1084  public function ‪stdWrapDoesOnlyCallIfEmptyIfTheTrimmedContentIsEmptyOrZero(string $content, bool $ifEmptyShouldBeCalled): void
1085  {
1086  $conf = [
1087  'ifEmpty.' => [
1088  'cObject' => 'TEXT',
1089  ],
1090  ];
1091 
1092  ‪$subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['stdWrap_ifEmpty']);
1093  $request = new ‪ServerRequest();
1094  ‪$subject->‪setRequest($request);
1095  ‪$subject->expects(self::exactly(($ifEmptyShouldBeCalled ? 1 : 0)))
1096  ->method('stdWrap_ifEmpty');
1097 
1098  ‪$subject->‪stdWrap($content, $conf);
1099  }
1100 
1106  public static function ‪substringDataProvider(): array
1107  {
1108  return [
1109  'sub -1' => ['g', 'substring', '-1'],
1110  'sub -1,0' => ['g', 'substring', '-1,0'],
1111  'sub -1,-1' => ['', 'substring', '-1,-1'],
1112  'sub -1,1' => ['g', 'substring', '-1,1'],
1113  'sub 0' => ['substring', 'substring', '0'],
1114  'sub 0,0' => ['substring', 'substring', '0,0'],
1115  'sub 0,-1' => ['substrin', 'substring', '0,-1'],
1116  'sub 0,1' => ['s', 'substring', '0,1'],
1117  'sub 1' => ['ubstring', 'substring', '1'],
1118  'sub 1,0' => ['ubstring', 'substring', '1,0'],
1119  'sub 1,-1' => ['ubstrin', 'substring', '1,-1'],
1120  'sub 1,1' => ['u', 'substring', '1,1'],
1121  'sub' => ['substring', 'substring', ''],
1122  ];
1123  }
1124 
1132  #[DataProvider('substringDataProvider')]
1133  #[Test]
1134  public function ‪substring(string $expect, string $content, string $conf): void
1135  {
1136  self::assertSame($expect, $this->subject->substring($content, $conf));
1137  }
1138 
1139  public static function ‪getDataWithTypeGpDataProvider(): array
1140  {
1141  return [
1142  'Value in get-data' => ['onlyInGet', 'GetValue'],
1143  'Value in post-data' => ['onlyInPost', 'PostValue'],
1144  'Value in post-data overriding get-data' => ['inGetAndPost', 'ValueInPost'],
1145  ];
1146  }
1147 
1151  #[DataProvider('getDataWithTypeGpDataProvider')]
1152  #[Test]
1153  public function ‪getDataWithTypeGp(string $key, string $expectedValue): void
1154  {
1155  $queryArguments = [
1156  'onlyInGet' => 'GetValue',
1157  'inGetAndPost' => 'ValueInGet',
1158  ];
1159  $postParameters = [
1160  'onlyInPost' => 'PostValue',
1161  'inGetAndPost' => 'ValueInPost',
1162  ];
1163  $request = new ‪ServerRequest('https://example.com');
1164  $request = $request->withQueryParams($queryArguments)->withParsedBody($postParameters);
1165  $pageInformation = new ‪PageInformation();
1166  $pageInformation->setPageRecord([]);
1167  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1168  $this->subject->setRequest($request);
1169  self::assertEquals($expectedValue, $this->subject->getData('gp:' . $key));
1170  }
1171 
1175  #[Test]
1176  public function ‪getDataWithTypeGetenv(): void
1177  {
1178  $envName = ‪StringUtility::getUniqueId('frontendtest');
1179  $value = ‪StringUtility::getUniqueId('someValue');
1180  putenv($envName . '=' . $value);
1181  $pageInformation = new ‪PageInformation();
1182  $pageInformation->setPageRecord([]);
1183  $request = new ‪ServerRequest('https://example.com');
1184  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1185  $this->subject->setRequest($request);
1186  self::assertEquals($value, $this->subject->getData('getenv:' . $envName));
1187  }
1188 
1192  #[Test]
1193  public function ‪getDataWithTypeGetindpenv(): void
1194  {
1195  $pageInformation = new ‪PageInformation();
1196  $pageInformation->setPageRecord([]);
1197  $request = new ‪ServerRequest('https://example.com');
1198  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1199  $this->subject->setRequest($request);
1200  $this->subject->expects(self::once())->method('getEnvironmentVariable')
1201  ->with(self::equalTo('SCRIPT_FILENAME'))->willReturn('dummyPath');
1202  self::assertEquals('dummyPath', $this->subject->getData('getindpenv:SCRIPT_FILENAME'));
1203  }
1204 
1208  #[Test]
1209  public function ‪getDataWithTypeField(): void
1210  {
1211  $key = 'someKey';
1212  $value = 'someValue';
1213  $field = [$key => $value];
1214  self::assertEquals($value, $this->subject->getData('field:' . $key, $field));
1215  }
1216 
1221  #[Test]
1223  {
1224  $key = 'somekey|level1|level2';
1225  $value = 'somevalue';
1226  $field = ['somekey' => ['level1' => ['level2' => 'somevalue']]];
1227 
1228  self::assertEquals($value, $this->subject->getData('field:' . $key, $field));
1229  }
1230 
1232  {
1233  return [
1234  'no whitespace' => [
1235  'typoScriptPath' => 'file:current:uid',
1236  ],
1237  'always whitespace' => [
1238  'typoScriptPath' => 'file : current : uid',
1239  ],
1240  'mixed whitespace' => [
1241  'typoScriptPath' => 'file:current : uid',
1242  ],
1243  ];
1244  }
1245 
1249  #[DataProvider('getDataWithTypeFileReturnsUidOfFileObjectDataProvider')]
1250  #[Test]
1251  public function ‪getDataWithTypeFileReturnsUidOfFileObject(string $typoScriptPath): void
1252  {
1253  $pageInformation = new ‪PageInformation();
1254  $pageInformation->setPageRecord([]);
1255  $request = new ‪ServerRequest('https://example.com');
1256  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1257  $this->subject->setRequest($request);
1259  $file = $this->createMock(File::class);
1260  $file->expects(self::once())->method('getUid')->willReturn(‪$uid);
1261  $this->subject->setCurrentFile($file);
1262  self::assertEquals(‪$uid, $this->subject->getData($typoScriptPath));
1263  }
1264 
1268  #[Test]
1269  public function ‪getDataWithTypeParameters(): void
1270  {
1271  $pageInformation = new ‪PageInformation();
1272  $pageInformation->setPageRecord([]);
1273  $request = new ‪ServerRequest('https://example.com');
1274  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1275  $this->subject->setRequest($request);
1276  $key = ‪StringUtility::getUniqueId('someKey');
1277  $value = ‪StringUtility::getUniqueId('someValue');
1278  $this->subject->parameters[$key] = $value;
1279  self::assertEquals($value, $this->subject->getData('parameters:' . $key));
1280  }
1281 
1285  #[Test]
1286  public function ‪getDataWithTypeRegister(): void
1287  {
1288  $pageInformation = new ‪PageInformation();
1289  $pageInformation->setPageRecord([]);
1290  $request = new ‪ServerRequest('https://example.com');
1291  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1292  $this->subject->setRequest($request);
1293  $key = ‪StringUtility::getUniqueId('someKey');
1294  $value = ‪StringUtility::getUniqueId('someValue');
1296  ‪$GLOBALS['TSFE']->register[$key] = $value;
1297  self::assertEquals($value, $this->subject->getData('register:' . $key));
1298  }
1299 
1303  #[Test]
1304  public function ‪getDataWithTypeSession(): void
1305  {
1306  $frontendUser = $this->getMockBuilder(FrontendUserAuthentication::class)
1307  ->onlyMethods(['getSessionData'])
1308  ->getMock();
1309  $frontendUser->expects(self::once())->method('getSessionData')->with('myext')->willReturn([
1310  'mydata' => [
1311  'someValue' => 42,
1312  ],
1313  ]);
1314  $request = (new ‪ServerRequest())->withAttribute('frontend.user', $frontendUser);
1315  $pageInformation = new ‪PageInformation();
1316  $pageInformation->setPageRecord([]);
1317  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1318  $this->subject->setRequest($request);
1319  self::assertEquals(42, $this->subject->getData('session:myext|mydata|someValue'));
1320  }
1321 
1325  #[Test]
1326  public function ‪getDataWithTypeLevel(): void
1327  {
1328  $pageInformation = new ‪PageInformation();
1329  $pageInformation->setPageRecord([]);
1330  $pageInformation->setLocalRootLine([
1331  0 => ['uid' => 1, 'title' => 'title1'],
1332  1 => ['uid' => 2, 'title' => 'title2'],
1333  2 => ['uid' => 3, 'title' => 'title3'],
1334  ]);
1335  $request = new ‪ServerRequest('https://example.com');
1336  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1337  $this->subject->setRequest($request);
1338  self::assertEquals(2, $this->subject->getData('level'));
1339  }
1340 
1344  #[Test]
1345  public function ‪getDataWithTypeLeveltitle(): void
1346  {
1347  $pageInformation = new ‪PageInformation();
1348  $pageInformation->setPageRecord([]);
1349  $pageInformation->setLocalRootLine([
1350  0 => ['uid' => 1, 'title' => 'title1'],
1351  1 => ['uid' => 2, 'title' => 'title2'],
1352  2 => ['uid' => 3, 'title' => ''],
1353  ]);
1354  $request = new ‪ServerRequest('https://example.com');
1355  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1356  $this->subject->setRequest($request);
1357  self::assertEquals('', $this->subject->getData('leveltitle:-1'));
1358  // since "title3" is not set, it will slide to "title2"
1359  self::assertEquals('title2', $this->subject->getData('leveltitle:-1,slide'));
1360  }
1361 
1365  #[Test]
1366  public function ‪getDataWithTypeLevelmedia(): void
1367  {
1368  $pageInformation = new ‪PageInformation();
1369  $pageInformation->setPageRecord([]);
1370  $pageInformation->setLocalRootLine([
1371  0 => ['uid' => 1, 'title' => 'title1', 'media' => 'media1'],
1372  1 => ['uid' => 2, 'title' => 'title2', 'media' => 'media2'],
1373  2 => ['uid' => 3, 'title' => 'title3', 'media' => ''],
1374  ]);
1375  $request = new ‪ServerRequest('https://example.com');
1376  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1377  $this->subject->setRequest($request);
1378  self::assertEquals('', $this->subject->getData('levelmedia:-1'));
1379  // since "title3" is not set, it will slide to "title2"
1380  self::assertEquals('media2', $this->subject->getData('levelmedia:-1,slide'));
1381  }
1382 
1386  #[Test]
1387  public function ‪getDataWithTypeLeveluid(): void
1388  {
1389  $pageInformation = new ‪PageInformation();
1390  $pageInformation->setPageRecord([]);
1391  $pageInformation->setLocalRootLine([
1392  0 => ['uid' => 1, 'title' => 'title1'],
1393  1 => ['uid' => 2, 'title' => 'title2'],
1394  2 => ['uid' => 3, 'title' => 'title3'],
1395  ]);
1396  $request = new ‪ServerRequest('https://example.com');
1397  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1398  $this->subject->setRequest($request);
1399  self::assertEquals(3, $this->subject->getData('leveluid:-1'));
1400  // every element will have a uid - so adding slide doesn't really make sense, just for completeness
1401  self::assertEquals(3, $this->subject->getData('leveluid:-1,slide'));
1402  }
1403 
1407  #[Test]
1408  public function ‪getDataWithTypeLevelfield(): void
1409  {
1410  $pageInformation = new ‪PageInformation();
1411  $pageInformation->setPageRecord([]);
1412  $pageInformation->setLocalRootLine([
1413  0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1414  1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1415  2 => ['uid' => 3, 'title' => 'title3', 'testfield' => ''],
1416  ]);
1417  $request = new ‪ServerRequest('https://example.com');
1418  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1419  $this->subject->setRequest($request);
1420  self::assertEquals('', $this->subject->getData('levelfield:-1,testfield'));
1421  self::assertEquals('field2', $this->subject->getData('levelfield:-1,testfield,slide'));
1422  }
1423 
1427  #[Test]
1428  public function ‪getDataWithTypeFullrootline(): void
1429  {
1430  $pageInformation = new ‪PageInformation();
1431  $pageInformation->setPageRecord([]);
1432  $pageInformation->setRootLine([
1433  0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1434  1 => ['uid' => 2, 'title' => 'title2', 'testfield' => 'field2'],
1435  2 => ['uid' => 3, 'title' => 'title3', 'testfield' => 'field3'],
1436  ]);
1437  $pageInformation->setLocalRootLine([
1438  0 => ['uid' => 1, 'title' => 'title1', 'testfield' => 'field1'],
1439  ]);
1440  $request = new ‪ServerRequest('https://example.com');
1441  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1442  $this->subject->setRequest($request);
1443  self::assertEquals('field2', $this->subject->getData('fullrootline:-1,testfield'));
1444  }
1445 
1449  #[Test]
1450  public function ‪getDataWithTypeDate(): void
1451  {
1452  $pageInformation = new ‪PageInformation();
1453  $pageInformation->setPageRecord([]);
1454  $request = new ‪ServerRequest('https://example.com');
1455  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1456  $this->subject->setRequest($request);
1457  $format = 'Y-M-D';
1458  $defaultFormat = 'd/m Y';
1459  self::assertEquals(date($format, ‪$GLOBALS['EXEC_TIME']), $this->subject->getData('date:' . $format));
1460  self::assertEquals(date($defaultFormat, ‪$GLOBALS['EXEC_TIME']), $this->subject->getData('date'));
1461  }
1462 
1466  #[Test]
1467  public function ‪getDataWithTypePage(): void
1468  {
1469  $pageInformation = new ‪PageInformation();
1470  ‪$uid = random_int(0, mt_getrandmax());
1471  $pageInformation->setPageRecord(['uid' => ‪$uid]);
1472  $request = new ‪ServerRequest('https://example.com');
1473  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1474  $this->subject->setRequest($request);
1475  self::assertEquals(‪$uid, $this->subject->getData('page:uid'));
1476  }
1477 
1481  #[Test]
1482  public function ‪getDataWithTypeCurrent(): void
1483  {
1484  $pageInformation = new ‪PageInformation();
1485  $pageInformation->setPageRecord([]);
1486  $request = new ‪ServerRequest('https://example.com');
1487  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1488  $this->subject->setRequest($request);
1489  $key = ‪StringUtility::getUniqueId('someKey');
1490  $value = ‪StringUtility::getUniqueId('someValue');
1491  $this->subject->data[$key] = $value;
1492  $this->subject->currentValKey = $key;
1493  self::assertEquals($value, $this->subject->getData('current'));
1494  }
1495 
1496  #[Test]
1498  {
1499  $pageInformation = new ‪PageInformation();
1500  $pageInformation->setPageRecord([]);
1501  $request = new ‪ServerRequest('https://example.com');
1502  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1503  $this->subject->setRequest($request);
1504  $dummyRecord = ['uid' => 5, 'title' => 'someTitle'];
1505  $pageRepository = $this->createMock(PageRepository::class);
1506  GeneralUtility::addInstance(PageRepository::class, $pageRepository);
1507  $pageRepository->expects(self::once())->method('getRawRecord')->with('tt_content', '106')->willReturn($dummyRecord);
1508  self::assertSame('someTitle', $this->subject->getData('db:tt_content:106:title'));
1509  }
1510 
1512  {
1513  return [
1514  'identifier with missing table, uid and column' => [
1515  'identifier' => 'db',
1516  ],
1517  'identifier with empty table, missing uid and column' => [
1518  'identifier' => 'db:',
1519  ],
1520  'identifier with missing table and column' => [
1521  'identifier' => 'db:tt_content',
1522  ],
1523  'identifier with empty table and missing uid and column' => [
1524  'identifier' => 'db:tt_content:',
1525  ],
1526  ];
1527  }
1528 
1529  #[DataProvider('getDataWithTypeDbReturnsEmptyStringOnInvalidIdentifiersDataProvider')]
1530  #[Test]
1532  {
1533  $pageInformation = new ‪PageInformation();
1534  $pageInformation->setPageRecord([]);
1535  $request = new ‪ServerRequest('https://example.com');
1536  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1537  $this->subject->setRequest($request);
1538  self::assertSame('', $this->subject->getData(‪$identifier));
1539  }
1540 
1542  {
1543  return [
1544  'identifier with empty uid and missing column' => [
1545  'identifier' => 'db:tt_content:106',
1546  ],
1547  'identifier with empty uid and column' => [
1548  'identifier' => 'db:tt_content:106:',
1549  ],
1550  'identifier with empty uid and not existing column' => [
1551  'identifier' => 'db:tt_content:106:not_existing_column',
1552  ],
1553  ];
1554  }
1555 
1556  #[DataProvider('getDataWithTypeDbReturnsEmptyStringOnInvalidIdentifiersCallsPageRepositoryOnceDataProvider')]
1557  #[Test]
1559  {
1560  $pageInformation = new ‪PageInformation();
1561  $pageInformation->setPageRecord([]);
1562  $request = new ‪ServerRequest('https://example.com');
1563  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1564  $this->subject->setRequest($request);
1565  $dummyRecord = ['uid' => 5, 'title' => 'someTitle'];
1566  $pageRepository = $this->createMock(PageRepository::class);
1567  GeneralUtility::addInstance(PageRepository::class, $pageRepository);
1568  $pageRepository->expects(self::once())->method('getRawRecord')->with('tt_content', '106')->willReturn($dummyRecord);
1569  self::assertSame('', $this->subject->getData(‪$identifier));
1570  }
1571 
1572  #[Test]
1573  public function ‪getDataWithTypeLll(): void
1574  {
1575  $pageInformation = new ‪PageInformation();
1576  $pageInformation->setPageRecord([]);
1577  $request = new ‪ServerRequest('https://example.com');
1578  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1579  $language = $this->createMock(SiteLanguage::class);
1580  $request = $request->withAttribute('language', $language);
1581  $this->subject->setRequest($request);
1582  $key = ‪StringUtility::getUniqueId('someKey');
1583  $value = ‪StringUtility::getUniqueId('someValue');
1584  $languageServiceFactory = $this->createMock(LanguageServiceFactory::class);
1585  $languageServiceMock = $this->createMock(LanguageService::class);
1586  $languageServiceFactory->expects(self::once())->method('createFromSiteLanguage')->with(self::anything())->willReturn($languageServiceMock);
1587  GeneralUtility::addInstance(LanguageServiceFactory::class, $languageServiceFactory);
1588  $languageServiceMock->expects(self::once())->method('sL')->with('LLL:' . $key)->willReturn($value);
1589  self::assertEquals($value, $this->subject->getData('lll:' . $key));
1590  }
1591 
1595  #[Test]
1596  public function ‪getDataWithTypePath(): void
1597  {
1598  $pageInformation = new ‪PageInformation();
1599  $pageInformation->setPageRecord([]);
1600  $request = new ‪ServerRequest('https://example.com');
1601  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1602  $this->subject->setRequest($request);
1603  $filenameIn = 'typo3/sysext/frontend/Public/Icons/Extension.svg';
1604  self::assertEquals($filenameIn, $this->subject->getData('path:' . $filenameIn));
1605  }
1606 
1610  #[Test]
1611  public function ‪getDataWithTypeContext(): void
1612  {
1613  $pageInformation = new ‪PageInformation();
1614  $pageInformation->setPageRecord([]);
1615  $request = new ‪ServerRequest('https://example.com');
1616  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1617  $this->subject->setRequest($request);
1618  $context = new ‪Context();
1619  $context->setAspect('workspace', new ‪WorkspaceAspect(3));
1620  $context->setAspect('frontend.user', new ‪UserAspect(new ‪FrontendUserAuthentication(), [0, -1]));
1621  GeneralUtility::setSingletonInstance(Context::class, $context);
1622  self::assertEquals(3, $this->subject->getData('context:workspace:id'));
1623  self::assertEquals('0,-1', $this->subject->getData('context:frontend.user:groupIds'));
1624  self::assertFalse($this->subject->getData('context:frontend.user:isLoggedIn'));
1625  self::assertSame('', $this->subject->getData('context:frontend.user:foozball'));
1626  }
1627 
1631  #[Test]
1632  public function ‪getDataWithTypeSite(): void
1633  {
1634  $pageInformation = new ‪PageInformation();
1635  $pageInformation->setPageRecord([]);
1636  $site = new ‪Site('my-site', 123, [
1637  'base' => 'http://example.com',
1638  'custom' => [
1639  'config' => [
1640  'nested' => 'yeah',
1641  ],
1642  ],
1643  ]);
1644  $request = (new ‪ServerRequest('https://example.com'))
1645  ->withAttribute('frontend.page.information', $pageInformation)
1646  ->withAttribute('site', $site);
1647  $this->subject->setRequest($request);
1648  self::assertEquals('http://example.com', $this->subject->getData('site:base'));
1649  self::assertEquals('yeah', $this->subject->getData('site:custom.config.nested'));
1650  }
1651 
1655  #[Test]
1657  {
1658  $pageInformation = new ‪PageInformation();
1659  $pageInformation->setPageRecord([]);
1660 
1661  $packageManager = new PackageManager(new ‪DependencyOrderingService());
1662  GeneralUtility::addInstance(ProviderConfigurationLoader::class, new ‪ProviderConfigurationLoader(
1663  $packageManager,
1664  new ‪NullFrontend('core'),
1665  'ExpressionLanguageProviders'
1666  ));
1667  GeneralUtility::addInstance(DefaultProvider::class, new ‪DefaultProvider(new ‪Typo3Version(), new ‪Context(), new ‪Features()));
1668 
1669  putenv('LOCAL_DEVELOPMENT=1');
1670 
1671  $site = new ‪Site('my-site', 123, [
1672  'base' => 'http://prod.com',
1673  'baseVariants' => [
1674  [
1675  'base' => 'http://staging.com',
1676  'condition' => 'applicationContext == "Production/Staging"',
1677  ],
1678  [
1679  'base' => 'http://dev.com',
1680  'condition' => 'getenv("LOCAL_DEVELOPMENT") == 1',
1681  ],
1682  ],
1683  ]);
1684 
1685  $request = (new ‪ServerRequest('https://example.com'))
1686  ->withAttribute('frontend.page.information', $pageInformation)
1687  ->withAttribute('site', $site);
1688  $this->subject->setRequest($request);
1689 
1690  self::assertEquals('http://dev.com', $this->subject->getData('site:base'));
1691  }
1692 
1696  #[Test]
1697  public function ‪getDataWithTypeSiteLanguage(): void
1698  {
1699  $pageInformation = new ‪PageInformation();
1700  $pageInformation->setPageRecord([]);
1701  $request = new ‪ServerRequest('https://example.com');
1702  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1703  $site = $this->‪createSiteWithLanguage([
1704  'base' => '/',
1705  'languageId' => 1,
1706  'locale' => 'de_DE',
1707  'title' => 'languageTitle',
1708  'navigationTitle' => 'German',
1709  ]);
1710  $language = $site->getLanguageById(1);
1711  $request = $request->withAttribute('language', $language);
1712  $this->subject->setRequest($request);
1713  self::assertEquals('German', $this->subject->getData('siteLanguage:navigationTitle'));
1714  self::assertEquals('de', $this->subject->getData('siteLanguage:twoLetterIsoCode'));
1715  self::assertEquals('de', $this->subject->getData('siteLanguage:locale:languageCode'));
1716  self::assertEquals('de-DE', $this->subject->getData('siteLanguage:hreflang'));
1717  self::assertEquals('de-DE', $this->subject->getData('siteLanguage:locale:full'));
1718  }
1719 
1723  #[Test]
1725  {
1726  $pageInformation = new ‪PageInformation();
1727  $pageInformation->setPageRecord([]);
1728  $request = new ‪ServerRequest('https://example.com');
1729  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1730  $this->subject->setRequest($request);
1731 
1732  $recordNumber = random_int(0, mt_getrandmax());
1733  $this->subject->parentRecordNumber = $recordNumber;
1734  self::assertEquals($recordNumber, $this->subject->getData('cobj:parentRecordNumber'));
1735  }
1736 
1740  #[Test]
1741  public function ‪getDataWithTypeDebugRootline(): void
1742  {
1743  $pageInformation = new ‪PageInformation();
1744  $pageInformation->setPageRecord([]);
1745  $pageInformation->setLocalRootLine([
1746  0 => ['uid' => 1, 'title' => 'title1'],
1747  1 => ['uid' => 2, 'title' => 'title2'],
1748  2 => ['uid' => 3, 'title' => ''],
1749  ]);
1750  $request = new ‪ServerRequest('https://example.com');
1751  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1752  $this->subject->setRequest($request);
1753  $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)';
1755  $result = $this->subject->getData('debug:rootLine');
1756  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1757 
1758  self::assertEquals($expectedResult, $cleanedResult);
1759  }
1760 
1764  #[Test]
1765  public function ‪getDataWithTypeDebugFullRootline(): void
1766  {
1767  $pageInformation = new ‪PageInformation();
1768  $pageInformation->setPageRecord([]);
1769  $rootline = [
1770  0 => ['uid' => 1, 'title' => 'title1'],
1771  1 => ['uid' => 2, 'title' => 'title2'],
1772  2 => ['uid' => 3, 'title' => ''],
1773  ];
1774  $pageInformation->setRootLine($rootline);
1775  $request = new ‪ServerRequest('https://example.com');
1776  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1777  $this->subject->setRequest($request);
1778 
1779  $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)';
1780 
1782  $result = $this->subject->getData('debug:fullRootLine');
1783  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1784 
1785  self::assertEquals($expectedResult, $cleanedResult);
1786  }
1787 
1791  #[Test]
1792  public function ‪getDataWithTypeDebugData(): void
1793  {
1794  $pageInformation = new ‪PageInformation();
1795  $pageInformation->setPageRecord([]);
1796  $request = new ‪ServerRequest('https://example.com');
1797  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1798  $this->subject->setRequest($request);
1799 
1800  $key = ‪StringUtility::getUniqueId('someKey');
1801  $value = ‪StringUtility::getUniqueId('someValue');
1802  $this->subject->data = [$key => $value];
1803 
1804  $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1805 
1807  $result = $this->subject->getData('debug:data');
1808  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1809 
1810  self::assertEquals($expectedResult, $cleanedResult);
1811  }
1812 
1816  #[Test]
1817  public function ‪getDataWithTypeDebugRegister(): void
1818  {
1819  $pageInformation = new ‪PageInformation();
1820  $pageInformation->setPageRecord([]);
1821  $request = new ‪ServerRequest('https://example.com');
1822  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1823  $this->subject->setRequest($request);
1824 
1825  $key = ‪StringUtility::getUniqueId('someKey');
1826  $value = ‪StringUtility::getUniqueId('someValue');
1828  ‪$GLOBALS['TSFE']->register = [$key => $value];
1829 
1830  $expectedResult = 'array(1item)' . $key . '=>"' . $value . '"(' . strlen($value) . 'chars)';
1831 
1833  $result = $this->subject->getData('debug:register');
1834  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1835 
1836  self::assertEquals($expectedResult, $cleanedResult);
1837  }
1838 
1842  #[Test]
1843  public function ‪getDataWithTypeDebugPage(): void
1844  {
1845  $pageInformation = new ‪PageInformation();
1846  ‪$uid = random_int(0, mt_getrandmax());
1847  $pageInformation->setPageRecord(['uid' => ‪$uid]);
1848  $request = new ‪ServerRequest('https://example.com');
1849  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1850  $this->subject->setRequest($request);
1851  $expectedResult = 'array(1item)uid=>' . ‪$uid . '(integer)';
1853  $result = $this->subject->getData('debug:page');
1854  $cleanedResult = str_replace(["\r", "\n", "\t", ' '], '', $result);
1855  self::assertEquals($expectedResult, $cleanedResult);
1856  }
1857 
1861  #[Test]
1862  public function ‪getDataWithApplicationContext(): void
1863  {
1865  new ‪ApplicationContext('Production'),
1866  true,
1867  false,
1872  ‪Environment::getPublicPath() . '/index.php',
1873  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1874  );
1875  $pageInformation = new ‪PageInformation();
1876  $pageInformation->setPageRecord([]);
1877  $request = new ‪ServerRequest('https://example.com');
1878  $request = $request->withAttribute('frontend.page.information', $pageInformation);
1879  $this->subject->setRequest($request);
1880  self::assertSame('Production', $this->subject->getData('applicationContext'));
1881  }
1882 
1883  #[Test]
1885  {
1886  $this->expectException(\LogicException::class);
1887  $this->expectExceptionCode(1414513947);
1888  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1889  $typoScript->setConfigArray([]);
1890  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1891  $this->subject->setRequest($request);
1892  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject, false);
1893  $this->subject->render($contentObjectFixture);
1894  }
1895 
1896  #[Test]
1898  {
1900  new ‪ApplicationContext('Production'),
1901  true,
1902  false,
1907  ‪Environment::getPublicPath() . '/index.php',
1908  ‪Environment::isWindows() ? 'WINDOWS' : 'UNIX'
1909  );
1910  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1911  $typoScript->setConfigArray([]);
1912  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1913  $this->subject->setRequest($request);
1914  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1915  $this->subject->render($contentObjectFixture);
1916  }
1917 
1918  #[Test]
1920  {
1921  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1922  $configuration = [
1923  'exceptionHandler' => '1',
1924  ];
1925  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1926  $typoScript->setConfigArray([]);
1927  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1928  $this->subject->setRequest($request);
1929  $this->subject->render($contentObjectFixture, $configuration);
1930  }
1931 
1932  #[Test]
1934  {
1935  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1936  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1937  $typoScript->setConfigArray([
1938  'contentObjectExceptionHandler' => '1',
1939  ]);
1940  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1941  $this->subject->setRequest($request);
1942  $this->subject->render($contentObjectFixture);
1943  }
1944 
1945  #[Test]
1947  {
1948  $this->expectException(\LogicException::class);
1949  $this->expectExceptionCode(1414513947);
1950  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject, false);
1951  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1952  $typoScript->setConfigArray([
1953  'contentObjectExceptionHandler' => '1',
1954  ]);
1955  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1956  $this->subject->setRequest($request);
1957  $configuration = [
1958  'exceptionHandler' => '0',
1959  ];
1960  $this->subject->render($contentObjectFixture, $configuration);
1961  }
1962 
1963  #[Test]
1965  {
1966  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1967  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1968  $typoScript->setConfigArray([]);
1969  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1970  $this->subject->setRequest($request);
1971  $configuration = [
1972  'exceptionHandler' => '1',
1973  'exceptionHandler.' => [
1974  'errorMessage' => 'New message for testing',
1975  ],
1976  ];
1977  self::assertSame('New message for testing', $this->subject->render($contentObjectFixture, $configuration));
1978  }
1979 
1980  #[Test]
1982  {
1983  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
1984  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
1985  $typoScript->setConfigArray([
1986  'contentObjectExceptionHandler.' => [
1987  'errorMessage' => 'Global message for testing',
1988  ],
1989  ]);
1990  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
1991  $this->subject->setRequest($request);
1992  $configuration = [
1993  'exceptionHandler' => '1',
1994  'exceptionHandler.' => [
1995  'errorMessage' => 'New message for testing',
1996  ],
1997  ];
1998 
1999  self::assertSame('New message for testing', $this->subject->render($contentObjectFixture, $configuration));
2000  }
2001 
2002  #[Test]
2004  {
2005  $this->expectException(\LogicException::class);
2006  $this->expectExceptionCode(1414513947);
2007  $contentObjectFixture = $this->‪createContentObjectThrowingExceptionFixture($this->subject);
2008  $configuration = [
2009  'exceptionHandler' => '1',
2010  'exceptionHandler.' => [
2011  'ignoreCodes.' => ['10.' => '1414513947'],
2012  ],
2013  ];
2014  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
2015  $typoScript->setConfigArray([]);
2016  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
2017  $this->subject->setRequest($request);
2018  $this->subject->render($contentObjectFixture, $configuration);
2019  }
2020 
2021  private function ‪createContentObjectThrowingExceptionFixture(‪ContentObjectRenderer ‪$subject, bool $addProductionExceptionHandlerInstance = true): ‪AbstractContentObject&MockObject
2022  {
2023  $contentObjectFixture = $this->getMockBuilder(AbstractContentObject::class)->getMock();
2024  $contentObjectFixture->expects(self::once())
2025  ->method('render')
2026  ->willReturnCallback(static function (array $conf = []): string {
2027  throw new \LogicException('Exception during rendering', 1414513947);
2028  });
2029  $contentObjectFixture->‪setContentObjectRenderer(‪$subject);
2030  if ($addProductionExceptionHandlerInstance) {
2031  GeneralUtility::addInstance(
2032  ProductionExceptionHandler::class,
2033  new ‪ProductionExceptionHandler(new ‪Context(), new ‪Random(), new NullLogger())
2034  );
2035  }
2036  return $contentObjectFixture;
2037  }
2038 
2039  public static function ‪_parseFuncReturnsCorrectHtmlDataProvider(): array
2040  {
2041  return [
2042  'Text without tag is wrapped with <p> tag' => [
2043  'Text without tag',
2045  '<p class="bodytext">Text without tag</p>',
2046  false,
2047  ],
2048  'Text wrapped with <p> tag remains the same' => [
2049  '<p class="myclass">Text with &lt;p&gt; tag</p>',
2051  '<p class="myclass">Text with &lt;p&gt; tag</p>',
2052  false,
2053  ],
2054  'Text with absolute external link' => [
2055  'Text with <link http://example.com/foo/>external link</link>',
2057  '<p class="bodytext">Text with <a href="http://example.com/foo/">external link</a></p>',
2058  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/foo/'))->withLinkText('external link'),
2059  ],
2060  'Empty lines are not duplicated' => [
2061  LF,
2063  '<p class="bodytext">&nbsp;</p>',
2064  false,
2065  ],
2066  'Multiple empty lines with no text' => [
2067  LF . LF . LF,
2069  '<p class="bodytext">&nbsp;</p>' . LF . '<p class="bodytext">&nbsp;</p>' . LF . '<p class="bodytext">&nbsp;</p>',
2070  false,
2071  ],
2072  'Empty lines are not duplicated at the end of content' => [
2073  'test' . LF . LF,
2075  '<p class="bodytext">test</p>' . LF . '<p class="bodytext">&nbsp;</p>',
2076  false,
2077  ],
2078  'Empty lines are not trimmed' => [
2079  LF . 'test' . LF,
2081  '<p class="bodytext">&nbsp;</p>' . LF . '<p class="bodytext">test</p>' . LF . '<p class="bodytext">&nbsp;</p>',
2082  false,
2083  ],
2084  ];
2085  }
2086 
2087  #[DataProvider('_parseFuncReturnsCorrectHtmlDataProvider')]
2088  #[Test]
2089  public function ‪stdWrap_parseFuncReturnsParsedHtml(string $value, array $configuration, string $expectedResult, ‪LinkResultInterface|false $linkResult): void
2090  {
2091  if ($linkResult !== false) {
2092  $linkFactory = $this->getMockBuilder(LinkFactory::class)->disableOriginalConstructor()->getMock();
2093  $linkFactory->method('create')->willReturn($linkResult);
2094  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
2095  }
2096  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
2097  $typoScript->setConfigArray([]);
2098  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
2099  $this->subject->setRequest($request);
2100  self::assertEquals($expectedResult, $this->subject->stdWrap_parseFunc($value, $configuration));
2101  }
2102 
2110  {
2111  $defaultListItemParseFunc = [
2112  'parseFunc' => '',
2113  'parseFunc.' => [
2114  'tags.' => [
2115  'li' => 'TEXT',
2116  'li.' => [
2117  'wrap' => '<li>LI:|</li>',
2118  'current' => '1',
2119  ],
2120  ],
2121  ],
2122  ];
2123 
2124  return [
2125  'parent & child tags with same beginning are processed' => [
2126  '<div><any data-skip><anyother data-skip>content</anyother></any></div>',
2127  [
2128  'parseFunc' => '',
2129  'parseFunc.' => [
2130  'tags.' => [
2131  'any' => 'TEXT',
2132  'any.' => [
2133  'wrap' => '<any data-processed>|</any>',
2134  'current' => 1,
2135  ],
2136  'anyother' => 'TEXT',
2137  'anyother.' => [
2138  'wrap' => '<anyother data-processed>|</anyother>',
2139  'current' => 1,
2140  ],
2141  ],
2142  'htmlSanitize' => true,
2143  'htmlSanitize.' => [
2144  'build' => TestSanitizerBuilder::class,
2145  ],
2146  ],
2147  ],
2148  '<div><any data-processed><anyother data-processed>content</anyother></any></div>',
2149  ],
2150  'list with empty and filled li' => [
2151  '<ul>
2152  <li></li>
2153  <li>second</li>
2154 </ul>',
2155  $defaultListItemParseFunc,
2156  '<ul>
2157  <li>LI:</li>
2158  <li>LI:second</li>
2159 </ul>',
2160  ],
2161  'list with filled li wrapped by a div containing text' => [
2162  '<div>text<ul><li></li><li>second</li></ul></div>',
2163  $defaultListItemParseFunc,
2164  '<div>text<ul><li>LI:</li><li>LI:second</li></ul></div>',
2165  ],
2166  'link list with empty li modification' => [
2167  '<ul>
2168  <li>
2169  <ul>
2170  <li></li>
2171  </ul>
2172  </li>
2173 </ul>',
2174  $defaultListItemParseFunc,
2175  '<ul>
2176  <li>LI:
2177  <ul>
2178  <li>LI:</li>
2179  </ul>
2180  </li>
2181 </ul>',
2182  ],
2183 
2184  'link list with li modifications' => [
2185  '<ul>
2186  <li>first</li>
2187  <li>second
2188  <ul>
2189  <li>first sub</li>
2190  <li>second sub</li>
2191  </ul>
2192  </li>
2193 </ul>',
2194  $defaultListItemParseFunc,
2195  '<ul>
2196  <li>LI:first</li>
2197  <li>LI:second
2198  <ul>
2199  <li>LI:first sub</li>
2200  <li>LI:second sub</li>
2201  </ul>
2202  </li>
2203 </ul>',
2204  ],
2205  'link list with li modifications and no text' => [
2206  '<ul>
2207  <li>first</li>
2208  <li>
2209  <ul>
2210  <li>first sub</li>
2211  <li>second sub</li>
2212  </ul>
2213  </li>
2214 </ul>',
2215  $defaultListItemParseFunc,
2216  '<ul>
2217  <li>LI:first</li>
2218  <li>LI:
2219  <ul>
2220  <li>LI:first sub</li>
2221  <li>LI:second sub</li>
2222  </ul>
2223  </li>
2224 </ul>',
2225  ],
2226  'link list with li modifications on third level' => [
2227  '<ul>
2228  <li>first</li>
2229  <li>second
2230  <ul>
2231  <li>first sub
2232  <ul>
2233  <li>first sub sub</li>
2234  <li>second sub sub</li>
2235  </ul>
2236  </li>
2237  <li>second sub</li>
2238  </ul>
2239  </li>
2240 </ul>',
2241  $defaultListItemParseFunc,
2242  '<ul>
2243  <li>LI:first</li>
2244  <li>LI:second
2245  <ul>
2246  <li>LI:first sub
2247  <ul>
2248  <li>LI:first sub sub</li>
2249  <li>LI:second sub sub</li>
2250  </ul>
2251  </li>
2252  <li>LI:second sub</li>
2253  </ul>
2254  </li>
2255 </ul>',
2256  ],
2257  'link list with li modifications on third level no text' => [
2258  '<ul>
2259  <li>first</li>
2260  <li>
2261  <ul>
2262  <li>
2263  <ul>
2264  <li>first sub sub</li>
2265  <li>first sub sub</li>
2266  </ul>
2267  </li>
2268  <li>second sub</li>
2269  </ul>
2270  </li>
2271 </ul>',
2272  $defaultListItemParseFunc,
2273  '<ul>
2274  <li>LI:first</li>
2275  <li>LI:
2276  <ul>
2277  <li>LI:
2278  <ul>
2279  <li>LI:first sub sub</li>
2280  <li>LI:first sub sub</li>
2281  </ul>
2282  </li>
2283  <li>LI:second sub</li>
2284  </ul>
2285  </li>
2286 </ul>',
2287  ],
2288  'link list with ul and li modifications' => [
2289  '<ul>
2290  <li>first</li>
2291  <li>second
2292  <ul>
2293  <li>first sub</li>
2294  <li>second sub</li>
2295  </ul>
2296  </li>
2297 </ul>',
2298  [
2299  'parseFunc' => '',
2300  'parseFunc.' => [
2301  'tags.' => [
2302  'ul' => 'TEXT',
2303  'ul.' => [
2304  'wrap' => '<ul><li>intro</li>|<li>outro</li></ul>',
2305  'current' => '1',
2306  ],
2307  'li' => 'TEXT',
2308  'li.' => [
2309  'wrap' => '<li>LI:|</li>',
2310  'current' => '1',
2311  ],
2312  ],
2313  ],
2314  ],
2315  '<ul><li>intro</li>
2316  <li>LI:first</li>
2317  <li>LI:second
2318  <ul><li>intro</li>
2319  <li>LI:first sub</li>
2320  <li>LI:second sub</li>
2321  <li>outro</li></ul>
2322  </li>
2323 <li>outro</li></ul>',
2324  ],
2325 
2326  'link list with li containing p tag and sub list' => [
2327  '<ul>
2328  <li>first</li>
2329  <li>
2330  <ul>
2331  <li>
2332  <span>
2333  <ul>
2334  <li>first sub sub</li>
2335  <li>first sub sub</li>
2336  </ul>
2337  </span>
2338  </li>
2339  <li>second sub</li>
2340  </ul>
2341  </li>
2342 </ul>',
2343  $defaultListItemParseFunc,
2344  '<ul>
2345  <li>LI:first</li>
2346  <li>LI:
2347  <ul>
2348  <li>LI:
2349  <span>
2350  <ul>
2351  <li>LI:first sub sub</li>
2352  <li>LI:first sub sub</li>
2353  </ul>
2354  </span>
2355  </li>
2356  <li>LI:second sub</li>
2357  </ul>
2358  </li>
2359 </ul>',
2360  ],
2361  ];
2362  }
2363 
2364  #[DataProvider('_parseFuncParsesNestedTagsProperlyDataProvider')]
2365  #[Test]
2366  public function ‪parseFuncParsesNestedTagsProperly(string $value, array $configuration, string $expectedResult): void
2367  {
2368  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
2369  $typoScript->setConfigArray([]);
2370  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
2371  $this->subject->setRequest($request);
2372  self::assertEquals($expectedResult, $this->subject->stdWrap_parseFunc($value, $configuration));
2373  }
2374 
2375  public static function ‪httpMakelinksDataProvider(): array
2376  {
2377  return [
2378  'http link' => [
2379  'Some text with a link http://example.com',
2380  [
2381  ],
2382  'Some text with a link <a href="http://example.com">example.com</a>',
2383  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com'))->withLinkText('example.com'),
2384  ],
2385  'http link with path' => [
2386  'Some text with a link http://example.com/path/to/page',
2387  [
2388  ],
2389  'Some text with a link <a href="http://example.com/path/to/page">example.com</a>',
2390  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page'))->withLinkText('example.com'),
2391  ],
2392  'http link with query parameter' => [
2393  'Some text with a link http://example.com?foo=bar',
2394  [
2395  ],
2396  'Some text with a link <a href="http://example.com?foo=bar">example.com</a>',
2397  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com?foo=bar'))->withLinkText('example.com'),
2398  ],
2399  'http link with question mark' => [
2400  'Some text with a link http://example.com?',
2401  [
2402  ],
2403  'Some text with a link <a href="http://example.com">example.com</a>?',
2404  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com'))->withLinkText('example.com'),
2405  ],
2406  'http link with period' => [
2407  'Some text with a link http://example.com.',
2408  [
2409  ],
2410  'Some text with a link <a href="http://example.com">example.com</a>.',
2411  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com'))->withLinkText('example.com'),
2412  ],
2413  'http link with fragment' => [
2414  'Some text with a link http://example.com#',
2415  [
2416  ],
2417  'Some text with a link <a href="http://example.com#">example.com</a>',
2418  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com#'))->withLinkText('example.com'),
2419  ],
2420  'http link with query parameter and fragment' => [
2421  'Some text with a link http://example.com?foo=bar#top',
2422  [
2423  ],
2424  'Some text with a link <a href="http://example.com?foo=bar#top">example.com</a>',
2425  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com?foo=bar#top'))->withLinkText('example.com'),
2426  ],
2427  'http link with query parameter and keep scheme' => [
2428  'Some text with a link http://example.com/path/to/page?foo=bar',
2429  [
2430  'keep' => 'scheme',
2431  ],
2432  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">http://example.com</a>',
2433  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('http://example.com'),
2434  ],
2435  'http link with query parameter and keep path' => [
2436  'Some text with a link http://example.com/path/to/page?foo=bar',
2437  [
2438  'keep' => 'path',
2439  ],
2440  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">example.com/path/to/page</a>',
2441  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('example.com/path/to/page'),
2442  ],
2443  'http link with query parameter and keep path with trailing slash' => [
2444  'Some text with a link http://example.com/path/to/page/?foo=bar',
2445  [
2446  'keep' => 'path',
2447  ],
2448  'Some text with a link <a href="http://example.com/path/to/page/?foo=bar">example.com/path/to/page/</a>',
2449  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page/?foo=bar'))->withLinkText('example.com/path/to/page/'),
2450  ],
2451  'http link with trailing slash and keep path with trailing slash' => [
2452  'Some text with a link http://example.com/',
2453  [
2454  'keep' => 'path',
2455  ],
2456  'Some text with a link <a href="http://example.com/">example.com</a>',
2457  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/'))->withLinkText('example.com'),
2458  ],
2459  'http link with query parameter and keep scheme,path' => [
2460  'Some text with a link http://example.com/path/to/page?foo=bar',
2461  [
2462  'keep' => 'scheme,path',
2463  ],
2464  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">http://example.com/path/to/page</a>',
2465  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('http://example.com/path/to/page'),
2466  ],
2467  'http link with multiple query parameters' => [
2468  'Some text with a link http://example.com?foo=bar&fuz=baz',
2469  [
2470  'keep' => 'scheme,path,query',
2471  ],
2472  'Some text with a link <a href="http://example.com?foo=bar&amp;fuz=baz">http://example.com?foo=bar&fuz=baz</a>',
2473  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com?foo=bar&fuz=baz'))->withLinkText('http://example.com?foo=bar&fuz=baz'),
2474  ],
2475  'http link with query parameter and keep scheme,path,query' => [
2476  'Some text with a link http://example.com/path/to/page?foo=bar',
2477  [
2478  'keep' => 'scheme,path,query',
2479  ],
2480  'Some text with a link <a href="http://example.com/path/to/page?foo=bar">http://example.com/path/to/page?foo=bar</a>',
2481  (new LinkResult(‪LinkService::TYPE_URL, 'http://example.com/path/to/page?foo=bar'))->withLinkText('http://example.com/path/to/page?foo=bar'),
2482  ],
2483  'https link' => [
2484  'Some text with a link https://example.com',
2485  [
2486  ],
2487  'Some text with a link <a href="https://example.com">example.com</a>',
2488  (new LinkResult(‪LinkService::TYPE_URL, 'https://example.com'))->withLinkText('example.com'),
2489  ],
2490  'http link with www' => [
2491  'Some text with a link http://www.example.com',
2492  [
2493  ],
2494  'Some text with a link <a href="http://www.example.com">www.example.com</a>',
2495  (new LinkResult(‪LinkService::TYPE_URL, 'http://www.example.com'))->withLinkText('www.example.com'),
2496  ],
2497  'https link with www' => [
2498  'Some text with a link https://www.example.com',
2499  [
2500  ],
2501  'Some text with a link <a href="https://www.example.com">www.example.com</a>',
2502  (new LinkResult(‪LinkService::TYPE_URL, 'https://www.example.com'))->withLinkText('www.example.com'),
2503  ],
2504  ];
2505  }
2506 
2507  #[DataProvider('httpMakelinksDataProvider')]
2508  #[Test]
2509  public function ‪httpMakelinksReturnsLink(string $data, array $configuration, string $expectedResult, LinkResult $linkResult): void
2510  {
2511  $linkFactory = $this->getMockBuilder(LinkFactory::class)->disableOriginalConstructor()->getMock();
2512  $linkFactory->method('create')->willReturn($linkResult);
2513  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
2514 
2515  self::assertSame($expectedResult, $this->subject->_call('http_makelinks', $data, $configuration));
2516  }
2517 
2518  public static function ‪invalidHttpMakelinksDataProvider(): array
2519  {
2520  return [
2521  'only http protocol' => [
2522  'http://',
2523  [
2524  ],
2525  'http://',
2526  ],
2527  'only https protocol' => [
2528  'https://',
2529  [
2530  ],
2531  'https://',
2532  ],
2533  'ftp link' => [
2534  'ftp://user@password:example.com',
2535  [
2536  ],
2537  'ftp://user@password:example.com',
2538  ],
2539  ];
2540  }
2541 
2542  #[DataProvider('invalidHttpMakelinksDataProvider')]
2543  #[Test]
2544  public function ‪httpMakelinksReturnsNoLink(string $data, array $configuration, string $expectedResult): void
2545  {
2546  self::assertSame($expectedResult, $this->subject->_call('http_makelinks', $data, $configuration));
2547  }
2548 
2549  public static function ‪mailtoMakelinksDataProvider(): array
2550  {
2551  return [
2552  'mailto link' => [
2553  'Some text with an email address mailto:john@example.com',
2554  [
2555  ],
2556  'Some text with an email address <a href="mailto:john@example.com">john@example.com</a>',
2557  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2558  ],
2559  'mailto link with subject parameter' => [
2560  'Some text with an email address mailto:john@example.com?subject=hi',
2561  [
2562  ],
2563  'Some text with an email address <a href="mailto:john@example.com?subject=hi">john@example.com</a>',
2564  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com?subject=hi'))->withLinkText('john@example.com'),
2565  ],
2566  'mailto link with multiple parameters' => [
2567  'Some text with an email address mailto:john@example.com?subject=Greetings&body=Hi+John',
2568  [
2569  ],
2570  'Some text with an email address <a href="mailto:john@example.com?subject=Greetings&amp;body=Hi+John">john@example.com</a>',
2571  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com?subject=Greetings&body=Hi+John'))->withLinkText('john@example.com'),
2572  ],
2573  'mailto link with question mark' => [
2574  'Some text with an email address mailto:john@example.com?',
2575  [
2576  ],
2577  'Some text with an email address <a href="mailto:john@example.com">john@example.com</a>?',
2578  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2579  ],
2580  'mailto link with period' => [
2581  'Some text with an email address mailto:john@example.com.',
2582  [
2583  ],
2584  'Some text with an email address <a href="mailto:john@example.com">john@example.com</a>.',
2585  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2586  ],
2587  'mailto link with wrap' => [
2588  'Some text with an email address mailto:john@example.com.',
2589  [
2590  'wrap' => '<span>|</span>',
2591  ],
2592  'Some text with an email address <span><a href="mailto:john@example.com">john@example.com</a></span>.',
2593  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2594  ],
2595  'mailto link with ATagBeforeWrap' => [
2596  'Some text with an email address mailto:john@example.com.',
2597  [
2598  'wrap' => '<span>|</span>',
2599  'ATagBeforeWrap' => 1,
2600  ],
2601  'Some text with an email address <a href="mailto:john@example.com"><span>john@example.com</span></a>.',
2602  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withLinkText('john@example.com'),
2603  ],
2604  'mailto link with ATagParams' => [
2605  'Some text with an email address mailto:john@example.com.',
2606  [
2607  'ATagParams' => 'class="email"',
2608  ],
2609  'Some text with an email address <a href="mailto:john@example.com" class="email">john@example.com</a>.',
2610  (new LinkResult(‪LinkService::TYPE_EMAIL, 'mailto:john@example.com'))->withAttribute('class', 'email')->withLinkText('john@example.com'),
2611  ],
2612  ];
2613  }
2614 
2615  #[DataProvider('mailtoMakelinksDataProvider')]
2616  #[Test]
2617  public function ‪mailtoMakelinksReturnsMailToLink(string $data, array $configuration, string $expectedResult, LinkResult $linkResult): void
2618  {
2619  $linkFactory = $this->getMockBuilder(LinkFactory::class)->disableOriginalConstructor()->getMock();
2620  $linkFactory->method('create')->willReturn($linkResult);
2621  GeneralUtility::addInstance(LinkFactory::class, $linkFactory);
2622 
2623  self::assertSame($expectedResult, $this->subject->_call('mailto_makelinks', $data, $configuration));
2624  }
2625 
2626  #[Test]
2628  {
2629  self::assertSame('mailto:', $this->subject->_call('mailto_makelinks', 'mailto:', []));
2630  }
2631 
2632  #[Test]
2633  public function ‪stdWrap_splitObjReturnsCount(): void
2634  {
2635  $conf = [
2636  'token' => ',',
2637  'returnCount' => 1,
2638  ];
2639  $expectedResult = 5;
2640  $amountOfEntries = $this->subject->splitObj('1, 2, 3, 4, 5', $conf);
2641  self::assertSame(
2642  $expectedResult,
2643  $amountOfEntries
2644  );
2645  }
2646 
2652  public static function ‪calculateCacheKeyDataProvider(): array
2653  {
2654  $value = ‪StringUtility::getUniqueId('value');
2655  $wrap = [‪StringUtility::getUniqueId('wrap')];
2656  $valueConf = ['key' => $value];
2657  $wrapConf = ['key.' => $wrap];
2658  $conf = array_merge($valueConf, $wrapConf);
2659  $will = ‪StringUtility::getUniqueId('stdWrap');
2660 
2661  return [
2662  'no conf' => [
2663  '',
2664  [],
2665  0,
2666  null,
2667  null,
2668  null,
2669  ],
2670  'value conf only' => [
2671  $value,
2672  $valueConf,
2673  0,
2674  null,
2675  null,
2676  null,
2677  ],
2678  'wrap conf only' => [
2679  $will,
2680  $wrapConf,
2681  1,
2682  '',
2683  $wrap,
2684  $will,
2685  ],
2686  'full conf' => [
2687  $will,
2688  $conf,
2689  1,
2690  $value,
2691  $wrap,
2692  $will,
2693  ],
2694  ];
2695  }
2696 
2710  #[DataProvider('calculateCacheKeyDataProvider')]
2711  #[Test]
2712  public function ‪calculateCacheKey(string $expect, array $conf, int $times, ?string $with, ?array $withWrap, ?string $will): void
2713  {
2714  ‪$subject = $this->getAccessibleMock(ContentObjectRenderer::class, ['stdWrap']);
2715  ‪$subject->expects(self::exactly($times))
2716  ->method('stdWrap')
2717  ->with($with, $withWrap)
2718  ->willReturn($will);
2719 
2720  $result = ‪$subject->_call('calculateCacheKey', $conf);
2721  self::assertSame($expect, $result);
2722  }
2723 
2729  public static function ‪getFromCacheDataProvider(): array
2730  {
2731  $conf = [‪StringUtility::getUniqueId('conf')];
2732  return [
2733  'empty cache key' => [
2734  false,
2735  $conf,
2736  '',
2737  0,
2738  null,
2739  ],
2740  'non-empty cache key' => [
2741  'value',
2742  $conf,
2743  'non-empty-key',
2744  1,
2745  'value',
2746  ],
2747  ];
2748  }
2749 
2764  #[DataProvider('getFromCacheDataProvider')]
2765  #[Test]
2766  public function ‪getFromCache(string|bool $expect, array $conf, string $cacheKey, int $times, ?string $cached): void
2767  {
2768  $tags = [‪StringUtility::getUniqueId('tags')];
2769 
2770  ‪$subject = $this->getAccessibleMock(
2771  ContentObjectRenderer::class,
2772  [
2773  'calculateCacheKey',
2774  'getRequest',
2775  'getTypoScriptFrontendController',
2776  ]
2777  );
2778  ‪$subject
2779  ->expects(self::once())
2780  ->method('calculateCacheKey')
2781  ->with($conf)
2782  ->willReturn($cacheKey);
2783  $request = (new ‪ServerRequest())->withAttribute('frontend.cache.instruction', new ‪CacheInstruction());
2784  ‪$subject
2785  ->expects(self::once())
2786  ->method('getRequest')
2787  ->willReturn($request);
2788  $typoScriptFrontendController = $this->createMock(TypoScriptFrontendController::class);
2789  $typoScriptFrontendController
2790  ->expects(self::exactly($times))
2791  ->method('addCacheTags')
2792  ->with($tags);
2793  ‪$subject
2794  ->expects(self::exactly($times))
2795  ->method('getTypoScriptFrontendController')
2796  ->willReturn($typoScriptFrontendController);
2797  $cacheFrontend = $this->createMock(CacheFrontendInterface::class);
2798  $cacheFrontend
2799  ->expects(self::exactly($times))
2800  ->method('get')
2801  ->with($cacheKey)
2802  ->willReturn(['content' => $cached, 'cacheTags' => $tags]);
2803  $cacheManager = $this->createMock(CacheManager::class);
2804  $cacheManager
2805  ->method('getCache')
2806  ->willReturn($cacheFrontend);
2807  GeneralUtility::setSingletonInstance(
2808  CacheManager::class,
2809  $cacheManager
2810  );
2811  self::assertSame($expect, ‪$subject->_call('getFromCache', $conf));
2812  }
2813 
2819  public static function ‪getFieldValDataProvider(): array
2820  {
2821  return [
2822  'invalid single key' => [null, 'invalid'],
2823  'single key of null' => [null, 'null'],
2824  'single key of empty string' => ['', 'empty'],
2825  'single key of non-empty string' => ['string 1', 'string1'],
2826  'single key of boolean false' => [false, 'false'],
2827  'single key of boolean true' => [true, 'true'],
2828  'single key of integer 0' => [0, 'zero'],
2829  'single key of integer 1' => [1, 'one'],
2830  'single key to be trimmed' => ['string 1', ' string1 '],
2831 
2832  'split nothing' => ['', '//'],
2833  'split one before' => ['string 1', 'string1//'],
2834  'split one after' => ['string 1', '//string1'],
2835  'split two ' => ['string 1', 'string1//string2'],
2836  'split three ' => ['string 1', 'string1//string2//string3'],
2837  'split to be trimmed' => ['string 1', ' string1 // string2 '],
2838  '0 is not empty' => [0, '// zero'],
2839  '1 is not empty' => [1, '// one'],
2840  'true is not empty' => [true, '// true'],
2841  'false is empty' => ['', '// false'],
2842  'null is empty' => ['', '// null'],
2843  'empty string is empty' => ['', '// empty'],
2844  'string is not empty' => ['string 1', '// string1'],
2845  'first non-empty winns' => [0, 'false//empty//null//zero//one'],
2846  'empty string is fallback' => ['', 'false // empty // null'],
2847  ];
2848  }
2849 
2886  #[DataProvider('getFieldValDataProvider')]
2887  #[Test]
2888  public function ‪getFieldVal(mixed $expect, string ‪$fields): void
2889  {
2890  $data = [
2891  'string1' => 'string 1',
2892  'string2' => 'string 2',
2893  'string3' => 'string 3',
2894  'empty' => '',
2895  'null' => null,
2896  'false' => false,
2897  'true' => true,
2898  'zero' => 0,
2899  'one' => 1,
2900  ];
2901  $this->subject->_set('data', $data);
2902  self::assertSame($expect, $this->subject->getFieldVal(‪$fields));
2903  }
2904 
2905  public static function ‪caseshiftDataProvider(): array
2906  {
2907  return [
2908  'lower' => [
2909  'x y', // expected
2910  'X Y', // content
2911  'lower', // case
2912  ],
2913  'upper' => [
2914  'X Y',
2915  'x y',
2916  'upper',
2917  ],
2918  'capitalize' => [
2919  'One Two',
2920  'one two',
2921  'capitalize',
2922  ],
2923  'ucfirst' => [
2924  'One two',
2925  'one two',
2926  'ucfirst',
2927  ],
2928  'lcfirst' => [
2929  'oNE TWO',
2930  'ONE TWO',
2931  'lcfirst',
2932  ],
2933  'uppercamelcase' => [
2934  'CamelCase',
2935  'camel_case',
2936  'uppercamelcase',
2937  ],
2938  'lowercamelcase' => [
2939  'camelCase',
2940  'camel_case',
2941  'lowercamelcase',
2942  ],
2943  ];
2944  }
2945 
2946  #[DataProvider('caseshiftDataProvider')]
2947  #[Test]
2948  public function ‪caseshift(string $expected, string $content, string $case): void
2949  {
2950  self::assertSame($expected, $this->subject->caseshift($content, $case));
2951  }
2952 
2953  public static function ‪HTMLcaseshiftDataProvider(): array
2954  {
2955  return [
2956  'simple text' => [
2957  'text',
2958  'TEXT',
2959  ],
2960  'simple tag' => [
2961  '<i>text</i>',
2962  '<i>TEXT</i>',
2963  ],
2964  'multiple nested tags with classes' => [
2965  '<div class="typo3">'
2966  . '<p>A <b>bold<\b> word.</p>'
2967  . '<p>An <i>italic<\i> word.</p>'
2968  . '</div>',
2969  '<div class="typo3">'
2970  . '<p>A <b>BOLD<\b> WORD.</p>'
2971  . '<p>AN <i>ITALIC<\i> WORD.</p>'
2972  . '</div>',
2973  ],
2974  ];
2975  }
2976 
2977  #[DataProvider('HTMLcaseshiftDataProvider')]
2978  #[Test]
2979  public function ‪HTMLcaseshift(string $content, string $expected): void
2980  {
2981  self::assertSame($expected, (new ‪ContentObjectRenderer())->‪HTMLcaseshift($content, 'upper'));
2982  }
2983 
2989  public static function ‪stdWrap_HTMLparserDataProvider(): array
2990  {
2991  $content = ‪StringUtility::getUniqueId('content');
2992  $parsed = ‪StringUtility::getUniqueId('parsed');
2993  return [
2994  'no config' => [
2995  $content,
2996  $content,
2997  [],
2998  0,
2999  $parsed,
3000  ],
3001  'no array' => [
3002  $content,
3003  $content,
3004  ['HTMLparser.' => 1],
3005  0,
3006  $parsed,
3007  ],
3008  'empty array' => [
3009  $parsed,
3010  $content,
3011  ['HTMLparser.' => []],
3012  1,
3013  $parsed,
3014  ],
3015  'non-empty array' => [
3016  $parsed,
3017  $content,
3018  ['HTMLparser.' => [true]],
3019  1,
3020  $parsed,
3021  ],
3022  ];
3023  }
3024 
3045  #[DataProvider('stdWrap_HTMLparserDataProvider')]
3046  #[Test]
3047  public function ‪stdWrap_HTMLparser(
3048  string $expect,
3049  string $content,
3050  array $conf,
3051  int $times,
3052  string $will
3053  ): void {
3054  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3055  ->onlyMethods(['HTMLparser_TSbridge'])->getMock();
3056  ‪$subject
3057  ->expects(self::exactly($times))
3058  ->method('HTMLparser_TSbridge')
3059  ->with($content, $conf['HTMLparser.'] ?? [])
3060  ->willReturn($will);
3061  self::assertSame(
3062  $expect,
3063  ‪$subject->‪stdWrap_HTMLparser($content, $conf)
3064  );
3065  }
3066 
3068  {
3069  return [
3070  'No Tag' => [
3071  [],
3072  ['addPageCacheTags' => ''],
3073  ],
3074  'Two expectedTags' => [
3075  ['tag1', 'tag2'],
3076  ['addPageCacheTags' => 'tag1,tag2'],
3077  ],
3078  'Two expectedTags plus one with stdWrap' => [
3079  ['tag1', 'tag2', 'tag3'],
3080  [
3081  'addPageCacheTags' => 'tag1,tag2',
3082  'addPageCacheTags.' => ['wrap' => '|,tag3'],
3083  ],
3084  ],
3085  ];
3086  }
3087 
3088  #[DataProvider('stdWrap_addPageCacheTagsAddsPageTagsDataProvider')]
3089  #[Test]
3090  public function ‪stdWrap_addPageCacheTagsAddsPageTags(array $expectedTags, array $configuration): void
3091  {
3092  $this->subject->stdWrap_addPageCacheTags('', $configuration);
3093  self::assertEquals($expectedTags, $this->frontendControllerMock->_get('pageCacheTags'));
3094  }
3095 
3106  #[Test]
3107  public function ‪stdWrap_age(): void
3108  {
3109  $now = 10;
3110  $content = '9';
3111  $conf = ['age' => ‪StringUtility::getUniqueId('age')];
3112  $return = ‪StringUtility::getUniqueId('return');
3113  $difference = $now - (int)$content;
3114  ‪$GLOBALS['EXEC_TIME'] = $now;
3115  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3116  ->onlyMethods(['calcAge'])->getMock();
3117  ‪$subject
3118  ->expects(self::once())
3119  ->method('calcAge')
3120  ->with($difference, $conf['age'])
3121  ->willReturn($return);
3122  self::assertSame($return, ‪$subject->‪stdWrap_age($content, $conf));
3123  }
3124 
3136  #[Test]
3137  public function ‪stdWrap_append(): void
3138  {
3139  $debugKey = '/stdWrap/.append';
3140  $content = ‪StringUtility::getUniqueId('content');
3141  $conf = [
3142  'append' => ‪StringUtility::getUniqueId('append'),
3143  'append.' => [‪StringUtility::getUniqueId('append.')],
3144  ];
3145  $return = ‪StringUtility::getUniqueId('return');
3146  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3147  ->onlyMethods(['cObjGetSingle'])->getMock();
3148  ‪$subject
3149  ->expects(self::once())
3150  ->method('cObjGetSingle')
3151  ->with($conf['append'], $conf['append.'], $debugKey)
3152  ->willReturn($return);
3153  self::assertSame(
3154  $content . $return,
3155  ‪$subject->‪stdWrap_append($content, $conf)
3156  );
3157  }
3158 
3164  public static function ‪stdWrapBrDataProvider(): array
3165  {
3166  return [
3167  'no xhtml with LF in between' => [
3168  'one<br>' . LF . 'two',
3169  'one' . LF . 'two',
3170  null,
3171  ],
3172  'no xhtml with LF in between and around' => [
3173  '<br>' . LF . 'one<br>' . LF . 'two<br>' . LF,
3174  LF . 'one' . LF . 'two' . LF,
3175  null,
3176  ],
3177  'xhtml with LF in between' => [
3178  'one<br />' . LF . 'two',
3179  'one' . LF . 'two',
3180  'xhtml_strict',
3181  ],
3182  'xhtml with LF in between and around' => [
3183  '<br />' . LF . 'one<br />' . LF . 'two<br />' . LF,
3184  LF . 'one' . LF . 'two' . LF,
3185  'xhtml_strict',
3186  ],
3187  ];
3188  }
3189 
3197  #[DataProvider('stdWrapBrDataProvider')]
3198  #[Test]
3199  public function ‪stdWrap_br(string $expected, string $input, ?string $doctype): void
3200  {
3201  $pageRenderer = $this->getMockBuilder(PageRenderer::class)->disableOriginalConstructor()->onlyMethods([])->getMock();
3202  $pageRenderer->setLanguage(new ‪Locale());
3203  $pageRenderer->setDocType(DocType::createFromConfigurationKey($doctype));
3204  GeneralUtility::setSingletonInstance(PageRenderer::class, $pageRenderer);
3205  self::assertSame($expected, $this->subject->stdWrap_br($input));
3206  }
3207 
3211  public static function ‪stdWrapBrTagDataProvider(): array
3212  {
3213  $noConfig = [];
3214  $config1 = ['brTag' => '<br/>'];
3215  $config2 = ['brTag' => '<br>'];
3216  return [
3217  'no config: one break at the beginning' => [LF . 'one' . LF . 'two', 'onetwo', $noConfig],
3218  'no config: multiple breaks at the beginning' => [LF . LF . 'one' . LF . 'two', 'onetwo', $noConfig],
3219  'no config: one break at the end' => ['one' . LF . 'two' . LF, 'onetwo', $noConfig],
3220  'no config: multiple breaks at the end' => ['one' . LF . 'two' . LF . LF, 'onetwo', $noConfig],
3221 
3222  'config1: one break at the beginning' => [LF . 'one' . LF . 'two', '<br/>one<br/>two', $config1],
3223  'config1: multiple breaks at the beginning' => [
3224  LF . LF . 'one' . LF . 'two',
3225  '<br/><br/>one<br/>two',
3226  $config1,
3227  ],
3228  'config1: one break at the end' => ['one' . LF . 'two' . LF, 'one<br/>two<br/>', $config1],
3229  'config1: multiple breaks at the end' => ['one' . LF . 'two' . LF . LF, 'one<br/>two<br/><br/>', $config1],
3230 
3231  'config2: one break at the beginning' => [LF . 'one' . LF . 'two', '<br>one<br>two', $config2],
3232  'config2: multiple breaks at the beginning' => [
3233  LF . LF . 'one' . LF . 'two',
3234  '<br><br>one<br>two',
3235  $config2,
3236  ],
3237  'config2: one break at the end' => ['one' . LF . 'two' . LF, 'one<br>two<br>', $config2],
3238  'config2: multiple breaks at the end' => ['one' . LF . 'two' . LF . LF, 'one<br>two<br><br>', $config2],
3239  ];
3240  }
3241 
3245  #[DataProvider('stdWrapBrTagDataProvider')]
3246  #[Test]
3247  public function ‪stdWrap_brTag(string $input, string $expected, array $config): void
3248  {
3249  self::assertEquals($expected, $this->subject->stdWrap_brTag($input, $config));
3250  }
3251 
3263  #[Test]
3264  public function ‪stdWrap_cObject(): void
3265  {
3266  $debugKey = '/stdWrap/.cObject';
3267  $content = ‪StringUtility::getUniqueId('content');
3268  $conf = [
3269  'cObject' => ‪StringUtility::getUniqueId('cObject'),
3270  'cObject.' => [‪StringUtility::getUniqueId('cObject.')],
3271  ];
3272  $return = ‪StringUtility::getUniqueId('return');
3273  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3274  ->onlyMethods(['cObjGetSingle'])->getMock();
3275  ‪$subject
3276  ->expects(self::once())
3277  ->method('cObjGetSingle')
3278  ->with($conf['cObject'], $conf['cObject.'], $debugKey)
3279  ->willReturn($return);
3280  self::assertSame(
3281  $return,
3282  ‪$subject->‪stdWrap_cObject($content, $conf)
3283  );
3284  }
3285 
3286  public static function ‪stdWrap_orderedStdWrapDataProvider(): array
3287  {
3288  return [
3289  'standard case: given order 1, 2' => [
3290  [
3291  'orderedStdWrap.' => [
3292  '1.' => [
3293  'wrap' => '<inner>|</inner>',
3294  ],
3295  '2.' => [
3296  'wrap' => '<outer>|</outer>',
3297  ],
3298  ],
3299  ],
3300  '<outer><inner>someContent</inner></outer>',
3301  ],
3302  'inverted: given order 2, 1' => [
3303  [
3304  'orderedStdWrap.' => [
3305  '2.' => [
3306  'wrap' => '<outer>|</outer>',
3307  ],
3308  '1.' => [
3309  'wrap' => '<inner>|</inner>',
3310  ],
3311  ],
3312  ],
3313  '<outer><inner>someContent</inner></outer>',
3314  ],
3315  '0 as integer: given order 0, 2' => [
3316  [
3317  'orderedStdWrap.' => [
3318  '0.' => [
3319  'wrap' => '<inner>|</inner>',
3320  ],
3321  '2.' => [
3322  'wrap' => '<outer>|</outer>',
3323  ],
3324  ],
3325  ],
3326  '<outer><inner>someContent</inner></outer>',
3327  ],
3328  'negative integers: given order 2, -2' => [
3329  [
3330  'orderedStdWrap.' => [
3331  '2.' => [
3332  'wrap' => '<outer>|</outer>',
3333  ],
3334  '-2.' => [
3335  'wrap' => '<inner>|</inner>',
3336  ],
3337  ],
3338  ],
3339  '<outer><inner>someContent</inner></outer>',
3340  ],
3341  'chars are casted to key 0, that is not in the array' => [
3342  [
3343  'orderedStdWrap.' => [
3344  '2.' => [
3345  'wrap' => '<inner>|</inner>',
3346  ],
3347  'xxx.' => [
3348  'wrap' => '<invalid>|</invalid>',
3349  ],
3350  ],
3351  ],
3352  '<inner>someContent</inner>',
3353  ],
3354  ];
3355  }
3356 
3357  #[DataProvider('stdWrap_orderedStdWrapDataProvider')]
3358  #[Test]
3359  public function ‪stdWrap_orderedStdWrap(array $config, string $expected): void
3360  {
3361  self::assertSame($expected, (new ‪ContentObjectRenderer())->‪stdWrap_orderedStdWrap('someContent', $config));
3362  }
3363 
3369  public static function ‪stdWrap_cacheReadDataProvider(): array
3370  {
3371  $cacheConf = [‪StringUtility::getUniqueId('cache.')];
3372  $conf = ['cache.' => $cacheConf];
3373  return [
3374  'no conf' => [
3375  'content',
3376  'content',
3377  [],
3378  0,
3379  null,
3380  null,
3381  ],
3382  'no cache. conf' => [
3383  'content',
3384  'content',
3385  ['otherConf' => 1],
3386  0,
3387  null,
3388  null,
3389  ],
3390  'non-cached simulation' => [
3391  'content',
3392  'content',
3393  $conf,
3394  1,
3395  $cacheConf,
3396  false,
3397  ],
3398  'cached simulation' => [
3399  'cachedContent',
3400  'content',
3401  $conf,
3402  1,
3403  $cacheConf,
3404  'cachedContent',
3405  ],
3406  ];
3407  }
3408 
3423  #[DataProvider('stdWrap_cacheReadDataProvider')]
3424  #[Test]
3425  public function ‪stdWrap_cacheRead(
3426  string $expect,
3427  string $input,
3428  array $conf,
3429  int $times,
3430  ?array $with,
3431  string|bool|null $will
3432  ): void {
3433  ‪$subject = $this->getAccessibleMock(
3434  ContentObjectRenderer::class,
3435  ['getFromCache']
3436  );
3437  ‪$subject
3438  ->expects(self::exactly($times))
3439  ->method('getFromCache')
3440  ->with($with)
3441  ->willReturn($will);
3442  self::assertSame(
3443  $expect,
3444  ‪$subject->‪stdWrap_cacheRead($input, $conf)
3445  );
3446  }
3447 
3453  public static function ‪stdWrap_cacheStoreDataProvider(): array
3454  {
3455  return [
3456  'Return immediate with no conf' => [
3457  null,
3458  0,
3459  null,
3460  ],
3461  'Return immediate with empty key' => [
3462  [‪StringUtility::getUniqueId('cache.')],
3463  1,
3464  '0',
3465  0,
3466  ],
3467  ];
3468  }
3469 
3483  #[DataProvider('stdWrap_cacheStoreDataProvider')]
3484  #[Test]
3485  public function ‪stdWrap_cacheStore(
3486  ?array $confCache,
3487  int $times,
3488  mixed $key,
3489  ): void {
3490  $content = ‪StringUtility::getUniqueId('content');
3491  $conf = [];
3492  $conf['cache.'] = $confCache;
3493  ‪$subject = $this->getAccessibleMock(
3494  ContentObjectRenderer::class,
3495  [
3496  'calculateCacheKey',
3497  'calculateCacheTags',
3498  'calculateCacheLifetime',
3499  'getTypoScriptFrontendController',
3500  ]
3501  );
3502  ‪$subject->expects(self::exactly($times))->method('calculateCacheKey')->with($confCache)->willReturn($key);
3503  self::assertSame(
3504  $content,
3505  ‪$subject->‪stdWrap_cacheStore($content, $conf)
3506  );
3507  }
3508 
3520  #[Test]
3522  {
3523  $beforeStdWrapContentStoredInCacheEvent = null;
3524  $modifiedContent = '---modified-content---';
3525 
3527  $container = GeneralUtility::getContainer();
3528  $container->set(
3529  'before-stdWrap-content-stored-in-cache-listener',
3530  static function (‪BeforeStdWrapContentStoredInCacheEvent $event) use (&$beforeStdWrapContentStoredInCacheEvent, $modifiedContent) {
3531  $beforeStdWrapContentStoredInCacheEvent = $event;
3532  $event->‪setContent($modifiedContent);
3533  }
3534  );
3535 
3536  $listenerProdiver = GeneralUtility::makeInstance(ListenerProvider::class, $container);
3537  $listenerProdiver->addListener(BeforeStdWrapContentStoredInCacheEvent::class, 'before-stdWrap-content-stored-in-cache-listener');
3538  $container->set(ListenerProvider::class, $listenerProdiver);
3539  $container->set(EventDispatcherInterface::class, new ‪EventDispatcher($listenerProdiver));
3540 
3541  $content = ‪StringUtility::getUniqueId('content');
3542  $tags = [‪StringUtility::getUniqueId('tags')];
3543  $key = ‪StringUtility::getUniqueId('key');
3544  $lifetime = 100;
3545  $cacheConfig = [
3547  ];
3548  $configuration = [
3549  'cache.' => $cacheConfig,
3550  ];
3551 
3552  ‪$subject = $this->getAccessibleMock(
3553  ContentObjectRenderer::class,
3554  [
3555  'calculateCacheKey',
3556  'calculateCacheTags',
3557  'calculateCacheLifetime',
3558  'getTypoScriptFrontendController',
3559  ]
3560  );
3561  ‪$subject->expects(self::once())->method('calculateCacheKey')->with($cacheConfig)->willReturn($key);
3562  ‪$subject->expects(self::once())->method('calculateCacheTags')->with($cacheConfig)->willReturn($tags);
3563  ‪$subject->expects(self::once())->method('calculateCacheLifetime')->with($cacheConfig)->willReturn($lifetime);
3564  $typoScriptFrontendController = $this->createMock(TypoScriptFrontendController::class);
3565  $typoScriptFrontendController->expects(self::once())->method('addCacheTags')->with($tags);
3566  ‪$subject->expects(self::once())->method('getTypoScriptFrontendController')->willReturn($typoScriptFrontendController);
3567  $cacheFrontend = $this->createMock(CacheFrontendInterface::class);
3568  $cacheFrontend
3569  ->expects(self::once())
3570  ->method('set')
3571  ->with($key, ['content' => $modifiedContent, 'cacheTags' => $tags], $tags, $lifetime)
3572  ->willReturn(null);
3573  $cacheManager = $this->createMock(CacheManager::class);
3574  $cacheManager
3575  ->method('getCache')
3576  ->willReturn($cacheFrontend);
3577  GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManager);
3578 
3579  $result = ‪$subject->‪stdWrap_cacheStore($content, $configuration);
3580 
3581  self::assertSame($modifiedContent, $result);
3582  self::assertInstanceOf(BeforeStdWrapContentStoredInCacheEvent::class, $beforeStdWrapContentStoredInCacheEvent);
3583  self::assertSame($modifiedContent, $beforeStdWrapContentStoredInCacheEvent->getContent());
3584  self::assertSame($tags, $beforeStdWrapContentStoredInCacheEvent->getTags());
3585  self::assertSame($key, $beforeStdWrapContentStoredInCacheEvent->getKey());
3586  self::assertSame($lifetime, $beforeStdWrapContentStoredInCacheEvent->getLifetime());
3587  self::assertSame($configuration, $beforeStdWrapContentStoredInCacheEvent->getConfiguration());
3588  self::assertSame(‪$subject, $beforeStdWrapContentStoredInCacheEvent->getContentObjectRenderer());
3589  }
3590 
3601  #[Test]
3602  public function ‪stdWrap_case(): void
3603  {
3604  $content = ‪StringUtility::getUniqueId();
3605  $conf = [
3606  'case' => ‪StringUtility::getUniqueId('used'),
3607  'case.' => [‪StringUtility::getUniqueId('discarded')],
3608  ];
3609  $return = ‪StringUtility::getUniqueId();
3610  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3611  ->onlyMethods(['HTMLcaseshift'])->getMock();
3612  ‪$subject
3613  ->expects(self::once())
3614  ->method('HTMLcaseshift')
3615  ->with($content, $conf['case'])
3616  ->willReturn($return);
3617  self::assertSame(
3618  $return,
3619  ‪$subject->‪stdWrap_case($content, $conf)
3620  );
3621  }
3622 
3626  #[Test]
3627  public function ‪stdWrap_char(): void
3628  {
3629  $input = 'discarded';
3630  $expected = 'C';
3631  self::assertEquals($expected, $this->subject->stdWrap_char($input, ['char' => '67']));
3632  }
3633 
3644  #[Test]
3645  public function ‪stdWrap_crop(): void
3646  {
3647  $content = ‪StringUtility::getUniqueId('content');
3648  $conf = [
3649  'crop' => ‪StringUtility::getUniqueId('crop'),
3650  'crop.' => ‪StringUtility::getUniqueId('not used'),
3651  ];
3652  $return = ‪StringUtility::getUniqueId('return');
3653  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3654  ->onlyMethods(['crop'])->getMock();
3655  ‪$subject
3656  ->expects(self::once())
3657  ->method('crop')
3658  ->with($content, $conf['crop'])
3659  ->willReturn($return);
3660  self::assertSame(
3661  $return,
3662  ‪$subject->‪stdWrap_crop($content, $conf)
3663  );
3664  }
3665 
3676  #[Test]
3677  public function ‪stdWrap_cropHTML(): void
3678  {
3679  $content = ‪StringUtility::getUniqueId('content');
3680  $conf = [
3681  'cropHTML' => ‪StringUtility::getUniqueId('cropHTML'),
3682  'cropHTML.' => ‪StringUtility::getUniqueId('not used'),
3683  ];
3684  $return = ‪StringUtility::getUniqueId('return');
3685  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3686  ->onlyMethods(['cropHTML'])->getMock();
3687  ‪$subject
3688  ->expects(self::once())
3689  ->method('cropHTML')
3690  ->with($content, $conf['cropHTML'])
3691  ->willReturn($return);
3692  self::assertSame(
3693  $return,
3694  ‪$subject->‪stdWrap_cropHTML($content, $conf)
3695  );
3696  }
3697 
3698  public static function ‪stdWrap_formattedDateProvider(): \Generator
3699  {
3700  yield 'regular formatting - no locale' => [
3701  '2023.02.02 AD at 13:05:00 UTC',
3702  "yyyy.MM.dd G 'at' HH:mm:ss zzz",
3703  ];
3704  yield 'full - no locale' => [
3705  'Thursday, February 2, 2023 at 13:05:00 Coordinated Universal Time',
3706  'FULL',
3707  ];
3708  yield 'long - no locale' => [
3709  'February 2, 2023 at 13:05:00 UTC',
3710  'LONG',
3711  ];
3712  yield 'medium - no locale' => [
3713  'Feb 2, 2023, 13:05:00',
3714  'MEDIUM',
3715  ];
3716  yield 'medium with int - no locale' => [
3717  'Feb 2, 2023, 13:05:00',
3718  \IntlDateFormatter::MEDIUM,
3719  ];
3720  yield 'short - no locale' => [
3721  '2/2/23, 13:05',
3722  'SHORT',
3723  ];
3724  yield 'regular formatting - german locale' => [
3725  '2023.02.02 n. Chr. um 13:05:00 UTC',
3726  "yyyy.MM.dd G 'um' HH:mm:ss zzz",
3727  'de-DE',
3728  ];
3729  yield 'full - german locale' => [
3730  'Donnerstag, 2. Februar 2023 um 13:05:00 Koordinierte Weltzeit',
3731  'FULL',
3732  'de-DE',
3733  ];
3734  yield 'long - german locale' => [
3735  '2. Februar 2023 um 13:05:00 UTC',
3736  'LONG',
3737  'de-DE',
3738  ];
3739  yield 'medium - german locale' => [
3740  '02.02.2023, 13:05:00',
3741  'MEDIUM',
3742  'de-DE',
3743  ];
3744  yield 'short - german locale' => [
3745  '02.02.23, 13:05',
3746  'SHORT',
3747  'de-DE',
3748  ];
3749  yield 'custom date only - german locale' => [
3750  '02. Februar 2023',
3751  'dd. MMMM yyyy',
3752  'de-DE',
3753  ];
3754  yield 'custom time only - german locale' => [
3755  '13:05:00',
3756  'HH:mm:ss',
3757  'de-DE',
3758  ];
3759  yield 'given date and time - german locale' => [
3760  'Freitag, 20. Februar 1998 um 03:00:00 Koordinierte Weltzeit',
3761  'FULL',
3762  'de-DE',
3763  '1998-02-20 3:00:00',
3764  ];
3765  }
3766 
3767  #[DataProvider('stdWrap_formattedDateProvider')]
3768  #[Test]
3769  public function ‪stdWrap_formattedDate(string $expected, mixed $pattern, string $locale = null, string $givenDate = null): void
3770  {
3771  $context = new ‪Context();
3772  $context->setAspect('date', new ‪DateTimeAspect(new \DateTimeImmutable('2023-02-02 13:05:00')));
3773  GeneralUtility::setSingletonInstance(Context::class, $context);
3775  $site = $this->‪createSiteWithLanguage([
3776  'base' => '/',
3777  'languageId' => 2,
3778  'locale' => 'en_UK',
3779  ]);
3780  $request = (new ‪ServerRequest())->withAttribute('language', $site->getLanguageById(2));
3781  ‪$subject->‪setRequest($request);
3782  $conf = ['formattedDate' => $pattern];
3783  if ($locale !== null) {
3784  $conf['formattedDate.']['locale'] = $locale;
3785  }
3786  self::assertEquals($expected, ‪$subject->‪stdWrap_formattedDate((string)$givenDate, $conf));
3787  }
3788 
3794  public static function ‪stdWrap_csConvDataProvider(): array
3795  {
3796  return [
3797  'empty string from ISO-8859-15' => [
3798  '',
3799  mb_convert_encoding('', 'ISO-8859-15', 'UTF-8'),
3800  ['csConv' => 'ISO-8859-15'],
3801  ],
3802  'empty string from BIG-5' => [
3803  '',
3804  mb_convert_encoding('', 'BIG-5'),
3805  ['csConv' => 'BIG-5'],
3806  ],
3807  '"0" from ISO-8859-15' => [
3808  '0',
3809  mb_convert_encoding('0', 'ISO-8859-15', 'UTF-8'),
3810  ['csConv' => 'ISO-8859-15'],
3811  ],
3812  '"0" from BIG-5' => [
3813  '0',
3814  mb_convert_encoding('0', 'BIG-5'),
3815  ['csConv' => 'BIG-5'],
3816  ],
3817  'euro symbol from ISO-88859-15' => [
3818  '€',
3819  mb_convert_encoding('€', 'ISO-8859-15', 'UTF-8'),
3820  ['csConv' => 'ISO-8859-15'],
3821  ],
3822  'good morning from BIG-5' => [
3823  '早安',
3824  mb_convert_encoding('早安', 'BIG-5'),
3825  ['csConv' => 'BIG-5'],
3826  ],
3827  ];
3828  }
3829 
3837  #[DataProvider('stdWrap_csConvDataProvider')]
3838  #[Test]
3839  public function ‪stdWrap_csConv(string $expected, string $input, array $conf): void
3840  {
3841  self::assertSame(
3842  $expected,
3843  $this->subject->stdWrap_csConv($input, $conf)
3844  );
3845  }
3846 
3856  #[Test]
3857  public function ‪stdWrap_current(): void
3858  {
3859  $data = [
3860  'currentValue_kidjls9dksoje' => 'default',
3861  'currentValue_new' => 'new',
3862  ];
3863  $this->subject->_set('data', $data);
3864  self::assertSame(
3865  'currentValue_kidjls9dksoje',
3866  $this->subject->_get('currentValKey')
3867  );
3868  self::assertSame(
3869  'default',
3870  $this->subject->stdWrap_current('discarded', ['discarded'])
3871  );
3872  $this->subject->_set('currentValKey', 'currentValue_new');
3873  self::assertSame(
3874  'new',
3875  $this->subject->stdWrap_current('discarded', ['discarded'])
3876  );
3877  }
3878 
3884  public static function ‪stdWrap_dataDataProvider(): array
3885  {
3886  $data = [‪StringUtility::getUniqueId('data')];
3887  return [
3888  'default' => [$data, $data, ''],
3889  ];
3890  }
3891 
3904  #[DataProvider('stdWrap_dataDataProvider')]
3905  #[Test]
3906  public function ‪stdWrap_data(array $expect, array $data): void
3907  {
3908  $conf = ['data' => ‪StringUtility::getUniqueId('conf.data')];
3909  $return = ‪StringUtility::getUniqueId('return');
3910  ‪$subject = $this->getAccessibleMock(
3911  ContentObjectRenderer::class,
3912  ['getData']
3913  );
3914  ‪$subject->_set('data', $data);
3915  ‪$subject
3916  ->expects(self::once())
3917  ->method('getData')
3918  ->with($conf['data'], $expect)
3919  ->willReturn($return);
3920  self::assertSame($return, ‪$subject->‪stdWrap_data('discard', $conf));
3921  }
3922 
3933  #[Test]
3934  public function ‪stdWrap_dataWrap(): void
3935  {
3936  $content = ‪StringUtility::getUniqueId('content');
3937  $conf = [
3938  'dataWrap' => ‪StringUtility::getUniqueId('dataWrap'),
3939  'dataWrap.' => [‪StringUtility::getUniqueId('not used')],
3940  ];
3941  $return = ‪StringUtility::getUniqueId('return');
3942  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
3943  ->onlyMethods(['dataWrap'])->getMock();
3944  ‪$subject
3945  ->expects(self::once())
3946  ->method('dataWrap')
3947  ->with($content, $conf['dataWrap'])
3948  ->willReturn($return);
3949  self::assertSame(
3950  $return,
3951  ‪$subject->‪stdWrap_dataWrap($content, $conf)
3952  );
3953  }
3954 
3960  public static function ‪stdWrap_dateDataProvider(): array
3961  {
3962  // Fictive execution time: 2015-10-02 12:00
3963  $now = 1443780000;
3964  return [
3965  'given timestamp' => [
3966  '02.10.2015',
3967  $now,
3968  ['date' => 'd.m.Y'],
3969  $now,
3970  ],
3971  'empty string' => [
3972  '02.10.2015',
3973  '',
3974  ['date' => 'd.m.Y'],
3975  $now,
3976  ],
3977  'testing null' => [
3978  '02.10.2015',
3979  null,
3980  ['date' => 'd.m.Y'],
3981  $now,
3982  ],
3983  'given timestamp return GMT' => [
3984  '02.10.2015 10:00:00',
3985  $now,
3986  [
3987  'date' => 'd.m.Y H:i:s',
3988  'date.' => ['GMT' => true],
3989  ],
3990  $now,
3991  ],
3992  ];
3993  }
3994 
4003  #[DataProvider('stdWrap_dateDataProvider')]
4004  #[Test]
4005  public function ‪stdWrap_date(string $expected, mixed $content, array $conf, int $now): void
4006  {
4007  ‪$GLOBALS['EXEC_TIME'] = $now;
4008  self::assertEquals(
4009  $expected,
4010  $this->subject->stdWrap_date($content, $conf)
4011  );
4012  }
4013 
4017  #[Test]
4018  public function ‪stdWrap_debug(): void
4019  {
4020  $expect = '<pre>&lt;p class=&quot;class&quot;&gt;&lt;br/&gt;'
4021  . '&lt;/p&gt;</pre>';
4022  $content = '<p class="class"><br/></p>';
4023  self::assertSame($expect, $this->subject->stdWrap_debug($content));
4024  }
4025 
4044  #[Test]
4045  public function ‪stdWrap_debugData(): void
4046  {
4047  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*';
4048  $content = ‪StringUtility::getUniqueId('content');
4049  $key = ‪StringUtility::getUniqueId('key');
4050  $value = ‪StringUtility::getUniqueId('value');
4051  $altValue = ‪StringUtility::getUniqueId('value alt');
4052  $this->subject->data = [$key => $value];
4053  ob_start();
4054  $result = $this->subject->stdWrap_debugData($content);
4055  $out = ob_get_clean();
4056  self::assertSame($result, $content);
4057  self::assertStringContainsString('$cObj->data', $out);
4058  self::assertStringContainsString($value, $out);
4059  self::assertStringNotContainsString($altValue, $out);
4060  }
4061 
4067  public static function ‪stdWrap_debugFuncDataProvider(): array
4068  {
4069  return [
4070  'expect array by string' => [true, '2'],
4071  'expect array by integer' => [true, 2],
4072  'do not expect array' => [false, ''],
4073  ];
4074  }
4075 
4095  #[DataProvider('stdWrap_debugFuncDataProvider')]
4096  #[Test]
4097  public function ‪stdWrap_debugFunc(bool $expectArray, mixed $confDebugFunc): void
4098  {
4099  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = '*';
4100  $content = ‪StringUtility::getUniqueId('content');
4101  $conf = ['debugFunc' => $confDebugFunc];
4102  ob_start();
4103  $result = $this->subject->stdWrap_debugFunc($content, $conf);
4104  $out = ob_get_clean();
4105  self::assertSame($result, $content);
4106  self::assertStringContainsString($content, $out);
4107  if ($expectArray) {
4108  self::assertStringContainsString('=>', $out);
4109  } else {
4110  self::assertStringNotContainsString('=>', $out);
4111  }
4112  }
4113 
4119  public static function ‪stdWrapDoubleBrTagDataProvider(): array
4120  {
4121  return [
4122  'no config: void input' => [
4123  '',
4124  '',
4125  [],
4126  ],
4127  'no config: single break' => [
4128  'one' . LF . 'two',
4129  'one' . LF . 'two',
4130  [],
4131  ],
4132  'no config: double break' => [
4133  'onetwo',
4134  'one' . LF . LF . 'two',
4135  [],
4136  ],
4137  'no config: double break with whitespace' => [
4138  'onetwo',
4139  'one' . LF . "\t" . ' ' . "\t" . ' ' . LF . 'two',
4140  [],
4141  ],
4142  'no config: single break around' => [
4143  LF . 'one' . LF,
4144  LF . 'one' . LF,
4145  [],
4146  ],
4147  'no config: double break around' => [
4148  'one',
4149  LF . LF . 'one' . LF . LF,
4150  [],
4151  ],
4152  'empty string: double break around' => [
4153  'one',
4154  LF . LF . 'one' . LF . LF,
4155  ['doubleBrTag' => ''],
4156  ],
4157  'br tag: double break' => [
4158  'one<br/>two',
4159  'one' . LF . LF . 'two',
4160  ['doubleBrTag' => '<br/>'],
4161  ],
4162  'br tag: double break around' => [
4163  '<br/>one<br/>',
4164  LF . LF . 'one' . LF . LF,
4165  ['doubleBrTag' => '<br/>'],
4166  ],
4167  'double br tag: double break around' => [
4168  '<br/><br/>one<br/><br/>',
4169  LF . LF . 'one' . LF . LF,
4170  ['doubleBrTag' => '<br/><br/>'],
4171  ],
4172  ];
4173  }
4174 
4182  #[DataProvider('stdWrapDoubleBrTagDataProvider')]
4183  #[Test]
4184  public function ‪stdWrap_doubleBrTag(string $expected, string $input, array $config): void
4185  {
4186  self::assertEquals($expected, $this->subject->stdWrap_doubleBrTag($input, $config));
4187  }
4188 
4199  #[Test]
4200  public function ‪stdWrap_encapsLines(): void
4201  {
4202  $content = ‪StringUtility::getUniqueId('content');
4203  $conf = [
4204  'encapsLines' => [‪StringUtility::getUniqueId('not used')],
4205  'encapsLines.' => [‪StringUtility::getUniqueId('encapsLines.')],
4206  ];
4207  $return = ‪StringUtility::getUniqueId('return');
4208  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
4209  ->onlyMethods(['encaps_lineSplit'])->getMock();
4210  ‪$subject
4211  ->expects(self::once())
4212  ->method('encaps_lineSplit')
4213  ->with($content, $conf['encapsLines.'])
4214  ->willReturn($return);
4215  self::assertSame(
4216  $return,
4217  ‪$subject->‪stdWrap_encapsLines($content, $conf)
4218  );
4219  }
4220 
4226  #[DataProvider('html5SelfClosingTagsDataprovider')]
4227  #[Test]
4228  public function ‪stdWrap_encapsLines_HTML5SelfClosingTags(string $input, string $expected): void
4229  {
4230  $rteParseFunc = ‪self::getLibParseFunc_RTE();
4231 
4232  $conf = [
4233  'encapsLines' => $rteParseFunc['parseFunc.']['nonTypoTagStdWrap.']['encapsLines'] ?? null,
4234  'encapsLines.' => $rteParseFunc['parseFunc.']['nonTypoTagStdWrap.']['encapsLines.'] ?? null,
4235  ];
4236  // don't add an &nbsp; to tag without content
4237  $conf['encapsLines.']['innerStdWrap_all.']['ifBlank'] = '';
4238  $additionalEncapsTags = ['a', 'b', 'span'];
4239 
4240  // We want to allow any tag to be an encapsulating tag
4241  // since this is possible and we don't want an additional tag to be wrapped around.
4242  $conf['encapsLines.']['encapsTagList'] .= ',' . implode(',', $additionalEncapsTags);
4243  $conf['encapsLines.']['encapsTagList'] .= ',' . implode(',', [$input]);
4244 
4245  // Check if we get a self-closing tag for
4246  // empty tags where this is allowed according to HTML5
4247  $content = '<' . $input . ' id="myId" class="bodytext" />';
4248  $result = $this->subject->stdWrap_encapsLines($content, $conf);
4249  self::assertSame($expected, $result);
4250  }
4251 
4252  public static function ‪html5SelfClosingTagsDataprovider(): array
4253  {
4254  return [
4255  'areaTag_selfclosing' => [
4256  'input' => 'area',
4257  'expected' => '<area id="myId" class="bodytext" />',
4258  ],
4259  'base_selfclosing' => [
4260  'input' => 'base',
4261  'expected' => '<base id="myId" class="bodytext" />',
4262  ],
4263  'br_selfclosing' => [
4264  'input' => 'br',
4265  'expected' => '<br id="myId" class="bodytext" />',
4266  ],
4267  'col_selfclosing' => [
4268  'input' => 'col',
4269  'expected' => '<col id="myId" class="bodytext" />',
4270  ],
4271  'embed_selfclosing' => [
4272  'input' => 'embed',
4273  'expected' => '<embed id="myId" class="bodytext" />',
4274  ],
4275  'hr_selfclosing' => [
4276  'input' => 'hr',
4277  'expected' => '<hr id="myId" class="bodytext" />',
4278  ],
4279  'img_selfclosing' => [
4280  'input' => 'img',
4281  'expected' => '<img id="myId" class="bodytext" />',
4282  ],
4283  'input_selfclosing' => [
4284  'input' => 'input',
4285  'expected' => '<input id="myId" class="bodytext" />',
4286  ],
4287  'keygen_selfclosing' => [
4288  'input' => 'keygen',
4289  'expected' => '<keygen id="myId" class="bodytext" />',
4290  ],
4291  'link_selfclosing' => [
4292  'input' => 'link',
4293  'expected' => '<link id="myId" class="bodytext" />',
4294  ],
4295  'meta_selfclosing' => [
4296  'input' => 'meta',
4297  'expected' => '<meta id="myId" class="bodytext" />',
4298  ],
4299  'param_selfclosing' => [
4300  'input' => 'param',
4301  'expected' => '<param id="myId" class="bodytext" />',
4302  ],
4303  'source_selfclosing' => [
4304  'input' => 'source',
4305  'expected' => '<source id="myId" class="bodytext" />',
4306  ],
4307  'track_selfclosing' => [
4308  'input' => 'track',
4309  'expected' => '<track id="myId" class="bodytext" />',
4310  ],
4311  'wbr_selfclosing' => [
4312  'input' => 'wbr',
4313  'expected' => '<wbr id="myId" class="bodytext" />',
4314  ],
4315  'p_notselfclosing' => [
4316  'input' => 'p',
4317  'expected' => '<p id="myId" class="bodytext"></p>',
4318  ],
4319  'a_notselfclosing' => [
4320  'input' => 'a',
4321  'expected' => '<a id="myId" class="bodytext"></a>',
4322  ],
4323  'strong_notselfclosing' => [
4324  'input' => 'strong',
4325  'expected' => '<strong id="myId" class="bodytext"></strong>',
4326  ],
4327  'span_notselfclosing' => [
4328  'input' => 'span',
4329  'expected' => '<span id="myId" class="bodytext"></span>',
4330  ],
4331  ];
4332  }
4333 
4339  public static function ‪stdWrap_encodeForJavaScriptValueDataProvider(): array
4340  {
4341  return [
4342  'double quote in string' => [
4343  '\'double\u0020quote\u0022\'',
4344  'double quote"',
4345  ],
4346  'backslash in string' => [
4347  '\'backslash\u0020\u005C\'',
4348  'backslash \\',
4349  ],
4350  'exclamation mark' => [
4351  '\'exclamation\u0021\'',
4352  'exclamation!',
4353  ],
4354  'whitespace tab, newline and carriage return' => [
4355  '\'white\u0009space\u000As\u000D\'',
4356  "white\tspace\ns\r",
4357  ],
4358  'single quote in string' => [
4359  '\'single\u0020quote\u0020\u0027\'',
4360  'single quote \'',
4361  ],
4362  'tag' => [
4363  '\'\u003Ctag\u003E\'',
4364  '<tag>',
4365  ],
4366  'ampersand in string' => [
4367  '\'amper\u0026sand\'',
4368  'amper&sand',
4369  ],
4370  ];
4371  }
4372 
4379  #[DataProvider('stdWrap_encodeForJavaScriptValueDataProvider')]
4380  #[Test]
4381  public function ‪stdWrap_encodeForJavaScriptValue(string $expect, string $content): void
4382  {
4383  self::assertSame(
4384  $expect,
4385  $this->subject->stdWrap_encodeForJavaScriptValue($content)
4386  );
4387  }
4388 
4394  public static function ‪stdWrap_expandListDataProvider(): array
4395  {
4396  return [
4397  'numbers' => ['1,2,3', '1,2,3'],
4398  'range' => ['3,4,5', '3-5'],
4399  'numbers and range' => ['1,3,4,5,7', '1,3-5,7'],
4400  ];
4401  }
4402 
4414  #[DataProvider('stdWrap_expandListDataProvider')]
4415  #[Test]
4416  public function ‪stdWrap_expandList(string $expected, string $content): void
4417  {
4418  self::assertEquals(
4419  $expected,
4420  $this->subject->stdWrap_expandList($content)
4421  );
4422  }
4423 
4432  #[Test]
4433  public function ‪stdWrap_field(): void
4434  {
4435  $expect = ‪StringUtility::getUniqueId('expect');
4436  $conf = ['field' => ‪StringUtility::getUniqueId('field')];
4437  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
4438  ->onlyMethods(['getFieldVal'])->getMock();
4439  ‪$subject
4440  ->expects(self::once())
4441  ->method('getFieldVal')
4442  ->with($conf['field'])
4443  ->willReturn($expect);
4444  self::assertSame(
4445  $expect,
4446  ‪$subject->‪stdWrap_field('discarded', $conf)
4447  );
4448  }
4449 
4455  public static function ‪stdWrap_fieldRequiredDataProvider(): array
4456  {
4457  $content = ‪StringUtility::getUniqueId('content');
4458  return [
4459  // resulting in boolean false
4460  'false is false' => [
4461  '',
4462  true,
4463  $content,
4464  ['fieldRequired' => 'false'],
4465  ],
4466  'null is false' => [
4467  '',
4468  true,
4469  $content,
4470  ['fieldRequired' => 'null'],
4471  ],
4472  'empty string is false' => [
4473  '',
4474  true,
4475  $content,
4476  ['fieldRequired' => 'empty'],
4477  ],
4478  'whitespace is false' => [
4479  '',
4480  true,
4481  $content,
4482  ['fieldRequired' => 'whitespace'],
4483  ],
4484  'string zero is false' => [
4485  '',
4486  true,
4487  $content,
4488  ['fieldRequired' => 'stringZero'],
4489  ],
4490  'string zero with whitespace is false' => [
4491  '',
4492  true,
4493  $content,
4494  ['fieldRequired' => 'stringZeroWithWhiteSpace'],
4495  ],
4496  'zero is false' => [
4497  '',
4498  true,
4499  $content,
4500  ['fieldRequired' => 'zero'],
4501  ],
4502  // resulting in boolean true
4503  'true is true' => [
4504  $content,
4505  false,
4506  $content,
4507  ['fieldRequired' => 'true'],
4508  ],
4509  'string is true' => [
4510  $content,
4511  false,
4512  $content,
4513  ['fieldRequired' => 'string'],
4514  ],
4515  'one is true' => [
4516  $content,
4517  false,
4518  $content,
4519  ['fieldRequired' => 'one'],
4520  ],
4521  ];
4522  }
4523 
4541  #[DataProvider('stdWrap_fieldRequiredDataProvider')]
4542  #[Test]
4543  public function ‪stdWrap_fieldRequired(string $expect, bool $stop, string $content, array $conf): void
4544  {
4545  $data = [
4546  'null' => null,
4547  'false' => false,
4548  'empty' => '',
4549  'whitespace' => "\t" . ' ',
4550  'stringZero' => '0',
4551  'stringZeroWithWhiteSpace' => "\t" . ' 0 ' . "\t",
4552  'zero' => 0,
4553  'string' => 'string',
4554  'true' => true,
4555  'one' => 1,
4556  ];
4558  ‪$subject->_set('data', $data);
4559  ‪$subject->_set('stdWrapRecursionLevel', 1);
4560  ‪$subject->_set('stopRendering', [1 => false]);
4561  self::assertSame(
4562  $expect,
4563  ‪$subject->‪stdWrap_fieldRequired($content, $conf)
4564  );
4565  self::assertSame($stop, ‪$subject->_get('stopRendering')[1]);
4566  }
4567 
4573  public static function ‪hashDataProvider(): array
4574  {
4575  return [
4576  'md5' => [
4577  'bacb98acf97e0b6112b1d1b650b84971',
4578  'joh316',
4579  ['hash' => 'md5'],
4580  ],
4581  'sha1' => [
4582  '063b3d108bed9f88fa618c6046de0dccadcf3158',
4583  'joh316',
4584  ['hash' => 'sha1'],
4585  ],
4586  'stdWrap capability' => [
4587  'bacb98acf97e0b6112b1d1b650b84971',
4588  'joh316',
4589  [
4590  'hash' => '5',
4591  'hash.' => ['wrap' => 'md|'],
4592  ],
4593  ],
4594  'non-existing hashing algorithm' => [
4595  '',
4596  'joh316',
4597  ['hash' => 'non-existing'],
4598  ],
4599  ];
4600  }
4601 
4615  #[DataProvider('hashDataProvider')]
4616  #[Test]
4617  public function ‪stdWrap_hash(string $expect, string $content, array $conf): void
4618  {
4619  self::assertSame(
4620  $expect,
4621  $this->subject->stdWrap_hash($content, $conf)
4622  );
4623  }
4624 
4630  public static function ‪stdWrap_htmlSpecialCharsDataProvider(): array
4631  {
4632  return [
4633  'void conf' => [
4634  '&lt;span&gt;1 &amp;lt; 2&lt;/span&gt;',
4635  '<span>1 &lt; 2</span>',
4636  [],
4637  ],
4638  'void preserveEntities' => [
4639  '&lt;span&gt;1 &amp;lt; 2&lt;/span&gt;',
4640  '<span>1 &lt; 2</span>',
4641  ['htmlSpecialChars.' => []],
4642  ],
4643  'false preserveEntities' => [
4644  '&lt;span&gt;1 &amp;lt; 2&lt;/span&gt;',
4645  '<span>1 &lt; 2</span>',
4646  ['htmlSpecialChars.' => ['preserveEntities' => 0]],
4647  ],
4648  'true preserveEntities' => [
4649  '&lt;span&gt;1 &lt; 2&lt;/span&gt;',
4650  '<span>1 &lt; 2</span>',
4651  ['htmlSpecialChars.' => ['preserveEntities' => 1]],
4652  ],
4653  ];
4654  }
4655 
4663  #[DataProvider('stdWrap_htmlSpecialCharsDataProvider')]
4664  #[Test]
4665  public function ‪stdWrap_htmlSpecialChars(string $expected, string $input, array $conf): void
4666  {
4667  self::assertSame(
4668  $expected,
4669  $this->subject->stdWrap_htmlSpecialChars($input, $conf)
4670  );
4671  }
4672 
4678  public static function ‪stdWrap_ifDataProvider(): array
4679  {
4680  $content = ‪StringUtility::getUniqueId('content');
4681  $conf = ['if.' => [‪StringUtility::getUniqueId('if.')]];
4682  return [
4683  // evals to true
4684  'empty config' => [
4685  $content,
4686  false,
4687  $content,
4688  [],
4689  0,
4690  false,
4691  ],
4692  'if. is empty array' => [
4693  $content,
4694  false,
4695  $content,
4696  ['if.' => []],
4697  0,
4698  false,
4699  ],
4700  'if. is null' => [
4701  $content,
4702  false,
4703  $content,
4704  ['if.' => null],
4705  0,
4706  false,
4707  ],
4708  'if. is false' => [
4709  $content,
4710  false,
4711  $content,
4712  ['if.' => false],
4713  0,
4714  false,
4715  ],
4716  'if. is 0' => [
4717  $content,
4718  false,
4719  $content,
4720  ['if.' => false],
4721  0,
4722  false,
4723  ],
4724  'if. is "0"' => [
4725  $content,
4726  false,
4727  $content,
4728  ['if.' => '0'],
4729  0,
4730  false,
4731  ],
4732  'checkIf returning true' => [
4733  $content,
4734  false,
4735  $content,
4736  $conf,
4737  1,
4738  true,
4739  ],
4740  // evals to false
4741  'checkIf returning false' => [
4742  '',
4743  true,
4744  $content,
4745  $conf,
4746  1,
4747  false,
4748  ],
4749  ];
4750  }
4751 
4770  #[DataProvider('stdWrap_ifDataProvider')]
4771  #[Test]
4772  public function ‪stdWrap_if(string $expect, bool $stop, string $content, array $conf, int $times, bool $will): void
4773  {
4774  ‪$subject = $this->getAccessibleMock(
4775  ContentObjectRenderer::class,
4776  ['checkIf']
4777  );
4778  ‪$subject->_set('stdWrapRecursionLevel', 1);
4779  ‪$subject->_set('stopRendering', [1 => false]);
4780  ‪$subject
4781  ->expects(self::exactly($times))
4782  ->method('checkIf')
4783  ->with($conf['if.'] ?? null)
4784  ->willReturn($will);
4785  self::assertSame($expect, ‪$subject->‪stdWrap_if($content, $conf));
4786  self::assertSame($stop, ‪$subject->_get('stopRendering')[1]);
4787  }
4788 
4794  public static function ‪checkIfDataProvider(): array
4795  {
4796  return [
4797  'true bitAnd the same' => [true, ['bitAnd' => '4', 'value' => '4']],
4798  'true bitAnd included' => [true, ['bitAnd' => '6', 'value' => '4']],
4799  'false bitAnd' => [false, ['bitAnd' => '4', 'value' => '3']],
4800  'negate true bitAnd the same' => [false, ['bitAnd' => '4', 'value' => '4', 'negate' => '1']],
4801  'negate true bitAnd included' => [false, ['bitAnd' => '6', 'value' => '4', 'negate' => '1']],
4802  'negate false bitAnd' => [true, ['bitAnd' => '3', 'value' => '4', 'negate' => '1']],
4803  'contains matches' => [true, ['contains' => 'long text', 'value' => 'this is a long text']],
4804  'contains does not match' => [false, ['contains' => 'short text', 'value' => 'this is a long text']],
4805  'negate contains does not match' => [false, ['contains' => 'long text', 'value' => 'this is a long text', 'negate' => '1']],
4806  'negate contains does not match but matches' => [true, ['contains' => 'short text', 'value' => 'this is a long text', 'negate' => '1']],
4807  'startsWith matches' => [true, ['startsWith' => 'this is', 'value' => 'this is a long text']],
4808  'startsWith does not match' => [false, ['startsWith' => 'a long text', 'value' => 'this is a long text']],
4809  'negate startsWith does not match' => [false, ['startsWith' => 'this is', 'value' => 'this is a long text', 'negate' => '1']],
4810  'negate startsWith does not match but matches' => [true, ['startsWith' => 'a long text', 'value' => 'this is a long text', 'negate' => '1']],
4811  'endsWith matches' => [true, ['endsWith' => 'a long text', 'value' => 'this is a long text']],
4812  'endsWith does not match' => [false, ['endsWith' => 'this is', 'value' => 'this is a long text']],
4813  'negate endsWith does not match' => [false, ['endsWith' => 'a long text', 'value' => 'this is a long text', 'negate' => '1']],
4814  'negate endsWith does not match but matches' => [true, ['endsWith' => 'this is', 'value' => 'this is a long text', 'negate' => '1']],
4815  ];
4816  }
4817 
4824  #[DataProvider('checkIfDataProvider')]
4825  #[Test]
4826  public function ‪checkIf(bool $expect, array $conf): void
4827  {
4828  ‪$subject = $this->getAccessibleMock(
4829  ContentObjectRenderer::class,
4830  ['stdWrap']
4831  );
4832  self::assertSame($expect, ‪$subject->‪checkIf($conf));
4833  }
4834 
4840  public static function ‪stdWrap_ifBlankDataProvider(): array
4841  {
4842  $alt = ‪StringUtility::getUniqueId('alternative content');
4843  $conf = ['ifBlank' => $alt];
4844  return [
4845  // blank cases
4846  'null is blank' => [$alt, null, $conf],
4847  'false is blank' => [$alt, false, $conf],
4848  'empty string is blank' => [$alt, '', $conf],
4849  'whitespace is blank' => [$alt, "\t" . '', $conf],
4850  // non-blank cases
4851  'string is not blank' => ['string', 'string', $conf],
4852  'zero is not blank' => [0, 0, $conf],
4853  'zero string is not blank' => ['0', '0', $conf],
4854  'zero float is not blank' => [0.0, 0.0, $conf],
4855  'true is not blank' => [true, true, $conf],
4856  ];
4857  }
4858 
4873  #[DataProvider('stdWrap_ifBlankDataProvider')]
4874  #[Test]
4875  public function ‪stdWrap_ifBlank(mixed $expect, mixed $content, array $conf): void
4876  {
4877  $result = $this->subject->stdWrap_ifBlank($content, $conf);
4878  self::assertSame($expect, $result);
4879  }
4880 
4886  public static function ‪stdWrap_ifEmptyDataProvider(): array
4887  {
4888  $alt = ‪StringUtility::getUniqueId('alternative content');
4889  $conf = ['ifEmpty' => $alt];
4890  return [
4891  // empty cases
4892  'null is empty' => [$alt, null, $conf],
4893  'false is empty' => [$alt, false, $conf],
4894  'zero is empty' => [$alt, 0, $conf],
4895  'float zero is empty' => [$alt, 0.0, $conf],
4896  'whitespace is empty' => [$alt, "\t" . ' ', $conf],
4897  'empty string is empty' => [$alt, '', $conf],
4898  'zero string is empty' => [$alt, '0', $conf],
4899  'zero string is empty with whitespace' => [
4900  $alt,
4901  "\t" . ' 0 ' . "\t",
4902  $conf,
4903  ],
4904  // non-empty cases
4905  'string is not empty' => ['string', 'string', $conf],
4906  '1 is not empty' => [1, 1, $conf],
4907  '-1 is not empty' => [-1, -1, $conf],
4908  '0.1 is not empty' => [0.1, 0.1, $conf],
4909  '-0.1 is not empty' => [-0.1, -0.1, $conf],
4910  'true is not empty' => [true, true, $conf],
4911  ];
4912  }
4913 
4927  #[DataProvider('stdWrap_ifEmptyDataProvider')]
4928  #[Test]
4929  public function ‪stdWrap_ifEmpty(mixed $expect, mixed $content, array $conf): void
4930  {
4931  $result = $this->subject->stdWrap_ifEmpty($content, $conf);
4932  self::assertSame($expect, $result);
4933  }
4934 
4940  public static function ‪stdWrap_ifNullDataProvider(): array
4941  {
4942  $alt = ‪StringUtility::getUniqueId('alternative content');
4943  $conf = ['ifNull' => $alt];
4944  return [
4945  'only null is null' => [$alt, null, $conf],
4946  'zero is not null' => [0, 0, $conf],
4947  'float zero is not null' => [0.0, 0.0, $conf],
4948  'false is not null' => [false, false, $conf],
4949  'zero string is not null' => ['0', '0', $conf],
4950  'empty string is not null' => ['', '', $conf],
4951  'whitespace is not null' => ["\t" . '', "\t" . '', $conf],
4952  ];
4953  }
4954 
4968  #[DataProvider('stdWrap_ifNullDataProvider')]
4969  #[Test]
4970  public function ‪stdWrap_ifNull(mixed $expect, mixed $content, array $conf): void
4971  {
4972  $result = $this->subject->stdWrap_ifNull($content, $conf);
4973  self::assertSame($expect, $result);
4974  }
4975 
4981  public static function ‪stdWrap_innerWrapDataProvider(): array
4982  {
4983  return [
4984  'no conf' => [
4985  'XXX',
4986  'XXX',
4987  [],
4988  ],
4989  'simple' => [
4990  '<wrap>XXX</wrap>',
4991  'XXX',
4992  ['innerWrap' => '<wrap>|</wrap>'],
4993  ],
4994  'missing pipe puts wrap before' => [
4995  '<pre>XXX',
4996  'XXX',
4997  ['innerWrap' => '<pre>'],
4998  ],
4999  'trims whitespace' => [
5000  '<wrap>XXX</wrap>',
5001  'XXX',
5002  ['innerWrap' => '<wrap>' . "\t" . ' | ' . "\t" . '</wrap>'],
5003  ],
5004  'split char change is not possible' => [
5005  '<wrap> # </wrap>XXX',
5006  'XXX',
5007  [
5008  'innerWrap' => '<wrap> # </wrap>',
5009  'innerWrap.' => ['splitChar' => '#'],
5010  ],
5011  ],
5012  ];
5013  }
5014 
5022  #[DataProvider('stdWrap_innerWrapDataProvider')]
5023  #[Test]
5024  public function ‪stdWrap_innerWrap(string $expected, string $input, array $conf): void
5025  {
5026  self::assertSame(
5027  $expected,
5028  $this->subject->stdWrap_innerWrap($input, $conf)
5029  );
5030  }
5031 
5037  public static function ‪stdWrap_innerWrap2DataProvider(): array
5038  {
5039  return [
5040  'no conf' => [
5041  'XXX',
5042  'XXX',
5043  [],
5044  ],
5045  'simple' => [
5046  '<wrap>XXX</wrap>',
5047  'XXX',
5048  ['innerWrap2' => '<wrap>|</wrap>'],
5049  ],
5050  'missing pipe puts wrap before' => [
5051  '<pre>XXX',
5052  'XXX',
5053  ['innerWrap2' => '<pre>'],
5054  ],
5055  'trims whitespace' => [
5056  '<wrap>XXX</wrap>',
5057  'XXX',
5058  ['innerWrap2' => '<wrap>' . "\t" . ' | ' . "\t" . '</wrap>'],
5059  ],
5060  'split char change is not possible' => [
5061  '<wrap> # </wrap>XXX',
5062  'XXX',
5063  [
5064  'innerWrap2' => '<wrap> # </wrap>',
5065  'innerWrap2.' => ['splitChar' => '#'],
5066  ],
5067  ],
5068  ];
5069  }
5070 
5078  #[DataProvider('stdWrap_innerWrap2DataProvider')]
5079  #[Test]
5080  public function ‪stdWrap_innerWrap2(string $expected, string $input, array $conf): void
5081  {
5082  self::assertSame(
5083  $expected,
5084  $this->subject->stdWrap_innerWrap2($input, $conf)
5085  );
5086  }
5087 
5097  #[Test]
5098  public function ‪stdWrap_insertData(): void
5099  {
5100  $content = ‪StringUtility::getUniqueId('content');
5101  $return = ‪StringUtility::getUniqueId('return');
5102  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5103  ->onlyMethods(['insertData'])->getMock();
5104  ‪$subject->expects(self::once())->method('insertData')
5105  ->with($content)->willReturn($return);
5106  self::assertSame(
5107  $return,
5109  );
5110  }
5111 
5117  public static function ‪stdWrap_insertDataProvider(): array
5118  {
5119  return [
5120  'empty' => ['', ''],
5121  'notFoundData' => ['any=1', 'any{$string}=1'],
5122  'queryParameter' => ['any{#string}=1', 'any{#string}=1'],
5123  ];
5124  }
5125 
5132  #[DataProvider('stdWrap_insertDataProvider')]
5133  #[Test]
5134  public function ‪stdWrap_insertDataAndInputExamples(mixed $expect, string $content): void
5135  {
5136  self::assertSame($expect, $this->subject->stdWrap_insertData($content));
5137  }
5138 
5144  public static function ‪stdWrap_intvalDataProvider(): array
5145  {
5146  return [
5147  // numbers
5148  'int' => [123, 123],
5149  'float' => [123, 123.45],
5150  'float does not round up' => [123, 123.55],
5151  // negative numbers
5152  'negative int' => [-123, -123],
5153  'negative float' => [-123, -123.45],
5154  'negative float does not round down' => [-123, -123.55],
5155  // strings
5156  'word string' => [0, 'string'],
5157  'empty string' => [0, ''],
5158  'zero string' => [0, '0'],
5159  'int string' => [123, '123'],
5160  'float string' => [123, '123.55'],
5161  'negative float string' => [-123, '-123.55'],
5162  // other types
5163  'null' => [0, null],
5164  'true' => [1, true],
5165  'false' => [0, false],
5166  ];
5167  }
5168 
5186  #[DataProvider('stdWrap_intvalDataProvider')]
5187  #[Test]
5188  public function ‪stdWrap_intval(int $expect, mixed $content): void
5189  {
5190  self::assertSame($expect, $this->subject->stdWrap_intval($content));
5191  }
5192 
5198  public static function ‪stdWrapKeywordsDataProvider(): array
5199  {
5200  return [
5201  'empty string' => ['', ''],
5202  'blank' => ['', ' '],
5203  'tab' => ['', "\t"],
5204  'single semicolon' => [',', ' ; '],
5205  'single comma' => [',', ' , '],
5206  'single nl' => [',', ' ' . PHP_EOL . ' '],
5207  'double semicolon' => [',,', ' ; ; '],
5208  'double comma' => [',,', ' , , '],
5209  'double nl' => [',,', ' ' . PHP_EOL . ' ' . PHP_EOL . ' '],
5210  'simple word' => ['one', ' one '],
5211  'simple word trimmed' => ['one', 'one'],
5212  ', separated' => ['one,two', ' one , two '],
5213  '; separated' => ['one,two', ' one ; two '],
5214  'nl separated' => ['one,two', ' one ' . PHP_EOL . ' two '],
5215  ', typical' => ['one,two,three', 'one, two, three'],
5216  '; typical' => ['one,two,three', ' one; two; three'],
5217  'nl typical' => [
5218  'one,two,three',
5219  'one' . PHP_EOL . 'two' . PHP_EOL . 'three',
5220  ],
5221  ', sourounded' => [',one,two,', ' , one , two , '],
5222  '; sourounded' => [',one,two,', ' ; one ; two ; '],
5223  'nl sourounded' => [
5224  ',one,two,',
5225  ' ' . PHP_EOL . ' one ' . PHP_EOL . ' two ' . PHP_EOL . ' ',
5226  ],
5227  'mixed' => [
5228  'one,two,three,four',
5229  ' one, two; three' . PHP_EOL . 'four',
5230  ],
5231  'keywods with blanks in words' => [
5232  'one plus,two minus',
5233  ' one plus , two minus ',
5234  ],
5235  ];
5236  }
5237 
5244  #[DataProvider('stdWrapKeywordsDataProvider')]
5245  #[Test]
5246  public function ‪stdWrap_keywords(string $expected, string $input): void
5247  {
5248  self::assertSame($expected, $this->subject->stdWrap_keywords($input));
5249  }
5250 
5256  public static function ‪stdWrap_langDataProvider(): array
5257  {
5258  return [
5259  'empty conf' => [
5260  'original',
5261  'original',
5262  [],
5263  'de_DE',
5264  ],
5265  'translation de' => [
5266  'Übersetzung',
5267  'original',
5268  [
5269  'lang.' => [
5270  'de' => 'Übersetzung',
5271  'it' => 'traduzione',
5272  ],
5273  ],
5274  'de_DE',
5275  ],
5276  'translation it' => [
5277  'traduzione',
5278  'original',
5279  [
5280  'lang.' => [
5281  'de' => 'Übersetzung',
5282  'it' => 'traduzione',
5283  ],
5284  ],
5285  'it_IT',
5286  ],
5287  'no translation' => [
5288  'original',
5289  'original',
5290  [
5291  'lang.' => [
5292  'de' => 'Übersetzung',
5293  'it' => 'traduzione',
5294  ],
5295  ],
5296  'en',
5297  ],
5298  'missing label' => [
5299  'original',
5300  'original',
5301  [
5302  'lang.' => [
5303  'de' => 'Übersetzung',
5304  'it' => 'traduzione',
5305  ],
5306  ],
5307  'fr_FR',
5308  ],
5309  ];
5310  }
5311 
5320  #[DataProvider('stdWrap_langDataProvider')]
5321  #[Test]
5322  public function ‪stdWrap_langViaSiteLanguage(string $expected, string $input, array $conf, string $language): void
5323  {
5324  $site = $this->‪createSiteWithLanguage([
5325  'base' => '/',
5326  'languageId' => 2,
5327  'locale' => $language,
5328  ]);
5329  $request = new ‪ServerRequest();
5330  $request = $request->withAttribute('language', $site->getLanguageById(2));
5331  $this->subject->setRequest($request);
5332  self::assertSame(
5333  $expected,
5334  $this->subject->stdWrap_lang($input, $conf)
5335  );
5336  }
5337 
5349  #[Test]
5350  public function ‪stdWrap_listNum(): void
5351  {
5352  $content = ‪StringUtility::getUniqueId('content');
5353  $conf = [
5354  'listNum' => ‪StringUtility::getUniqueId('listNum'),
5355  'listNum.' => [
5356  'splitChar' => ‪StringUtility::getUniqueId('splitChar'),
5357  ],
5358  ];
5359  $return = ‪StringUtility::getUniqueId('return');
5360  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5361  ->onlyMethods(['listNum'])->getMock();
5362  ‪$subject
5363  ->expects(self::once())
5364  ->method('listNum')
5365  ->with(
5366  $content,
5367  $conf['listNum'],
5368  $conf['listNum.']['splitChar']
5369  )
5370  ->willReturn($return);
5371  self::assertSame(
5372  $return,
5373  ‪$subject->‪stdWrap_listNum($content, $conf)
5374  );
5375  }
5376 
5382  public static function ‪stdWrap_noTrimWrapDataProvider(): array
5383  {
5384  return [
5385  'Standard case' => [
5386  ' left middle right ',
5387  'middle',
5388  [
5389  'noTrimWrap' => '| left | right |',
5390  ],
5391  ],
5392  'Tabs as whitespace' => [
5393  "\t" . 'left' . "\t" . 'middle' . "\t" . 'right' . "\t",
5394  'middle',
5395  [
5396  'noTrimWrap' =>
5397  '|' . "\t" . 'left' . "\t" . '|' . "\t" . 'right' . "\t" . '|',
5398  ],
5399  ],
5400  'Split char is 0' => [
5401  ' left middle right ',
5402  'middle',
5403  [
5404  'noTrimWrap' => '0 left 0 right 0',
5405  'noTrimWrap.' => ['splitChar' => '0'],
5406  ],
5407  ],
5408  'Split char is pipe (default)' => [
5409  ' left middle right ',
5410  'middle',
5411  [
5412  'noTrimWrap' => '| left | right |',
5413  'noTrimWrap.' => ['splitChar' => '|'],
5414  ],
5415  ],
5416  'Split char is a' => [
5417  ' left middle right ',
5418  'middle',
5419  [
5420  'noTrimWrap' => 'a left a right a',
5421  'noTrimWrap.' => ['splitChar' => 'a'],
5422  ],
5423  ],
5424  'Split char is a word (ab)' => [
5425  ' left middle right ',
5426  'middle',
5427  [
5428  'noTrimWrap' => 'ab left ab right ab',
5429  'noTrimWrap.' => ['splitChar' => 'ab'],
5430  ],
5431  ],
5432  'Split char accepts stdWrap' => [
5433  ' left middle right ',
5434  'middle',
5435  [
5436  'noTrimWrap' => 'abc left abc right abc',
5437  'noTrimWrap.' => [
5438  'splitChar' => 'b',
5439  'splitChar.' => ['wrap' => 'a|c'],
5440  ],
5441  ],
5442  ],
5443  ];
5444  }
5445 
5453  #[DataProvider('stdWrap_noTrimWrapDataProvider')]
5454  #[Test]
5455  public function ‪stdWrap_noTrimWrap(string $expect, string $content, array $conf): void
5456  {
5457  self::assertSame(
5458  $expect,
5459  $this->subject->stdWrap_noTrimWrap($content, $conf)
5460  );
5461  }
5462 
5472  #[Test]
5473  public function ‪stdWrap_numRows(): void
5474  {
5475  $conf = [
5476  'numRows' => ‪StringUtility::getUniqueId('numRows'),
5477  'numRows.' => [‪StringUtility::getUniqueId('numRows')],
5478  ];
5479  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5480  ->onlyMethods(['numRows'])->getMock();
5481  ‪$subject->expects(self::once())->method('numRows')
5482  ->with($conf['numRows.'])->willReturn('return');
5483  self::assertSame(
5484  'return',
5485  ‪$subject->‪stdWrap_numRows('discard', $conf)
5486  );
5487  }
5488 
5499  #[Test]
5500  public function ‪stdWrap_numberFormat(): void
5501  {
5502  $content = ‪StringUtility::getUniqueId('content');
5503  $conf = [
5504  'numberFormat' => ‪StringUtility::getUniqueId('not used'),
5505  'numberFormat.' => [‪StringUtility::getUniqueId('numberFormat.')],
5506  ];
5507  $return = ‪StringUtility::getUniqueId('return');
5508  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5509  ->onlyMethods(['numberFormat'])->getMock();
5510  ‪$subject
5511  ->expects(self::once())
5512  ->method('numberFormat')
5513  ->with((float)$content, $conf['numberFormat.'])
5514  ->willReturn($return);
5515  self::assertSame(
5516  $return,
5517  ‪$subject->‪stdWrap_numberFormat($content, $conf)
5518  );
5519  }
5520 
5526  public static function ‪stdWrap_outerWrapDataProvider(): array
5527  {
5528  return [
5529  'no conf' => [
5530  'XXX',
5531  'XXX',
5532  [],
5533  ],
5534  'simple' => [
5535  '<wrap>XXX</wrap>',
5536  'XXX',
5537  ['outerWrap' => '<wrap>|</wrap>'],
5538  ],
5539  'missing pipe puts wrap before' => [
5540  '<pre>XXX',
5541  'XXX',
5542  ['outerWrap' => '<pre>'],
5543  ],
5544  'trims whitespace' => [
5545  '<wrap>XXX</wrap>',
5546  'XXX',
5547  ['outerWrap' => '<wrap>' . "\t" . ' | ' . "\t" . '</wrap>'],
5548  ],
5549  'split char change is not possible' => [
5550  '<wrap> # </wrap>XXX',
5551  'XXX',
5552  [
5553  'outerWrap' => '<wrap> # </wrap>',
5554  'outerWrap.' => ['splitChar' => '#'],
5555  ],
5556  ],
5557  ];
5558  }
5559 
5567  #[DataProvider('stdWrap_outerWrapDataProvider')]
5568  #[Test]
5569  public function ‪stdWrap_outerWrap(string $expected, string $input, array $conf): void
5570  {
5571  self::assertSame(
5572  $expected,
5573  $this->subject->stdWrap_outerWrap($input, $conf)
5574  );
5575  }
5576 
5582  public static function ‪stdWrap_overrideDataProvider(): array
5583  {
5584  return [
5585  'standard case' => [
5586  'override',
5587  'content',
5588  ['override' => 'override'],
5589  ],
5590  'empty conf does not override' => [
5591  'content',
5592  'content',
5593  [],
5594  ],
5595  'empty string does not override' => [
5596  'content',
5597  'content',
5598  ['override' => ''],
5599  ],
5600  'whitespace does not override' => [
5601  'content',
5602  'content',
5603  ['override' => ' ' . "\t"],
5604  ],
5605  'zero does not override' => [
5606  'content',
5607  'content',
5608  ['override' => 0],
5609  ],
5610  'false does not override' => [
5611  'content',
5612  'content',
5613  ['override' => false],
5614  ],
5615  'null does not override' => [
5616  'content',
5617  'content',
5618  ['override' => null],
5619  ],
5620  'one does override' => [
5621  1,
5622  'content',
5623  ['override' => 1],
5624  ],
5625  'minus one does override' => [
5626  -1,
5627  'content',
5628  ['override' => -1],
5629  ],
5630  'float does override' => [
5631  -0.1,
5632  'content',
5633  ['override' => -0.1],
5634  ],
5635  'true does override' => [
5636  true,
5637  'content',
5638  ['override' => true],
5639  ],
5640  'the value is not trimmed' => [
5641  "\t" . 'override',
5642  'content',
5643  ['override' => "\t" . 'override'],
5644  ],
5645  ];
5646  }
5647 
5653  #[DataProvider('stdWrap_overrideDataProvider')]
5654  #[Test]
5655  public function ‪stdWrap_override(mixed $expect, string $content, array $conf): void
5656  {
5657  self::assertSame(
5658  $expect,
5659  $this->subject->stdWrap_override($content, $conf)
5660  );
5661  }
5662 
5674  #[Test]
5675  public function ‪stdWrap_parseFunc(): void
5676  {
5677  $content = ‪StringUtility::getUniqueId('content');
5678  $conf = [
5679  'parseFunc' => ‪StringUtility::getUniqueId('parseFunc'),
5680  'parseFunc.' => [‪StringUtility::getUniqueId('parseFunc.')],
5681  ];
5682  $return = ‪StringUtility::getUniqueId('return');
5683  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5684  ->onlyMethods(['parseFunc'])->getMock();
5685  ‪$subject
5686  ->expects(self::once())
5687  ->method('parseFunc')
5688  ->with($content, $conf['parseFunc.'], $conf['parseFunc'])
5689  ->willReturn($return);
5690  self::assertSame(
5691  $return,
5692  ‪$subject->‪stdWrap_parseFunc($content, $conf)
5693  );
5694  }
5695 
5707  #[Test]
5708  public function ‪stdWrap_postCObject(): void
5709  {
5710  $debugKey = '/stdWrap/.postCObject';
5711  $content = ‪StringUtility::getUniqueId('content');
5712  $conf = [
5713  'postCObject' => ‪StringUtility::getUniqueId('postCObject'),
5714  'postCObject.' => [‪StringUtility::getUniqueId('postCObject.')],
5715  ];
5716  $return = ‪StringUtility::getUniqueId('return');
5717  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5718  ->onlyMethods(['cObjGetSingle'])->getMock();
5719  ‪$subject
5720  ->expects(self::once())
5721  ->method('cObjGetSingle')
5722  ->with($conf['postCObject'], $conf['postCObject.'], $debugKey)
5723  ->willReturn($return);
5724  self::assertSame(
5725  $content . $return,
5726  ‪$subject->‪stdWrap_postCObject($content, $conf)
5727  );
5728  }
5729 
5739  #[Test]
5740  public function ‪stdWrap_postUserFunc(): void
5741  {
5742  $content = ‪StringUtility::getUniqueId('content');
5743  $conf = [
5744  'postUserFunc' => ‪StringUtility::getUniqueId('postUserFunc'),
5745  'postUserFunc.' => [‪StringUtility::getUniqueId('postUserFunc.')],
5746  ];
5747  $return = ‪StringUtility::getUniqueId('return');
5748  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5749  ->onlyMethods(['callUserFunction'])->getMock();
5750  ‪$subject
5751  ->expects(self::once())
5752  ->method('callUserFunction')
5753  ->with($conf['postUserFunc'], $conf['postUserFunc.'])
5754  ->willReturn($return);
5755  self::assertSame(
5756  $return,
5757  ‪$subject->‪stdWrap_postUserFunc($content, $conf)
5758  );
5759  }
5760 
5777  #[Test]
5778  public function ‪stdWrap_postUserFuncInt(): void
5779  {
5780  $uniqueHash = ‪StringUtility::getUniqueId('uniqueHash');
5781  $substKey = 'INT_SCRIPT.' . $uniqueHash;
5782  $content = ‪StringUtility::getUniqueId('content');
5783  $conf = [
5784  'postUserFuncInt' => ‪StringUtility::getUniqueId('function'),
5785  'postUserFuncInt.' => [‪StringUtility::getUniqueId('function array')],
5786  ];
5787  $expect = '<!--' . $substKey . '-->';
5788  $frontend = $this->getMockBuilder(TypoScriptFrontendController::class)
5789  ->disableOriginalConstructor()->onlyMethods(['uniqueHash'])
5790  ->getMock();
5791  $frontend->expects(self::once())->method('uniqueHash')
5792  ->with()->willReturn($uniqueHash);
5793  $frontend->config = ['INTincScript' => []];
5794  ‪$subject = $this->getAccessibleMock(
5795  ContentObjectRenderer::class,
5796  null,
5797  [$frontend]
5798  );
5799  self::assertSame(
5800  $expect,
5801  ‪$subject->‪stdWrap_postUserFuncInt($content, $conf)
5802  );
5803  $array = [
5804  'content' => $content,
5805  'postUserFunc' => $conf['postUserFuncInt'],
5806  'conf' => $conf['postUserFuncInt.'],
5807  'type' => 'POSTUSERFUNC',
5808  'cObj' => serialize(‪$subject),
5809  ];
5810  self::assertSame(
5811  $array,
5812  $frontend->config['INTincScript'][$substKey]
5813  );
5814  }
5815 
5827  #[Test]
5828  public function ‪stdWrap_preCObject(): void
5829  {
5830  $debugKey = '/stdWrap/.preCObject';
5831  $content = ‪StringUtility::getUniqueId('content');
5832  $conf = [
5833  'preCObject' => ‪StringUtility::getUniqueId('preCObject'),
5834  'preCObject.' => [‪StringUtility::getUniqueId('preCObject.')],
5835  ];
5836  $return = ‪StringUtility::getUniqueId('return');
5837  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5838  ->onlyMethods(['cObjGetSingle'])->getMock();
5839  ‪$subject
5840  ->expects(self::once())
5841  ->method('cObjGetSingle')
5842  ->with($conf['preCObject'], $conf['preCObject.'], $debugKey)
5843  ->willReturn($return);
5844  self::assertSame(
5845  $return . $content,
5846  ‪$subject->‪stdWrap_preCObject($content, $conf)
5847  );
5848  }
5849 
5861  #[Test]
5862  public function ‪stdWrap_preIfEmptyListNum(): void
5863  {
5864  $content = ‪StringUtility::getUniqueId('content');
5865  $conf = [
5866  'preIfEmptyListNum' => ‪StringUtility::getUniqueId('preIfEmptyListNum'),
5867  'preIfEmptyListNum.' => [
5868  'splitChar' => ‪StringUtility::getUniqueId('splitChar'),
5869  ],
5870  ];
5871  $return = ‪StringUtility::getUniqueId('return');
5872  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5873  ->onlyMethods(['listNum'])->getMock();
5874  ‪$subject
5875  ->expects(self::once())
5876  ->method('listNum')
5877  ->with(
5878  $content,
5879  $conf['preIfEmptyListNum'],
5880  $conf['preIfEmptyListNum.']['splitChar']
5881  )
5882  ->willReturn($return);
5883  self::assertSame(
5884  $return,
5885  ‪$subject->‪stdWrap_preIfEmptyListNum($content, $conf)
5886  );
5887  }
5888 
5894  public static function ‪stdWrap_prefixCommentDataProvider(): array
5895  {
5896  $content = ‪StringUtility::getUniqueId('content');
5897  $will = ‪StringUtility::getUniqueId('will');
5898  $conf = [];
5899  $conf['prefixComment'] = ‪StringUtility::getUniqueId('prefixComment');
5900  $emptyConf1 = [];
5901  $emptyConf2 = [];
5902  $emptyConf2['prefixComment'] = '';
5903  return [
5904  'standard case' => [$will, $content, $conf, false, 1, $will],
5905  'emptyConf1' => [$content, $content, $emptyConf1, false, 0, $will],
5906  'emptyConf2' => [$content, $content, $emptyConf2, false, 0, $will],
5907  'disabled by bool' => [$content, $content, $conf, true, 0, $will],
5908  'disabled by int' => [$content, $content, $conf, 1, 0, $will],
5909  ];
5910  }
5911 
5926  #[DataProvider('stdWrap_prefixCommentDataProvider')]
5927  #[Test]
5928  public function ‪stdWrap_prefixComment(
5929  string $expect,
5930  string $content,
5931  array $conf,
5932  int|bool $disable,
5933  int $times,
5934  string $will
5935  ): void {
5936  $typoScript = new ‪FrontendTypoScript(new ‪RootNode(), []);
5937  $typoScript->setConfigArray([
5938  'disablePrefixComment' => $disable,
5939  ]);
5940  $request = (new ‪ServerRequest())->withAttribute('frontend.typoscript', $typoScript);
5941  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)->onlyMethods(['prefixComment'])->getMock();
5942  ‪$subject->‪setRequest($request);
5943  ‪$subject->expects(self::exactly($times))
5944  ->method('prefixComment')
5945  ->with($conf['prefixComment'] ?? null, [], $content)
5946  ->willReturn($will);
5947  self::assertSame(
5948  $expect,
5949  ‪$subject->‪stdWrap_prefixComment($content, $conf)
5950  );
5951  }
5952 
5964  #[Test]
5965  public function ‪stdWrap_prepend(): void
5966  {
5967  $debugKey = '/stdWrap/.prepend';
5968  $content = ‪StringUtility::getUniqueId('content');
5969  $conf = [
5970  'prepend' => ‪StringUtility::getUniqueId('prepend'),
5971  'prepend.' => [‪StringUtility::getUniqueId('prepend.')],
5972  ];
5973  $return = ‪StringUtility::getUniqueId('return');
5974  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
5975  ->onlyMethods(['cObjGetSingle'])->getMock();
5976  ‪$subject
5977  ->expects(self::once())
5978  ->method('cObjGetSingle')
5979  ->with($conf['prepend'], $conf['prepend.'], $debugKey)
5980  ->willReturn($return);
5981  self::assertSame(
5982  $return . $content,
5983  ‪$subject->‪stdWrap_prepend($content, $conf)
5984  );
5985  }
5986 
5992  public static function ‪stdWrap_prioriCalcDataProvider(): array
5993  {
5994  return [
5995  'priority of *' => ['7', '1 + 2 * 3', []],
5996  'priority of parentheses' => ['9', '(1 + 2) * 3', []],
5997  'float' => ['1.5', '3/2', []],
5998  'intval casts to int' => [1, '3/2', ['prioriCalc' => 'intval']],
5999  'intval does not round' => [2, '2.7', ['prioriCalc' => 'intval']],
6000  ];
6001  }
6002 
6021  #[DataProvider('stdWrap_prioriCalcDataProvider')]
6022  #[Test]
6023  public function ‪stdWrap_prioriCalc(mixed $expect, string $content, array $conf): void
6024  {
6025  $result = $this->subject->stdWrap_prioriCalc($content, $conf);
6026  self::assertSame($expect, $result);
6027  }
6028 
6040  #[Test]
6041  public function ‪stdWrap_preUserFunc(): void
6042  {
6043  $content = ‪StringUtility::getUniqueId('content');
6044  $conf = [
6045  'preUserFunc' => ‪StringUtility::getUniqueId('preUserFunc'),
6046  'preUserFunc.' => [‪StringUtility::getUniqueId('preUserFunc.')],
6047  ];
6048  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6049  ->onlyMethods(['callUserFunction'])->getMock();
6050  ‪$subject->expects(self::once())->method('callUserFunction')
6051  ->with($conf['preUserFunc'], $conf['preUserFunc.'], $content)
6052  ->willReturn('return');
6053  self::assertSame(
6054  'return',
6055  ‪$subject->‪stdWrap_preUserFunc($content, $conf)
6056  );
6057  }
6058 
6064  public static function ‪stdWrap_rawUrlEncodeDataProvider(): array
6065  {
6066  return [
6067  'https://typo3.org?id=10' => [
6068  'https%3A%2F%2Ftypo3.org%3Fid%3D10',
6069  'https://typo3.org?id=10',
6070  ],
6071  'https://typo3.org?id=10&foo=bar' => [
6072  'https%3A%2F%2Ftypo3.org%3Fid%3D10%26foo%3Dbar',
6073  'https://typo3.org?id=10&foo=bar',
6074  ],
6075  ];
6076  }
6077 
6084  #[DataProvider('stdWrap_rawUrlEncodeDataProvider')]
6085  #[Test]
6086  public function ‪stdWrap_rawUrlEncode(string $expect, string $content): void
6087  {
6088  self::assertSame(
6089  $expect,
6090  $this->subject->stdWrap_rawUrlEncode($content)
6091  );
6092  }
6093 
6104  #[Test]
6105  public function ‪stdWrap_replacement(): void
6106  {
6107  $content = ‪StringUtility::getUniqueId('content');
6108  $conf = [
6109  'replacement' => ‪StringUtility::getUniqueId('not used'),
6110  'replacement.' => [‪StringUtility::getUniqueId('replacement.')],
6111  ];
6112  $return = ‪StringUtility::getUniqueId('return');
6113  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6114  ->onlyMethods(['replacement'])->getMock();
6115  ‪$subject
6116  ->expects(self::once())
6117  ->method('replacement')
6118  ->with($content, $conf['replacement.'])
6119  ->willReturn($return);
6120  self::assertSame(
6121  $return,
6122  ‪$subject->‪stdWrap_replacement($content, $conf)
6123  );
6124  }
6125 
6131  public static function ‪stdWrap_requiredDataProvider(): array
6132  {
6133  return [
6134  // empty content
6135  'empty string is empty' => ['', true, ''],
6136  'null is empty' => ['', true, null],
6137  'false is empty' => ['', true, false],
6138 
6139  // non-empty content
6140  'blank is not empty' => [' ', false, ' '],
6141  'tab is not empty' => ["\t", false, "\t"],
6142  'linebreak is not empty' => [PHP_EOL, false, PHP_EOL],
6143  '"0" is not empty' => ['0', false, '0'],
6144  '0 is not empty' => [0, false, 0],
6145  '1 is not empty' => [1, false, 1],
6146  'true is not empty' => [true, false, true],
6147  ];
6148  }
6149 
6163  #[DataProvider('stdWrap_requiredDataProvider')]
6164  #[Test]
6165  public function ‪stdWrap_required(mixed $expect, bool $stop, mixed $content): void
6166  {
6168  ‪$subject->_set('stdWrapRecursionLevel', 1);
6169  ‪$subject->_set('stopRendering', [1 => false]);
6170  self::assertSame($expect, ‪$subject->‪stdWrap_required($content));
6171  self::assertSame($stop, ‪$subject->_get('stopRendering')[1]);
6172  }
6173 
6184  #[Test]
6185  public function ‪stdWrap_round(): void
6186  {
6187  $content = ‪StringUtility::getUniqueId('content');
6188  $conf = [
6189  'round' => ‪StringUtility::getUniqueId('not used'),
6190  'round.' => [‪StringUtility::getUniqueId('round.')],
6191  ];
6192  $return = ‪StringUtility::getUniqueId('return');
6193  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6194  ->onlyMethods(['round'])->getMock();
6195  ‪$subject
6196  ->expects(self::once())
6197  ->method('round')
6198  ->with($content, $conf['round.'])
6199  ->willReturn($return);
6200  self::assertSame($return, ‪$subject->‪stdWrap_round($content, $conf));
6201  }
6202 
6206  #[Test]
6207  public function ‪stdWrap_setContentToCurrent(): void
6208  {
6209  $pageInformation = new ‪PageInformation();
6210  $pageInformation->setPageRecord([]);
6211  $request = new ‪ServerRequest('https://example.com');
6212  $request = $request->withAttribute('frontend.page.information', $pageInformation);
6213  $this->subject->setRequest($request);
6214 
6215  $content = ‪StringUtility::getUniqueId('content');
6216  self::assertNotSame($content, $this->subject->getData('current'));
6217  self::assertSame(
6218  $content,
6219  $this->subject->stdWrap_setContentToCurrent($content)
6220  );
6221  self::assertSame($content, $this->subject->getData('current'));
6222  }
6223 
6229  public static function ‪stdWrap_setCurrentDataProvider(): array
6230  {
6231  return [
6232  'no conf' => [
6233  'content',
6234  [],
6235  ],
6236  'empty string' => [
6237  'content',
6238  ['setCurrent' => ''],
6239  ],
6240  'non-empty string' => [
6241  'content',
6242  ['setCurrent' => 'xxx'],
6243  ],
6244  'integer null' => [
6245  'content',
6246  ['setCurrent' => 0],
6247  ],
6248  'integer not null' => [
6249  'content',
6250  ['setCurrent' => 1],
6251  ],
6252  'boolean true' => [
6253  'content',
6254  ['setCurrent' => true],
6255  ],
6256  'boolean false' => [
6257  'content',
6258  ['setCurrent' => false],
6259  ],
6260  ];
6261  }
6262 
6269  #[DataProvider('stdWrap_setCurrentDataProvider')]
6270  #[Test]
6271  public function ‪stdWrap_setCurrent(string $input, array $conf): void
6272  {
6273  $pageInformation = new ‪PageInformation();
6274  $pageInformation->setPageRecord([]);
6275  $request = new ‪ServerRequest('https://example.com');
6276  $request = $request->withAttribute('frontend.page.information', $pageInformation);
6277  $this->subject->setRequest($request);
6278 
6279  if (isset($conf['setCurrent'])) {
6280  self::assertNotSame($conf['setCurrent'], $this->subject->getData('current'));
6281  }
6282  self::assertSame($input, $this->subject->stdWrap_setCurrent($input, $conf));
6283  if (isset($conf['setCurrent'])) {
6284  self::assertSame($conf['setCurrent'], $this->subject->getData('current'));
6285  }
6286  }
6287 
6298  #[Test]
6299  public function ‪stdWrap_split(): void
6300  {
6301  $content = ‪StringUtility::getUniqueId('content');
6302  $conf = [
6303  'split' => ‪StringUtility::getUniqueId('not used'),
6304  'split.' => [‪StringUtility::getUniqueId('split.')],
6305  ];
6306  $return = ‪StringUtility::getUniqueId('return');
6307  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6308  ->onlyMethods(['splitObj'])->getMock();
6309  ‪$subject
6310  ->expects(self::once())
6311  ->method('splitObj')
6312  ->with($content, $conf['split.'])
6313  ->willReturn($return);
6314  self::assertSame(
6315  $return,
6316  ‪$subject->‪stdWrap_split($content, $conf)
6317  );
6318  }
6319 
6329  #[Test]
6330  public function ‪stdWrap_stdWrap(): void
6331  {
6332  $content = ‪StringUtility::getUniqueId('content');
6333  $conf = [
6334  'stdWrap' => ‪StringUtility::getUniqueId('not used'),
6335  'stdWrap.' => [‪StringUtility::getUniqueId('stdWrap.')],
6336  ];
6337  $return = ‪StringUtility::getUniqueId('return');
6338  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6339  ->onlyMethods(['stdWrap'])->getMock();
6340  ‪$subject
6341  ->expects(self::once())
6342  ->method('stdWrap')
6343  ->with($content, $conf['stdWrap.'])
6344  ->willReturn($return);
6345  self::assertSame($return, ‪$subject->‪stdWrap_stdWrap($content, $conf));
6346  }
6347 
6351  public static function ‪stdWrap_stdWrapValueDataProvider(): array
6352  {
6353  return [
6354  'only key returns value' => [
6355  'ifNull',
6356  [
6357  'ifNull' => '1',
6358  ],
6359  '',
6360  '1',
6361  ],
6362  'array without key returns empty string' => [
6363  'ifNull',
6364  [
6365  'ifNull.' => '1',
6366  ],
6367  '',
6368  '',
6369  ],
6370  'array without key returns default' => [
6371  'ifNull',
6372  [
6373  'ifNull.' => '1',
6374  ],
6375  'default',
6376  'default',
6377  ],
6378  'non existing key returns default' => [
6379  'ifNull',
6380  [
6381  'noTrimWrap' => 'test',
6382  'noTrimWrap.' => '1',
6383  ],
6384  'default',
6385  'default',
6386  ],
6387  'default value null is returned' => [
6388  'ifNull',
6389  [],
6390  null,
6391  null,
6392  ],
6393  'existing key and array returns stdWrap' => [
6394  'test',
6395  [
6396  'test' => 'value',
6397  'test.' => ['case' => 'upper'],
6398  ],
6399  'default',
6400  'VALUE',
6401  ],
6402  'the string "0" from stdWrap will be returned' => [
6403  'test',
6404  [
6405  'test' => '',
6406  'test.' => [
6407  'wrap' => '|0',
6408  ],
6409  ],
6410  '100',
6411  '0',
6412  ],
6413  ];
6414  }
6415 
6416  #[DataProvider('stdWrap_stdWrapValueDataProvider')]
6417  #[Test]
6418  public function ‪stdWrap_stdWrapValue(
6419  string $key,
6420  array $configuration,
6421  ?string $defaultValue,
6422  ?string $expected
6423  ): void {
6424  $result = $this->subject->stdWrapValue($key, $configuration, $defaultValue);
6425  self::assertSame($expected, $result);
6426  }
6427 
6433  public static function ‪stdWrap_strPadDataProvider(): array
6434  {
6435  return [
6436  'pad string with default settings and length 10' => [
6437  'Alien ',
6438  'Alien',
6439  [
6440  'length' => '10',
6441  ],
6442  ],
6443  'pad string with default settings and length 10 and multibyte character' => [
6444  'Älien ',
6445  'Älien',
6446  [
6447  'length' => '10',
6448  ],
6449  ],
6450  'pad string with padWith -= and type left and length 10' => [
6451  '-=-=-Alien',
6452  'Alien',
6453  [
6454  'length' => '10',
6455  'padWith' => '-=',
6456  'type' => 'left',
6457  ],
6458  ],
6459  'pad string with padWith äö and type left and length 10 and multibyte characters' => [
6460  'äöäöäÄlien',
6461  'Älien',
6462  [
6463  'length' => '10',
6464  'padWith' => 'äö',
6465  'type' => 'left',
6466  ],
6467  ],
6468  'pad string with padWith _ and type both and length 10' => [
6469  '__Alien___',
6470  'Alien',
6471  [
6472  'length' => '10',
6473  'padWith' => '_',
6474  'type' => 'both',
6475  ],
6476  ],
6477  'pad string with padWith 0 and type both and length 10' => [
6478  '00Alien000',
6479  'Alien',
6480  [
6481  'length' => '10',
6482  'padWith' => '0',
6483  'type' => 'both',
6484  ],
6485  ],
6486  'pad string with padWith ___ and type both and length 6' => [
6487  'Alien_',
6488  'Alien',
6489  [
6490  'length' => '6',
6491  'padWith' => '___',
6492  'type' => 'both',
6493  ],
6494  ],
6495  'pad string with padWith _ and type both and length 12, using stdWrap for length' => [
6496  '___Alien____',
6497  'Alien',
6498  [
6499  'length' => '1',
6500  'length.' => [
6501  'wrap' => '|2',
6502  ],
6503  'padWith' => '_',
6504  'type' => 'both',
6505  ],
6506  ],
6507  'pad string with padWith _ and type both and length 12, using stdWrap for padWidth' => [
6508  '-_=Alien-_=-',
6509  'Alien',
6510  [
6511  'length' => '12',
6512  'padWith' => '_',
6513  'padWith.' => [
6514  'wrap' => '-|=',
6515  ],
6516  'type' => 'both',
6517  ],
6518  ],
6519  'pad string with padWith _ and type both and length 12, using stdWrap for type' => [
6520  '_______Alien',
6521  'Alien',
6522  [
6523  'length' => '12',
6524  'padWith' => '_',
6525  'type' => 'both',
6526  // make type become "left"
6527  'type.' => [
6528  'substring' => '2,1',
6529  'wrap' => 'lef|',
6530  ],
6531  ],
6532  ],
6533  ];
6534  }
6535 
6543  #[DataProvider('stdWrap_strPadDataProvider')]
6544  #[Test]
6545  public function ‪stdWrap_strPad(string $expect, string $content, array $conf): void
6546  {
6547  $conf = ['strPad.' => $conf];
6548  $result = $this->subject->stdWrap_strPad($content, $conf);
6549  self::assertSame($expect, $result);
6550  }
6551 
6557  public static function ‪stdWrap_strftimeDataProvider(): array
6558  {
6559  // Fictive execution time is 2012-09-01 12:00 in UTC/GMT.
6560  $now = 1346500800;
6561  return [
6562  'given timestamp' => [
6563  '01-09-2012',
6564  $now,
6565  ['strftime' => '%d-%m-%Y'],
6566  $now,
6567  ],
6568  'empty string' => [
6569  '01-09-2012',
6570  '',
6571  ['strftime' => '%d-%m-%Y'],
6572  $now,
6573  ],
6574  'testing null' => [
6575  '01-09-2012',
6576  null,
6577  ['strftime' => '%d-%m-%Y'],
6578  $now,
6579  ],
6580  ];
6581  }
6582 
6591  #[DataProvider('stdWrap_strftimeDataProvider')]
6592  #[Test]
6593  public function ‪stdWrap_strftime(string $expect, mixed $content, array $conf, int $now): void
6594  {
6595  // Save current timezone and set to UTC to make the system under test
6596  // behave the same in all server timezone settings
6597  $timezoneBackup = date_default_timezone_get();
6598  date_default_timezone_set('UTC');
6599 
6600  ‪$GLOBALS['EXEC_TIME'] = $now;
6601  $result = $this->subject->stdWrap_strftime($content, $conf);
6602 
6603  // Reset timezone
6604  date_default_timezone_set($timezoneBackup);
6605 
6606  self::assertSame($expect, $result);
6607  }
6608 
6612  #[Test]
6613  public function ‪stdWrap_stripHtml(): void
6614  {
6615  $content = '<html><p>Hello <span class="inline">inline tag<span>!</p><p>Hello!</p></html>';
6616  $expected = 'Hello inline tag!Hello!';
6617  self::assertSame($expected, $this->subject->stdWrap_stripHtml($content));
6618  }
6619 
6625  public static function ‪stdWrap_strtotimeDataProvider(): array
6626  {
6627  return [
6628  'date from content' => [
6629  1417651200,
6630  '2014-12-04',
6631  ['strtotime' => '1'],
6632  ],
6633  'manipulation of date from content' => [
6634  1417996800,
6635  '2014-12-04',
6636  ['strtotime' => '+ 2 weekdays'],
6637  ],
6638  'date from configuration' => [
6639  1417651200,
6640  '',
6641  ['strtotime' => '2014-12-04'],
6642  ],
6643  'manipulation of date from configuration' => [
6644  1417996800,
6645  '',
6646  ['strtotime' => '2014-12-04 + 2 weekdays'],
6647  ],
6648  'empty input' => [
6649  false,
6650  '',
6651  ['strtotime' => '1'],
6652  ],
6653  'date from content and configuration' => [
6654  false,
6655  '2014-12-04',
6656  ['strtotime' => '2014-12-05'],
6657  ],
6658  ];
6659  }
6660 
6668  #[DataProvider('stdWrap_strtotimeDataProvider')]
6669  #[Test]
6670  public function ‪stdWrap_strtotime(mixed $expect, string $content, array $conf): void
6671  {
6672  // Set exec_time to a hard timestamp
6673  ‪$GLOBALS['EXEC_TIME'] = 1417392000;
6674  // Save current timezone and set to UTC to make the system under test
6675  // behave the same in all server timezone settings
6676  $timezoneBackup = date_default_timezone_get();
6677  date_default_timezone_set('UTC');
6678 
6679  $result = $this->subject->stdWrap_strtotime($content, $conf);
6680 
6681  // Reset timezone
6682  date_default_timezone_set($timezoneBackup);
6683 
6684  self::assertEquals($expect, $result);
6685  }
6686 
6697  #[Test]
6698  public function ‪stdWrap_substring(): void
6699  {
6700  $content = ‪StringUtility::getUniqueId('content');
6701  $conf = [
6702  'substring' => ‪StringUtility::getUniqueId('substring'),
6703  'substring.' => ‪StringUtility::getUniqueId('not used'),
6704  ];
6705  $return = ‪StringUtility::getUniqueId('return');
6706  ‪$subject = $this->getMockBuilder(ContentObjectRenderer::class)
6707  ->onlyMethods(['substring'])->getMock();
6708