[TASK] Make TypoScriptParserTest.php notice free
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / TypoScript / Parser / TypoScriptParserTest.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
20 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
23 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
24
25 /**
26 * Test case
27 */
28 class TypoScriptParserTest extends UnitTestCase
29 {
30 /**
31 * @var TypoScriptParser|AccessibleObjectInterface
32 */
33 protected $typoScriptParser;
34
35 /**
36 * Set up
37 */
38 protected function setUp(): void
39 {
40 $accessibleClassName = $this->buildAccessibleProxy(TypoScriptParser::class);
41 $this->typoScriptParser = new $accessibleClassName();
42 }
43
44 /**
45 *
46 */
47 protected function tearDown(): void
48 {
49 GeneralUtility::purgeInstances();
50 parent::tearDown();
51 }
52
53 /**
54 * Data provider for executeValueModifierReturnsModifiedResult
55 *
56 * @return array modifier name, modifier arguments, current value, expected result
57 */
58 public function executeValueModifierDataProvider(): array
59 {
60 return [
61 'prependString with string' => [
62 'prependString',
63 'abc',
64 '!',
65 '!abc'
66 ],
67 'prependString with empty string' => [
68 'prependString',
69 'foo',
70 '',
71 'foo',
72 ],
73 'appendString with string' => [
74 'appendString',
75 'abc',
76 '!',
77 'abc!',
78 ],
79 'appendString with empty string' => [
80 'appendString',
81 'abc',
82 '',
83 'abc',
84 ],
85 'removeString removes simple string' => [
86 'removeString',
87 'abcdef',
88 'bc',
89 'adef',
90 ],
91 'removeString removes nothing if no match' => [
92 'removeString',
93 'abcdef',
94 'foo',
95 'abcdef',
96 ],
97 'removeString removes multiple matches' => [
98 'removeString',
99 'FooBarFoo',
100 'Foo',
101 'Bar',
102 ],
103 'replaceString replaces simple match' => [
104 'replaceString',
105 'abcdef',
106 'bc|123',
107 'a123def',
108 ],
109 'replaceString replaces simple match with nothing' => [
110 'replaceString',
111 'abcdef',
112 'bc',
113 'adef',
114 ],
115 'replaceString replaces multiple matches' => [
116 'replaceString',
117 'FooBarFoo',
118 'Foo|Bar',
119 'BarBarBar',
120 ],
121 'addToList adds at end of existing list' => [
122 'addToList',
123 '123,456',
124 '789',
125 '123,456,789',
126 ],
127 'addToList adds at end of existing list including white-spaces' => [
128 'addToList',
129 '123,456',
130 ' 789 , 32 , 12 ',
131 '123,456, 789 , 32 , 12 ',
132 ],
133 'addToList adds nothing' => [
134 'addToList',
135 '123,456',
136 '',
137 '123,456,', // This result is probably not what we want (appended comma) ... fix it?
138 ],
139 'addToList adds to empty list' => [
140 'addToList',
141 '',
142 'foo',
143 'foo',
144 ],
145 'removeFromList removes value from list' => [
146 'removeFromList',
147 '123,456,789,abc',
148 '456',
149 '123,789,abc',
150 ],
151 'removeFromList removes value at beginning of list' => [
152 'removeFromList',
153 '123,456,abc',
154 '123',
155 '456,abc',
156 ],
157 'removeFromList removes value at end of list' => [
158 'removeFromList',
159 '123,456,abc',
160 'abc',
161 '123,456',
162 ],
163 'removeFromList removes multiple values from list' => [
164 'removeFromList',
165 'foo,123,bar,123',
166 '123',
167 'foo,bar',
168 ],
169 'removeFromList removes empty values' => [
170 'removeFromList',
171 'foo,,bar',
172 '',
173 'foo,bar',
174 ],
175 'uniqueList removes duplicates' => [
176 'uniqueList',
177 '123,456,abc,456,456',
178 '',
179 '123,456,abc',
180 ],
181 'uniqueList removes duplicate empty list values' => [
182 'uniqueList',
183 '123,,456,,abc',
184 '',
185 '123,,456,abc',
186 ],
187 'reverseList returns list reversed' => [
188 'reverseList',
189 '123,456,abc,456',
190 '',
191 '456,abc,456,123',
192 ],
193 'reverseList keeps empty values' => [
194 'reverseList',
195 ',123,,456,abc,,456',
196 '',
197 '456,,abc,456,,123,',
198 ],
199 'reverseList does not change single element' => [
200 'reverseList',
201 '123',
202 '',
203 '123',
204 ],
205 'sortList sorts a list' => [
206 'sortList',
207 '10,100,0,20,abc',
208 '',
209 '0,10,20,100,abc',
210 ],
211 'sortList sorts a list numeric' => [
212 'sortList',
213 '10,0,100,-20',
214 'numeric',
215 '-20,0,10,100',
216 ],
217 'sortList sorts a list descending' => [
218 'sortList',
219 '10,100,0,20,abc,-20',
220 'descending',
221 'abc,100,20,10,0,-20',
222 ],
223 'sortList sorts a list numeric descending' => [
224 'sortList',
225 '10,100,0,20,-20',
226 'descending,numeric',
227 '100,20,10,0,-20',
228 ],
229 'sortList ignores invalid modifier arguments' => [
230 'sortList',
231 '10,100,20',
232 'foo,descending,bar',
233 '100,20,10',
234 ],
235 ];
236 }
237
238 /**
239 * @test
240 * @dataProvider executeValueModifierDataProvider
241 * @param string $modifierName
242 * @param string $currentValue
243 * @param string $modifierArgument
244 * @param string $expected
245 */
246 public function executeValueModifierReturnsModifiedResult(
247 string $modifierName,
248 string $currentValue,
249 string $modifierArgument,
250 string $expected
251 ): void {
252 $actualValue = $this->typoScriptParser->_call(
253 'executeValueModifier',
254 $modifierName,
255 $modifierArgument,
256 $currentValue
257 );
258 $this->assertEquals($expected, $actualValue);
259 }
260
261 /**
262 * Data provider for executeValueModifierThrowsException
263 *
264 * @return array modifier name, modifier arguments, current value, expected result
265 */
266 public function executeValueModifierInvalidDataProvider(): array
267 {
268 return [
269 'sortList sorts a list numeric' => [
270 'sortList',
271 '10,0,100,-20,abc',
272 'numeric',
273 ],
274 'sortList sorts a list numeric descending' => [
275 'sortList',
276 '10,100,0,20,abc,-20',
277 'descending,numeric',
278 ],
279 ];
280 }
281
282 /**
283 * @test
284 * @dataProvider executeValueModifierInvalidDataProvider
285 * @param string $modifierName
286 * @param string $currentValue
287 * @param string $modifierArgument
288 */
289 public function executeValueModifierThrowsException(
290 string $modifierName,
291 string $currentValue,
292 string $modifierArgument
293 ): void {
294 $this->expectException(\InvalidArgumentException::class);
295 $this->expectExceptionCode(1438191758);
296 $this->typoScriptParser->_call('executeValueModifier', $modifierName, $modifierArgument, $currentValue);
297 }
298
299 /**
300 * @test
301 */
302 public function invalidCharactersInObjectNamesAreReported(): void
303 {
304 $timeTrackerProphecy = $this->prophesize(TimeTracker::class);
305 GeneralUtility::setSingletonInstance(TimeTracker::class, $timeTrackerProphecy->reveal());
306
307 $typoScript = '$.10 = invalid';
308 $this->typoScriptParser->parse($typoScript);
309 $expected = 'Line 0: Object Name String, "$.10" contains invalid character "$". Must be alphanumeric or one of: "_:-\."';
310 $this->assertEquals($expected, $this->typoScriptParser->errors[0][0]);
311 }
312
313 /**
314 * @test
315 */
316 public function emptyConditionIsReported(): void
317 {
318 $timeTrackerProphecy = $this->prophesize(TimeTracker::class);
319 GeneralUtility::setSingletonInstance(TimeTracker::class, $timeTrackerProphecy->reveal());
320
321 $typoScript = '[]';
322 $this->typoScriptParser->parse($typoScript);
323 $expected = 'Empty condition is always false, this does not make sense. At line 0';
324 $this->assertEquals($expected, $this->typoScriptParser->errors[0][0]);
325 }
326
327 /**
328 * @return array
329 */
330 public function doubleSlashCommentsDataProvider(): array
331 {
332 return [
333 'valid, without spaces' => ['// valid, without spaces'],
334 'valid, with one space' => [' // valid, with one space'],
335 'valid, with multiple spaces' => [' // valid, with multiple spaces'],
336 ];
337 }
338
339 /**
340 * @test
341 * @dataProvider doubleSlashCommentsDataProvider
342 * @param string $typoScript
343 */
344 public function doubleSlashCommentsAreValid(string $typoScript): void
345 {
346 $this->typoScriptParser->parse($typoScript);
347 $this->assertEmpty($this->typoScriptParser->errors);
348 }
349
350 /**
351 * @return array
352 */
353 public function includeFileDataProvider(): array
354 {
355 return [
356 'TS code before not matching include' => [
357 '
358 foo = bar
359 <INCLUDE_TYPOSCRIPT: source="FILE:dev.ts" condition="applicationContext = /^NotMatched/">
360 '
361 ],
362 'TS code after not matching include' => [
363 '
364 <INCLUDE_TYPOSCRIPT: source="FILE:dev.ts" condition="applicationContext = /^NotMatched/">
365 foo = bar
366 '
367 ],
368 ];
369 }
370
371 /**
372 * @test
373 * @dataProvider includeFileDataProvider
374 * @param string $typoScript
375 */
376 public function includeFilesWithConditions(string $typoScript): void
377 {
378 $resolvedIncludeLines = TypoScriptParser::checkIncludeLines($typoScript);
379 $this->assertContains('foo = bar', $resolvedIncludeLines);
380 $this->assertNotContains('INCLUDE_TYPOSCRIPT', $resolvedIncludeLines);
381 }
382
383 /**
384 * @return array
385 */
386 public function importFilesDataProvider(): array
387 {
388 return [
389 'Found include file is imported' => [
390 // Input TypoScript
391 'bennilove = before
392 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt"
393 '
394 ,
395 // Expected
396 '
397 bennilove = before
398 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' begin ###
399 test.Core.TypoScript = 1
400 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' end ###
401 '
402 ],
403 'Not found file is not imported' => [
404 // Input TypoScript
405 'bennilove = before
406 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/notfoundfile.txt"
407 '
408 ,
409 // Expected
410 '
411 bennilove = before
412
413 ###
414 ### ERROR: No file or folder found for importing TypoScript on "EXT:core/Tests/Unit/TypoScript/Fixtures/notfoundfile.txt".
415 ###
416 '
417 ],
418 'All files with glob are imported' => [
419 // Input TypoScript
420 'bennilove = before
421 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/*.txt"
422 '
423 ,
424 // Expected
425 '
426 bennilove = before
427 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' begin ###
428 test.Core.TypoScript = 1
429 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' end ###
430 '
431 ],
432 'Specific file with typoscript ending is imported' => [
433 // Input TypoScript
434 'bennilove = before
435 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript"
436 '
437 ,
438 // Expected
439 '
440 bennilove = before
441 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
442 test.TYPO3Forever.TypoScript = 1
443
444 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
445 '
446 ],
447 'All files in folder are imported, sorted by name' => [
448 // Input TypoScript
449 'bennilove = before
450 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/"
451 '
452 ,
453 // Expected
454 '
455 bennilove = before
456 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/badfilename.php\' begin ###
457
458 ###
459 ### ERROR: File "EXT:core/Tests/Unit/TypoScript/Fixtures/badfilename.php" was not included since it is not allowed due to fileDenyPattern.
460 ###
461
462 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/badfilename.php\' end ###
463 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' begin ###
464 test.Core.TypoScript = 1
465 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' end ###
466 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' begin ###
467
468 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
469 test.TYPO3Forever.TypoScript = 1
470
471 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
472
473 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' end ###
474 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
475 test.TYPO3Forever.TypoScript = 1
476
477 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
478 '
479 ],
480 'All files ending with typoscript in folder are imported' => [
481 // Input TypoScript
482 'bennilove = before
483 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/*typoscript"
484 '
485 ,
486 // Expected
487 '
488 bennilove = before
489 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' begin ###
490
491 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
492 test.TYPO3Forever.TypoScript = 1
493
494 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
495
496 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' end ###
497 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
498 test.TYPO3Forever.TypoScript = 1
499
500 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
501 '
502 ],
503 'All typoscript files in folder are imported' => [
504 // Input TypoScript
505 'bennilove = before
506 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/*.typoscript"
507 '
508 ,
509 // Expected
510 '
511 bennilove = before
512 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' begin ###
513
514 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
515 test.TYPO3Forever.TypoScript = 1
516
517 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
518
519 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' end ###
520 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
521 test.TYPO3Forever.TypoScript = 1
522
523 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
524 '
525 ],
526 'All typoscript files in folder with glob are not imported due to recursion level=0' => [
527 // Input TypoScript
528 'bennilove = before
529 @import "EXT:core/Tests/Unit/**/*.typoscript"
530 '
531 ,
532 // Expected
533 '
534 bennilove = before
535
536 ###
537 ### ERROR: No file or folder found for importing TypoScript on "EXT:core/Tests/Unit/**/*.typoscript".
538 ###
539 '
540 ],
541 'TypoScript file ending is automatically added' => [
542 // Input TypoScript
543 'bennilove = before
544 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/setup"
545 '
546 ,
547 // Expected
548 '
549 bennilove = before
550 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
551 test.TYPO3Forever.TypoScript = 1
552
553 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
554 '
555 ],
556 ];
557 }
558
559 /**
560 * @test
561 * @dataProvider importFilesDataProvider
562 * @param string $typoScript
563 * @param string $expected
564 */
565 public function importFiles(string $typoScript, string $expected): void
566 {
567 $resolvedIncludeLines = TypoScriptParser::checkIncludeLines($typoScript);
568 $this->assertEquals($expected, $resolvedIncludeLines);
569 }
570
571 /**
572 * @param string $typoScript
573 * @param array $expected
574 * @dataProvider typoScriptIsParsedToArrayDataProvider
575 * @test
576 */
577 public function typoScriptIsParsedToArray(string $typoScript, array $expected): void
578 {
579 $this->typoScriptParser->parse($typoScript);
580 $this->assertEquals($expected, $this->typoScriptParser->setup);
581 }
582
583 /**
584 * @return array
585 */
586 public function typoScriptIsParsedToArrayDataProvider(): array
587 {
588 return [
589 'simple assignment' => [
590 'key = value',
591 [
592 'key' => 'value',
593 ]
594 ],
595 'simple assignment with escaped dot at the beginning' => [
596 '\\.key = value',
597 [
598 '.key' => 'value',
599 ]
600 ],
601 'simple assignment with protected escaped dot at the beginning' => [
602 '\\\\.key = value',
603 [
604 '\\.' => [
605 'key' => 'value',
606 ],
607 ]
608 ],
609 'nested assignment' => [
610 'lib.key = value',
611 [
612 'lib.' => [
613 'key' => 'value',
614 ],
615 ],
616 ],
617 'nested assignment with escaped key' => [
618 'lib\\.key = value',
619 [
620 'lib.key' => 'value',
621 ],
622 ],
623 'nested assignment with escaped key and escaped dot at the beginning' => [
624 '\\.lib\\.key = value',
625 [
626 '.lib.key' => 'value',
627 ],
628 ],
629 'nested assignment with protected escaped key' => [
630 'lib\\\\.key = value',
631 [
632 'lib\\.' => ['key' => 'value'],
633 ],
634 ],
635 'nested assignment with protected escaped key and protected escaped dot at the beginning' => [
636 '\\\\.lib\\\\.key = value',
637 [
638 '\\.' => [
639 'lib\\.' => ['key' => 'value'],
640 ],
641 ],
642 ],
643 'assignment with escaped an non escaped keys' => [
644 'firstkey.secondkey\\.thirdkey.setting = value',
645 [
646 'firstkey.' => [
647 'secondkey.thirdkey.' => [
648 'setting' => 'value'
649 ]
650 ]
651 ]
652 ],
653 'nested structured assignment' => [
654 'lib {' . LF .
655 'key = value' . LF .
656 '}',
657 [
658 'lib.' => [
659 'key' => 'value',
660 ],
661 ],
662 ],
663 'nested structured assignment with escaped key inside' => [
664 'lib {' . LF .
665 'key\\.nextkey = value' . LF .
666 '}',
667 [
668 'lib.' => [
669 'key.nextkey' => 'value',
670 ],
671 ],
672 ],
673 'nested structured assignment with escaped key inside and escaped dots at the beginning' => [
674 '\\.lib {' . LF .
675 '\\.key\\.nextkey = value' . LF .
676 '}',
677 [
678 '.lib.' => [
679 '.key.nextkey' => 'value',
680 ],
681 ],
682 ],
683 'nested structured assignment with protected escaped key inside' => [
684 'lib {' . LF .
685 'key\\\\.nextkey = value' . LF .
686 '}',
687 [
688 'lib.' => [
689 'key\\.' => ['nextkey' => 'value'],
690 ],
691 ],
692 ],
693 'nested structured assignment with protected escaped key inside and protected escaped dots at the beginning' => [
694 '\\\\.lib {' . LF .
695 '\\\\.key\\\\.nextkey = value' . LF .
696 '}',
697 [
698 '\\.' => [
699 'lib.' => [
700 '\\.' => [
701 'key\\.' => ['nextkey' => 'value'],
702 ],
703 ],
704 ],
705 ],
706 ],
707 'nested structured assignment with escaped key' => [
708 'lib\\.anotherkey {' . LF .
709 'key = value' . LF .
710 '}',
711 [
712 'lib.anotherkey.' => [
713 'key' => 'value',
714 ],
715 ],
716 ],
717 'nested structured assignment with protected escaped key' => [
718 'lib\\\\.anotherkey {' . LF .
719 'key = value' . LF .
720 '}',
721 [
722 'lib\\.' => [
723 'anotherkey.' => [
724 'key' => 'value',
725 ],
726 ],
727 ],
728 ],
729 'multiline assignment' => [
730 'key (' . LF .
731 'first' . LF .
732 'second' . LF .
733 ')',
734 [
735 'key' => 'first' . LF . 'second',
736 ],
737 ],
738 'multiline assignment with escaped key' => [
739 'key\\.nextkey (' . LF .
740 'first' . LF .
741 'second' . LF .
742 ')',
743 [
744 'key.nextkey' => 'first' . LF . 'second',
745 ],
746 ],
747 'multiline assignment with protected escaped key' => [
748 'key\\\\.nextkey (' . LF .
749 'first' . LF .
750 'second' . LF .
751 ')',
752 [
753 'key\\.' => ['nextkey' => 'first' . LF . 'second'],
754 ],
755 ],
756 'copying values' => [
757 'lib.default = value' . LF .
758 'lib.copy < lib.default',
759 [
760 'lib.' => [
761 'default' => 'value',
762 'copy' => 'value',
763 ],
764 ],
765 ],
766 'copying values with escaped key' => [
767 'lib\\.default = value' . LF .
768 'lib.copy < lib\\.default',
769 [
770 'lib.default' => 'value',
771 'lib.' => [
772 'copy' => 'value',
773 ],
774 ],
775 ],
776 'copying values with protected escaped key' => [
777 'lib\\\\.default = value' . LF .
778 'lib.copy < lib\\\\.default',
779 [
780 'lib\\.' => ['default' => 'value'],
781 'lib.' => [
782 'copy' => 'value',
783 ],
784 ],
785 ],
786 'one-line hash comment' => [
787 'first = 1' . LF .
788 '# ignore = me' . LF .
789 'second = 2',
790 [
791 'first' => '1',
792 'second' => '2',
793 ],
794 ],
795 'one-line slash comment' => [
796 'first = 1' . LF .
797 '// ignore = me' . LF .
798 'second = 2',
799 [
800 'first' => '1',
801 'second' => '2',
802 ],
803 ],
804 'multi-line slash comment' => [
805 'first = 1' . LF .
806 '/*' . LF .
807 'ignore = me' . LF .
808 '*/' . LF .
809 'second = 2',
810 [
811 'first' => '1',
812 'second' => '2',
813 ],
814 ],
815 'nested assignment repeated segment names' => [
816 'test.test.test = 1',
817 [
818 'test.' => [
819 'test.' => [
820 'test' => '1',
821 ],
822 ]
823 ],
824 ],
825 'simple assignment operator with tab character before "="' => [
826 'test = someValue',
827 [
828 'test' => 'someValue',
829 ],
830 ],
831 'simple assignment operator character as value "="' => [
832 'test ==TEST=',
833 [
834 'test' => '=TEST=',
835 ],
836 ],
837 'nested assignment operator character as value "="' => [
838 'test.test ==TEST=',
839 [
840 'test.' => [
841 'test' => '=TEST=',
842 ],
843 ],
844 ],
845 'simple assignment character as value "<"' => [
846 'test =<TEST>',
847 [
848 'test' => '<TEST>',
849 ],
850 ],
851 'nested assignment character as value "<"' => [
852 'test.test =<TEST>',
853 [
854 'test.' => [
855 'test' => '<TEST>',
856 ],
857 ],
858 ],
859 'simple assignment character as value ">"' => [
860 'test =>TEST<',
861 [
862 'test' => '>TEST<',
863 ],
864 ],
865 'nested assignment character as value ">"' => [
866 'test.test =>TEST<',
867 [
868 'test.' => [
869 'test' => '>TEST<',
870 ],
871 ],
872 ],
873 'nested assignment repeated segment names with whitespaces' => [
874 'test.test.test = 1' . " \t",
875 [
876 'test.' => [
877 'test.' => [
878 'test' => '1',
879 ],
880 ]
881 ],
882 ],
883 'simple assignment operator character as value "=" with whitespaces' => [
884 'test = =TEST=' . " \t",
885 [
886 'test' => '=TEST=',
887 ],
888 ],
889 'nested assignment operator character as value "=" with whitespaces' => [
890 'test.test = =TEST=' . " \t",
891 [
892 'test.' => [
893 'test' => '=TEST=',
894 ],
895 ],
896 ],
897 'simple assignment character as value "<" with whitespaces' => [
898 'test = <TEST>' . " \t",
899 [
900 'test' => '<TEST>',
901 ],
902 ],
903 'nested assignment character as value "<" with whitespaces' => [
904 'test.test = <TEST>' . " \t",
905 [
906 'test.' => [
907 'test' => '<TEST>',
908 ],
909 ],
910 ],
911 'simple assignment character as value ">" with whitespaces' => [
912 'test = >TEST<' . " \t",
913 [
914 'test' => '>TEST<',
915 ],
916 ],
917 'nested assignment character as value ">" with whitespaces' => [
918 'test.test = >TEST<',
919 [
920 'test.' => [
921 'test' => '>TEST<',
922 ],
923 ],
924 ],
925 'CSC example #1' => [
926 'linkParams.ATagParams.dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
927 [
928 'linkParams.' => [
929 'ATagParams.' => [
930 'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
931 ],
932 ],
933 ],
934 ],
935 'CSC example #2' => [
936 'linkParams.ATagParams {' . LF .
937 'dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
938 '}',
939 [
940 'linkParams.' => [
941 'ATagParams.' => [
942 'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
943 ],
944 ],
945 ],
946 ],
947 'CSC example #3' => [
948 'linkParams.ATagParams.dataWrap (' . LF .
949 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
950 ')',
951 [
952 'linkParams.' => [
953 'ATagParams.' => [
954 'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
955 ],
956 ],
957 ],
958 ],
959 'key with colon' => [
960 'some:key = is valid',
961 [
962 'some:key' => 'is valid'
963 ]
964 ],
965 'special operator' => [
966 'some := addToList(a)',
967 [
968 'some' => 'a'
969 ]
970 ],
971 'special operator with white-spaces' => [
972 'some := addToList (a)',
973 [
974 'some' => 'a'
975 ]
976 ],
977 'special operator with tabs' => [
978 'some := addToList (a)',
979 [
980 'some' => 'a'
981 ]
982 ],
983 'special operator with white-spaces and tabs in value' => [
984 'some := addToList( a, b, c )',
985 [
986 'some' => 'a, b, c'
987 ]
988 ],
989 'special operator and colon, no spaces' => [
990 'some:key:=addToList(a)',
991 [
992 'some:key' => 'a'
993 ]
994 ],
995 'key with all special symbols' => [
996 'someSpecial\\_:-\\.Chars = is valid',
997 [
998 'someSpecial\\_:-.Chars' => 'is valid'
999 ]
1000 ],
1001 ];
1002 }
1003
1004 /**
1005 * @test
1006 */
1007 public function setValCanBeCalledWithArrayValueParameter(): void
1008 {
1009 $string = '';
1010 $setup = [];
1011 $value = [];
1012 $this->typoScriptParser->setVal($string, $setup, $value);
1013 }
1014
1015 /**
1016 * @test
1017 */
1018 public function setValCanBeCalledWithStringValueParameter(): void
1019 {
1020 $string = '';
1021 $setup = [];
1022 $value = '';
1023 $this->typoScriptParser->setVal($string, $setup, $value);
1024 }
1025
1026 /**
1027 * @test
1028 * @dataProvider parseNextKeySegmentReturnsCorrectNextKeySegmentDataProvider
1029 * @param string $key
1030 * @param string $expectedKeySegment
1031 * @param string $expectedRemainingKey
1032 */
1033 public function parseNextKeySegmentReturnsCorrectNextKeySegment(
1034 string $key,
1035 string $expectedKeySegment,
1036 string $expectedRemainingKey
1037 ): void {
1038 [$keySegment, $remainingKey] = $this->typoScriptParser->_call('parseNextKeySegment', $key);
1039 $this->assertSame($expectedKeySegment, $keySegment);
1040 $this->assertSame($expectedRemainingKey, $remainingKey);
1041 }
1042
1043 /**
1044 * @return array
1045 */
1046 public function parseNextKeySegmentReturnsCorrectNextKeySegmentDataProvider(): array
1047 {
1048 return [
1049 'key without separator' => [
1050 'testkey',
1051 'testkey',
1052 ''
1053 ],
1054 'key with normal separator' => [
1055 'test.key',
1056 'test',
1057 'key'
1058 ],
1059 'key with multiple normal separators' => [
1060 'test.key.subkey',
1061 'test',
1062 'key.subkey'
1063 ],
1064 'key with separator and escape character' => [
1065 'te\\st.test',
1066 'te\\st',
1067 'test'
1068 ],
1069 'key with escaped separators' => [
1070 'test\\.key\\.subkey',
1071 'test.key.subkey',
1072 ''
1073 ],
1074 'key with escaped and unescaped separator 1' => [
1075 'test.test\\.key',
1076 'test',
1077 'test\\.key'
1078 ],
1079 'key with escaped and unescaped separator 2' => [
1080 'test\\.test.key\\.key2',
1081 'test.test',
1082 'key\\.key2'
1083 ],
1084 'key with escaped escape character' => [
1085 'test\\\\.key',
1086 'test\\',
1087 'key'
1088 ],
1089 'key with escaped separator and additional escape character' => [
1090 'test\\\\\\.key',
1091 'test\\\\',
1092 'key'
1093 ],
1094
1095 'multiple escape characters within the key are preserved' => [
1096 'te\\\\st\\\\.key',
1097 'te\\\\st\\',
1098 'key'
1099 ]
1100 ];
1101 }
1102 }