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