[TASK] typo3/testing-framework now flushes runtime caches
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Tests / Unit / Configuration / TypoScript / ConditionMatching / ConditionMatcherTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Frontend\Tests\Unit\Configuration\TypoScript\ConditionMatching;
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
18 use Prophecy\Argument;
19 use TYPO3\CMS\Core\Configuration\TypoScript\Exception\InvalidTypoScriptConditionException;
20 use TYPO3\CMS\Core\Context\Context;
21 use TYPO3\CMS\Core\Context\UserAspect;
22 use TYPO3\CMS\Core\Http\ServerRequest;
23 use TYPO3\CMS\Core\Log\Logger;
24 use TYPO3\CMS\Core\Site\Entity\Site;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
27 use TYPO3\CMS\Frontend\Configuration\TypoScript\ConditionMatching\ConditionMatcher;
28 use TYPO3\CMS\Frontend\Tests\Unit\Configuration\TypoScript\ConditionMatching\Fixtures\TestConditionException;
29 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
30
31 /**
32 * Test case
33 */
34 class ConditionMatcherTest extends UnitTestCase
35 {
36 /**
37 * @var ConditionMatcher
38 */
39 protected $subject;
40
41 /**
42 * @var string
43 */
44 protected $testGlobalNamespace;
45
46 protected function setUp(): void
47 {
48 $this->resetSingletonInstances = true;
49 $GLOBALS['TYPO3_REQUEST'] = new ServerRequest();
50
51 $this->testGlobalNamespace = $this->getUniqueId('TEST');
52 $GLOBALS[$this->testGlobalNamespace] = [];
53 $GLOBALS['TSFE'] = new \stdClass();
54 $GLOBALS['TSFE']->page = [];
55 $GLOBALS['TSFE']->tmpl = new \stdClass();
56 $GLOBALS['TSFE']->tmpl->rootLine = [
57 2 => ['uid' => 121, 'pid' => 111],
58 1 => ['uid' => 111, 'pid' => 101],
59 0 => ['uid' => 101, 'pid' => 0]
60 ];
61
62 $frontedUserAuthentication = $this->getMockBuilder(FrontendUserAuthentication::class)
63 ->setMethods(['dummy'])
64 ->getMock();
65
66 $frontedUserAuthentication->user['uid'] = 13;
67 $frontedUserAuthentication->groupData['uid'] = [14];
68 $GLOBALS['TSFE']->fe_user = $frontedUserAuthentication;
69 $this->getFreshConditionMatcher();
70 }
71
72 protected function getFreshConditionMatcher()
73 {
74 $this->subject = new ConditionMatcher(new Context([
75 'frontend.user' => new UserAspect($GLOBALS['TSFE']->fe_user)
76 ]));
77 $this->subject->setLogger($this->prophesize(Logger::class)->reveal());
78 }
79
80 /**
81 * Tests whether a faulty expression fails.
82 *
83 * @test
84 */
85 public function simulateDisabledMatchAllConditionsFailsOnFaultyExpression(): void
86 {
87 $this->getFreshConditionMatcher();
88 $this->assertFalse($this->subject->match('[nullCondition = This expression would return FALSE in general]'));
89 }
90
91 /**
92 * Tests whether simulating positive matches for all conditions succeeds.
93 *
94 * @test
95 */
96 public function simulateEnabledMatchAllConditionsSucceeds(): void
97 {
98 $this->getFreshConditionMatcher();
99 $this->subject->setSimulateMatchResult(true);
100 $this->assertTrue($this->subject->match('[nullCondition = This expression would return FALSE in general]'));
101 }
102
103 /**
104 * Tests whether simulating positive matches for specific conditions succeeds.
105 *
106 * @test
107 */
108 public function simulateEnabledMatchSpecificConditionsSucceeds(): void
109 {
110 $this->getFreshConditionMatcher();
111 $testCondition = '[' . $this->getUniqueId('test') . ' = Any condition to simulate a positive match]';
112 $this->subject->setSimulateMatchConditions([$testCondition]);
113 $this->assertTrue($this->subject->match($testCondition));
114 }
115
116 /**
117 * Tests whether the language comparison matches.
118 *
119 * @test
120 */
121 public function languageConditionMatchesSingleLanguageExpression(): void
122 {
123 $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3';
124 $this->getFreshConditionMatcher();
125 $this->assertTrue($this->subject->match('[language = *de*]'));
126 $this->assertTrue($this->subject->match('[language = *de-de*]'));
127 // Test expression language
128 // @TODO: not work yet, looks like test setup issue
129 // $this->assertTrue($this->subject->match('[like(request.getNormalizedParams().getHttpAcceptLanguage(), "**de*")]'));
130 // $this->assertTrue($this->subject->match('[like(request.getNormalizedParams().getHttpAcceptLanguage(), "**de-de*")]'));
131 }
132
133 /**
134 * Tests whether the language comparison matches.
135 *
136 * @test
137 */
138 public function languageConditionMatchesMultipleLanguagesExpression(): void
139 {
140 $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3';
141 $this->getFreshConditionMatcher();
142 $this->assertTrue($this->subject->match('[language = *en*,*de*]'));
143 $this->assertTrue($this->subject->match('[language = *en-us*,*de-de*]'));
144 // Test expression language
145 // @TODO: not work yet, looks like test setup issue
146 // $this->assertTrue($this->subject->match('[like(request.getNormalizedParams().getHttpAcceptLanguage(), "*en*,*de*")]'));
147 // $this->assertTrue($this->subject->match('[like(request.getNormalizedParams().getHttpAcceptLanguage(), "*en-us*,*de-de*")]'));
148 }
149
150 /**
151 * Tests whether the language comparison matches.
152 *
153 * @test
154 */
155 public function languageConditionMatchesCompleteLanguagesExpression(): void
156 {
157 $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'de-de,de;q=0.8,en-us;q=0.5,en;q=0.3';
158 $this->getFreshConditionMatcher();
159 $this->assertTrue($this->subject->match('[language = de-de,de;q=0.8,en-us;q=0.5,en;q=0.3]'));
160 // Test expression language
161 // @TODO: not work yet, looks like test setup issue
162 // $this->assertTrue($this->subject->match('[request.getNormalizedParams().getHttpAcceptLanguage() == "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3"]'));
163 }
164
165 /**
166 * Tests whether usergroup comparison matches.
167 *
168 * @test
169 */
170 public function usergroupConditionMatchesSingleGroupId(): void
171 {
172 $subject = new ConditionMatcher(new Context([
173 'frontend.user' => new UserAspect(new FrontendUserAuthentication(), [13, 14, 15])
174 ]));
175 $loggerProphecy = $this->prophesize(Logger::class);
176 $subject->setLogger($loggerProphecy->reveal());
177 $this->assertTrue($subject->match('[usergroup = 13]'));
178 // Test expression language
179 $this->assertTrue($subject->match('[usergroup(13)]'));
180 $this->assertTrue($subject->match('[usergroup("13")]'));
181 $this->assertTrue($subject->match('[usergroup(\'13\')]'));
182 }
183
184 /**
185 * Tests whether usergroup comparison matches.
186 *
187 * @test
188 */
189 public function usergroupConditionMatchesMultipleUserGroupId(): void
190 {
191 $subject = new ConditionMatcher(new Context([
192 'frontend.user' => new UserAspect(new FrontendUserAuthentication(), [13, 14, 15])
193 ]));
194 $loggerProphecy = $this->prophesize(Logger::class);
195 $subject->setLogger($loggerProphecy->reveal());
196 $this->assertTrue($subject->match('[usergroup = 999,15,14,13]'));
197 // Test expression language
198 $this->assertFalse($subject->match('[usergroup(999,15,14,13)]'));
199 $this->assertTrue($subject->match('[usergroup("999,15,14,13")]'));
200 $this->assertTrue($subject->match('[usergroup(\'999,15,14,13\')]'));
201 }
202
203 /**
204 * Tests whether usergroup comparison matches.
205 *
206 * @test
207 */
208 public function usergroupConditionDoesNotMatchDefaulUserGroupIds(): void
209 {
210 $subject = new ConditionMatcher(new Context([
211 'frontend.user' => new UserAspect(new FrontendUserAuthentication(), [0, -1])
212 ]));
213 $loggerProphecy = $this->prophesize(Logger::class);
214 $subject->setLogger($loggerProphecy->reveal());
215 $this->assertFalse($subject->match('[usergroup = 0,-1]'));
216 // Test expression language
217 $this->assertFalse($subject->match('[usergroup("0,-1")]'));
218 $this->assertFalse($subject->match('[usergroup(\'0,-1\')]'));
219 }
220
221 /**
222 * Tests whether user comparison matches.
223 *
224 * @test
225 */
226 public function loginUserConditionMatchesAnyLoggedInUser(): void
227 {
228 $this->getFreshConditionMatcher();
229 // @TODO: not work yet, looks like test setup issue
230 $this->assertTrue($this->subject->match('[loginUser = *]'));
231 // Test expression language
232 $this->assertTrue($this->subject->match('[loginUser("*")]'));
233 $this->assertTrue($this->subject->match('[loginUser(\'*\')]'));
234 }
235
236 /**
237 * Tests whether user comparison matches.
238 *
239 * @test
240 */
241 public function loginUserConditionMatchesSingleLoggedInUser(): void
242 {
243 $this->getFreshConditionMatcher();
244 // @TODO: not work yet, looks like test setup issue
245 $this->assertTrue($this->subject->match('[loginUser = 13]'));
246 // Test expression language
247 $this->assertTrue($this->subject->match('[loginUser(13)]'));
248 $this->assertTrue($this->subject->match('[loginUser("13")]'));
249 $this->assertTrue($this->subject->match('[loginUser(\'13\')]'));
250 }
251
252 /**
253 * Tests whether user comparison matches.
254 *
255 * @test
256 */
257 public function loginUserConditionMatchesMultipleLoggedInUsers(): void
258 {
259 $this->getFreshConditionMatcher();
260 // @TODO: not work yet, looks like test setup issue
261 $this->assertTrue($this->subject->match('[loginUser = 999,13]'));
262 // Test expression language
263 $this->assertTrue($this->subject->match('[loginUser("999,13")]'));
264 $this->assertTrue($this->subject->match('[loginUser(\'999,13\')]'));
265 }
266
267 /**
268 * Tests whether user comparison matches.
269 *
270 * @test
271 */
272 public function loginUserConditionDoesNotMatchIfNotUserIsLoggedId(): void
273 {
274 $user = new FrontendUserAuthentication();
275 $user->user['uid'] = 13;
276 $subject = new ConditionMatcher(new Context([
277 'frontend.user' => new UserAspect($user)
278 ]));
279 $loggerProphecy = $this->prophesize(Logger::class);
280 $subject->setLogger($loggerProphecy->reveal());
281 $this->assertFalse($subject->match('[loginUser = *]'));
282 $this->assertFalse($subject->match('[loginUser = 13]'));
283 // Test expression language
284 $this->assertFalse($subject->match('[loginUser("*")]'));
285 $this->assertTrue($subject->match('[loginUser("*") == false]'));
286 $this->assertFalse($subject->match('[loginUser("13")]'));
287 $this->assertFalse($subject->match('[loginUser(\'*\')]'));
288 $this->assertFalse($subject->match('[loginUser(\'13\')]'));
289 }
290
291 /**
292 * Tests whether user is not logged in
293 *
294 * @test
295 */
296 public function loginUserConditionMatchIfUserIsNotLoggedIn(): void
297 {
298 $user = new FrontendUserAuthentication();
299 $subject = new ConditionMatcher(new Context([
300 'frontend.user' => new UserAspect($user)
301 ]));
302 $loggerProphecy = $this->prophesize(Logger::class);
303 $subject->setLogger($loggerProphecy->reveal());
304 $this->assertTrue($subject->match('[loginUser = ]'));
305 // Test expression language
306 $this->assertTrue($subject->match('[loginUser(\'*\') == false]'));
307 $this->assertTrue($subject->match('[loginUser("*") == false]'));
308 }
309
310 /**
311 * Tests whether numerical comparison matches.
312 *
313 * @test
314 */
315 public function globalVarConditionMatchesOnEqualExpression(): void
316 {
317 $this->assertTrue($this->subject->match('[globalVar = LIT:10 = 10]'));
318 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 = 10.1]'));
319 $this->assertTrue($this->subject->match('[globalVar = LIT:10 == 10]'));
320 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 == 10.1]'));
321 // Test expression language
322 // Access with LIT is not possible in expression language, because constants available as variable
323 }
324
325 /**
326 * Tests whether numerical comparison matches.
327 *
328 * @test
329 */
330 public function globalVarConditionMatchesOnEqualExpressionWithMultipleValues(): void
331 {
332 $this->assertTrue($this->subject->match('[globalVar = LIT:10 = 10|20|30]'));
333 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 = 10.1|20.2|30.3]'));
334 $this->assertTrue($this->subject->match('[globalVar = LIT:20 = 10|20|30]'));
335 $this->assertTrue($this->subject->match('[globalVar = LIT:20.2 = 10.1|20.2|30.3]'));
336 $this->assertTrue($this->subject->match('[globalVar = LIT:10 == 10|20|30]'));
337 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 == 10.1|20.2|30.3]'));
338 $this->assertTrue($this->subject->match('[globalVar = LIT:20 == 10|20|30]'));
339 $this->assertTrue($this->subject->match('[globalVar = LIT:20.2 == 10.1|20.2|30.3]'));
340 // Test expression language
341 // Access with LIT is not possible in expression language, because constants available as variable
342 }
343
344 /**
345 * Tests whether numerical comparison matches.
346 *
347 * @test
348 */
349 public function globalVarConditionMatchesOnNotEqualExpression(): void
350 {
351 $this->assertTrue($this->subject->match('[globalVar = LIT:10 != 20]'));
352 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 != 10.2]'));
353 // Test expression language
354 // Access with LIT is not possible in expression language, because constants available as variable
355 }
356
357 /**
358 * Tests whether numerical comparison does not match.
359 *
360 * @test
361 */
362 public function globalVarConditionDoesNotMatchOnNotEqualExpression(): void
363 {
364 $this->assertFalse($this->subject->match('[globalVar = LIT:10 != 10]'));
365 // Test expression language
366 // Access with LIT is not possible in expression language, because constants available as variable
367 }
368
369 /**
370 * Tests whether numerical comparison matches.
371 *
372 * @test
373 */
374 public function globalVarConditionMatchesOnNotEqualExpressionWithMultipleValues(): void
375 {
376 $this->assertTrue($this->subject->match('[globalVar = LIT:10 != 20|30]'));
377 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 != 10.2|20.3]'));
378 // Test expression language
379 // Access with LIT is not possible in expression language, because constants available as variable
380 }
381
382 /**
383 * Tests whether numerical comparison matches.
384 *
385 * @test
386 */
387 public function globalVarConditionMatchesOnLowerThanExpression(): void
388 {
389 $this->assertTrue($this->subject->match('[globalVar = LIT:10 < 20]'));
390 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 < 10.2]'));
391 // Test expression language
392 // Access with LIT is not possible in expression language, because constants available as variable
393 }
394
395 /**
396 * Tests whether numerical comparison matches.
397 *
398 * @test
399 */
400 public function globalVarConditionMatchesOnLowerThanOrEqualExpression(): void
401 {
402 $this->assertTrue($this->subject->match('[globalVar = LIT:10 <= 10]'));
403 $this->assertTrue($this->subject->match('[globalVar = LIT:10 <= 20]'));
404 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 <= 10.1]'));
405 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 <= 10.2]'));
406 // Test expression language
407 // Access with LIT is not possible in expression language, because constants available as variable
408 }
409
410 /**
411 * Tests whether numerical comparison matches.
412 *
413 * @test
414 */
415 public function globalVarConditionMatchesOnGreaterThanExpression(): void
416 {
417 $this->assertTrue($this->subject->match('[globalVar = LIT:20 > 10]'));
418 $this->assertTrue($this->subject->match('[globalVar = LIT:10.2 > 10.1]'));
419 // Test expression language
420 // Access with LIT is not possible in expression language, because constants available as variable
421 }
422
423 /**
424 * Tests whether numerical comparison matches.
425 *
426 * @test
427 */
428 public function globalVarConditionMatchesOnGreaterThanOrEqualExpression(): void
429 {
430 $this->assertTrue($this->subject->match('[globalVar = LIT:10 >= 10]'));
431 $this->assertTrue($this->subject->match('[globalVar = LIT:20 >= 10]'));
432 $this->assertTrue($this->subject->match('[globalVar = LIT:10.1 >= 10.1]'));
433 $this->assertTrue($this->subject->match('[globalVar = LIT:10.2 >= 10.1]'));
434 // Test expression language
435 // Access with LIT is not possible in expression language, because constants available as variable
436 }
437
438 /**
439 * Tests whether numerical comparison matches.
440 *
441 * @test
442 */
443 public function globalVarConditionMatchesOnEmptyExpressionWithNoValueSet(): void
444 {
445 $testKey = $this->getUniqueId('test');
446 $this->assertTrue($this->subject->match('[globalVar = GP:' . $testKey . '=]'));
447 $this->assertTrue($this->subject->match('[globalVar = GP:' . $testKey . ' = ]'));
448 }
449
450 /**
451 * Tests whether numerical comparison matches.
452 *
453 * @test
454 */
455 public function globalVarConditionDoesNotMatchOnEmptyExpressionWithValueSetToZero(): void
456 {
457 $testKey = $this->getUniqueId('test');
458 $_GET = [];
459 $_POST = [$testKey => 0];
460 $this->assertFalse($this->subject->match('[globalVar = GP:' . $testKey . '=]'));
461 $this->assertFalse($this->subject->match('[globalVar = GP:' . $testKey . ' = ]'));
462 }
463
464 /**
465 * Tests whether an array with zero as key matches its value
466 *
467 * @test
468 */
469 public function globalVarConditionMatchesOnArrayExpressionWithZeroAsKey(): void
470 {
471 $testKey = $this->getUniqueId('test');
472 $testValue = '1';
473 $_GET = [];
474 $_POST = [$testKey => ['0' => $testValue]];
475 $this->assertTrue($this->subject->match('[globalVar = GP:' . $testKey . '|0=' . $testValue . ']'));
476 }
477
478 /**
479 * Tests whether string comparison matches.
480 *
481 * @test
482 */
483 public function globalStringConditionMatchesOnEqualExpression(): void
484 {
485 $this->assertTrue($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = TYPO3.Test.Condition]'));
486 $this->assertFalse($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = TYPO3]'));
487 // Test expression language
488 // Access with LIT is not possible in expression language, because constants available as variable
489 }
490
491 /**
492 * Tests whether string comparison matches.
493 *
494 * @test
495 */
496 public function globalStringConditionMatchesOnEmptyExpressionWithValueSetToEmptyString(): void
497 {
498 $testKey = $this->getUniqueId('test');
499 $_GET = [];
500 $_POST = [$testKey => ''];
501 $this->assertTrue($this->subject->match('[globalString = GP:' . $testKey . '=]'));
502 $this->assertTrue($this->subject->match('[globalString = GP:' . $testKey . ' = ]'));
503 }
504
505 /**
506 * Tests whether string comparison matches.
507 *
508 * @test
509 */
510 public function globalStringConditionMatchesOnEmptyLiteralExpressionWithValueSetToEmptyString(): void
511 {
512 $this->assertTrue($this->subject->match('[globalString = LIT:=]'));
513 $this->assertTrue($this->subject->match('[globalString = LIT: = ]'));
514 // Test expression language
515 // Access with LIT is not possible in expression language, because constants available as variable
516 }
517
518 /**
519 * Tests whether string comparison matches.
520 *
521 * @test
522 */
523 public function globalStringConditionMatchesWildcardExpression(): void
524 {
525 $this->assertTrue($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = TYPO3?Test?Condition]'));
526 $this->assertTrue($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = TYPO3.T*t.Condition]'));
527 $this->assertTrue($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = TYPO3?T*t?Condition]'));
528 // Test expression language
529 // Access with LIT is not possible in expression language, because constants available as variable
530 }
531
532 /**
533 * Tests whether string comparison matches.
534 *
535 * @test
536 */
537 public function globalStringConditionMatchesRegularExpression(): void
538 {
539 $this->assertTrue($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = /^[A-Za-z3.]+$/]'));
540 $this->assertTrue($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = /^TYPO3\\..+Condition$/]'));
541 $this->assertFalse($this->subject->match('[globalString = LIT:TYPO3.Test.Condition = /^FALSE/]'));
542 // Test expression language
543 // Access with LIT is not possible in expression language, because constants available as variable
544 }
545
546 /**
547 * Tests whether string comparison matches.
548 *
549 * @test
550 */
551 public function globalStringConditionMatchesEmptyRegularExpression(): void
552 {
553 $testKey = $this->getUniqueId('test');
554 $GLOBALS['_SERVER'][$testKey] = '';
555 $this->assertTrue($this->subject->match('[globalString = _SERVER|' . $testKey . ' = /^$/]'));
556 // Test expression language
557 // Access request by request() method
558 }
559
560 /**
561 * Tests whether treeLevel comparison matches.
562 *
563 * @test
564 */
565 public function treeLevelConditionMatchesSingleValue(): void
566 {
567 $this->assertTrue($this->subject->match('[treeLevel = 2]'));
568 // Test expression language
569 $this->assertTrue($this->subject->match('[tree.level == 2]'));
570 }
571
572 /**
573 * Tests whether treeLevel comparison matches.
574 *
575 * @test
576 */
577 public function treeLevelConditionMatchesMultipleValues(): void
578 {
579 $this->assertTrue($this->subject->match('[treeLevel = 999,998,2]'));
580 // Test expression language
581 $this->assertTrue($this->subject->match('[tree.level in [999,998,2]]'));
582 }
583
584 /**
585 * Tests whether treeLevel comparison matches.
586 *
587 * @test
588 */
589 public function treeLevelConditionDoesNotMatchFaultyValue(): void
590 {
591 $this->assertFalse($this->subject->match('[treeLevel = 999]'));
592 // Test expression language
593 $this->assertFalse($this->subject->match('[tree.level == 999]'));
594 }
595
596 /**
597 * @return array
598 */
599 public function pageDataProvider(): array
600 {
601 return [
602 '[page|layout = 0]' => ['[page|layout = 0]', true],
603 '[page|layout = 1]' => ['[page|layout = 1]', false],
604 '[page|title = Foo]' => ['[page|title = Foo]', true],
605 ];
606 }
607
608 /**
609 * @test
610 * @dataProvider pageDataProvider
611 * @param string $expression
612 * @param bool $expected
613 */
614 public function checkConditionMatcherForPage(string $expression, bool $expected): void
615 {
616 $GLOBALS['TSFE']->page = ['title' => 'Foo', 'layout' => 0];
617 $this->getFreshConditionMatcher();
618 $this->assertSame($expected, $this->subject->match($expression));
619 }
620
621 /**
622 * Tests whether a page Id is found in the previous rootline entries.
623 *
624 * @test
625 */
626 public function PIDupinRootlineConditionMatchesSinglePageIdInRootline(): void
627 {
628 $GLOBALS['TSFE']->id = 121;
629 $this->getFreshConditionMatcher();
630 $this->assertTrue($this->subject->match('[PIDupinRootline = 111]'));
631 // Test expression language
632 $this->assertTrue($this->subject->match('[111 in tree.rootLineIds]'));
633 $this->assertTrue($this->subject->match('["111" in tree.rootLineIds]'));
634 $this->assertTrue($this->subject->match('[\'111\' in tree.rootLineIds]'));
635 }
636
637 /**
638 * Tests whether a page Id is found in the previous rootline entries.
639 *
640 * @test
641 */
642 public function PIDupinRootlineConditionMatchesMultiplePageIdsInRootline(): void
643 {
644 $GLOBALS['TSFE']->id = 121;
645 $this->getFreshConditionMatcher();
646 $this->assertTrue($this->subject->match('[PIDupinRootline = 999,111,101]'));
647 // Test expression language
648 $this->assertTrue($this->subject->match('[999 in tree.rootLineIds][111 in tree.rootLineIds][101 in tree.rootLineIds]'));
649 }
650
651 /**
652 * Tests whether a page Id is found in the previous rootline entries.
653 *
654 * @test
655 */
656 public function PIDupinRootlineConditionDoesNotMatchPageIdNotInRootline(): void
657 {
658 $GLOBALS['TSFE']->id = 121;
659 $this->getFreshConditionMatcher();
660 $this->assertFalse($this->subject->match('[PIDupinRootline = 999]'));
661 // Test expression language
662 $this->assertFalse($this->subject->match('[999 in tree.rootLineIds]'));
663 }
664
665 /**
666 * Tests whether a page Id is found in the previous rootline entries.
667 *
668 * @test
669 */
670 public function PIDupinRootlineConditionDoesNotMatchLastPageIdInRootline(): void
671 {
672 $GLOBALS['TSFE']->id = 121;
673 $this->getFreshConditionMatcher();
674 $this->assertFalse($this->subject->match('[PIDupinRootline = 121]'));
675 // Test expression language
676 $this->assertFalse($this->subject->match('[page.uid != 121 && 121 in rootLineUids]'));
677 }
678
679 /**
680 * Tests whether a page Id is found in all rootline entries.
681 *
682 * @test
683 */
684 public function PIDinRootlineConditionMatchesSinglePageIdInRootline(): void
685 {
686 $GLOBALS['TSFE']->id = 121;
687 $this->getFreshConditionMatcher();
688 $this->assertTrue($this->subject->match('[PIDinRootline = 111]'));
689 // Test expression language
690 $this->assertTrue($this->subject->match('[111 in tree.rootLineIds]'));
691 }
692
693 /**
694 * Tests whether a page Id is found in all rootline entries.
695 *
696 * @test
697 */
698 public function PIDinRootlineConditionMatchesMultiplePageIdsInRootline(): void
699 {
700 $GLOBALS['TSFE']->id = 121;
701 $this->getFreshConditionMatcher();
702 $this->assertTrue($this->subject->match('[PIDinRootline = 999,111,101]'));
703 // Test expression language
704 $this->assertTrue($this->subject->match('[999 in tree.rootLineIds][111 in tree.rootLineIds][101 in tree.rootLineIds]'));
705 }
706
707 /**
708 * Tests whether a page Id is found in all rootline entries.
709 *
710 * @test
711 */
712 public function PIDinRootlineConditionMatchesLastPageIdInRootline(): void
713 {
714 $GLOBALS['TSFE']->id = 121;
715 $this->getFreshConditionMatcher();
716 $this->assertTrue($this->subject->match('[PIDinRootline = 121]'));
717 // Test expression language
718 $this->assertTrue($this->subject->match('[121 in tree.rootLineIds]'));
719 }
720
721 /**
722 * Tests whether a page Id is found in all rootline entries.
723 *
724 * @test
725 */
726 public function PIDinRootlineConditionDoesNotMatchPageIdNotInRootline(): void
727 {
728 $GLOBALS['TSFE']->id = 121;
729 $this->assertFalse($this->subject->match('[PIDinRootline = 999]'));
730 // Test expression language
731 $this->assertFalse($this->subject->match('[999 in tree.rootLineIds]'));
732 }
733
734 /**
735 * Tests whether the compatibility version can be evaluated.
736 * (e.g. 7.9 is compatible to 7.0 but not to 15.0)
737 *
738 * @test
739 */
740 public function compatVersionConditionMatchesOlderRelease(): void
741 {
742 $this->assertTrue($this->subject->match('[compatVersion = 7.0]'));
743 // Test expression language
744 $this->assertTrue($this->subject->match('[compatVersion(7.0)]'));
745 $this->assertTrue($this->subject->match('[compatVersion("7.0")]'));
746 $this->assertTrue($this->subject->match('[compatVersion(\'7.0\')]'));
747 }
748
749 /**
750 * Tests whether the compatibility version can be evaluated.
751 * (e.g. 7.9 is compatible to 7.0 but not to 15.0)
752 *
753 * @test
754 */
755 public function compatVersionConditionMatchesSameRelease(): void
756 {
757 $this->assertTrue($this->subject->match('[compatVersion = ' . TYPO3_branch . ']'));
758 // Test expression language
759 $this->assertTrue($this->subject->match('[compatVersion(' . TYPO3_branch . ')]'));
760 }
761
762 /**
763 * Tests whether the compatibility version can be evaluated.
764 * (e.g. 7.9 is compatible to 7.0 but not to 15.0)
765 *
766 * @test
767 */
768 public function compatVersionConditionDoesNotMatchNewerRelease(): void
769 {
770 $this->assertFalse($this->subject->match('[compatVersion = 15.0]'));
771 // Test expression language
772 $this->assertFalse($this->subject->match('[compatVersion(15.0)]'));
773 $this->assertFalse($this->subject->match('[compatVersion("15.0")]'));
774 $this->assertFalse($this->subject->match('[compatVersion(\'15.0\')]'));
775 }
776
777 /**
778 * Tests whether the generic fetching of variables works with the namespace 'GP'.
779 *
780 * @test
781 */
782 public function genericGetVariablesSucceedsWithNamespaceGP(): void
783 {
784 $_GET = ['testGet' => 'getTest'];
785 $_POST = ['testPost' => 'postTest'];
786 $this->getFreshConditionMatcher();
787 $this->assertTrue($this->subject->match('[globalString = GP:testGet = getTest]'));
788 $this->assertTrue($this->subject->match('[globalString = GP:testPost = postTest]'));
789 }
790
791 /**
792 * Tests whether the generic fetching of variables works with the namespace 'TSFE'.
793 *
794 * @test
795 */
796 public function genericGetVariablesSucceedsWithNamespaceTSFE(): void
797 {
798 $GLOBALS['TSFE']->id = 1234567;
799 $GLOBALS['TSFE']->testSimpleObject = new \stdClass();
800 $GLOBALS['TSFE']->testSimpleObject->testSimpleVariable = 'testValue';
801
802 $this->getFreshConditionMatcher();
803 $this->assertTrue($this->subject->match('[globalString = TSFE:id = 1234567]'));
804 $this->assertTrue($this->subject->match('[globalString = TSFE:testSimpleObject|testSimpleVariable = testValue]'));
805 // Test expression language
806 $this->assertTrue($this->subject->match('[getTSFE().id == 1234567]'));
807 $this->assertTrue($this->subject->match('[getTSFE().testSimpleObject.testSimpleVariable == "testValue"]'));
808 }
809
810 /**
811 * Tests whether the generic fetching of variables works with the namespace 'session'.
812 *
813 * @test
814 */
815 public function genericGetVariablesSucceedsWithNamespaceSession(): void
816 {
817 $prophecy = $this->prophesize(FrontendUserAuthentication::class);
818 $prophecy->getSessionData(Argument::exact('foo'))->willReturn(['bar' => 1234567]);
819 $GLOBALS['TSFE']->fe_user = $prophecy->reveal();
820
821 $this->getFreshConditionMatcher();
822 $this->assertTrue($this->subject->match('[globalString = session:foo|bar = 1234567]'));
823 // Test expression language
824 $this->assertTrue($this->subject->match('[session("foo|bar") == 1234567]'));
825 }
826
827 /**
828 * Tests whether the generic fetching of variables works with the namespace 'ENV'.
829 *
830 * @test
831 */
832 public function genericGetVariablesSucceedsWithNamespaceENV(): void
833 {
834 $testKey = $this->getUniqueId('test');
835 putenv($testKey . '=testValue');
836 $this->getFreshConditionMatcher();
837 $this->assertTrue($this->subject->match('[globalString = ENV:' . $testKey . ' = testValue]'));
838 // Test expression language
839 $this->assertTrue($this->subject->match('[getenv("' . $testKey . '") == "testValue"]'));
840 }
841
842 /**
843 * Tests whether the generic fetching of variables works with the namespace 'IENV'.
844 *
845 * @test
846 */
847 public function genericGetVariablesSucceedsWithNamespaceIENV(): void
848 {
849 $_SERVER['HTTP_HOST'] = GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY') . ':1234567';
850 $this->getFreshConditionMatcher();
851 $this->assertTrue($this->subject->match('[globalString = IENV:TYPO3_PORT = 1234567]'));
852 // Test expression language
853 // @TODO: not work yet, looks like test setup issue
854 // $this->assertTrue($this->subject->match('[request.getNormalizedParams().getRequestPort() == 1234567]'));
855 }
856
857 /**
858 * Tests whether the generic fetching of variables works with any global namespace.
859 *
860 * @test
861 */
862 public function genericGetVariablesSucceedsWithAnyGlobalNamespace(): void
863 {
864 $GLOBALS[$this->testGlobalNamespace] = [
865 'first' => 'testFirst',
866 'second' => ['third' => 'testThird']
867 ];
868 $this->getFreshConditionMatcher();
869 $this->assertTrue($this->subject->match('[globalString = ' . $this->testGlobalNamespace . '|first = testFirst]'));
870 $this->assertTrue($this->subject->match('[globalString = ' . $this->testGlobalNamespace . '|second|third = testThird]'));
871 }
872
873 /**
874 * Tests whether any property of a site language matches the request
875 *
876 * @test
877 */
878 public function siteLanguageMatchesCondition(): void
879 {
880 $site = new Site('angelo', 13, [
881 'languages' => [
882 [
883 'languageId' => 0,
884 'title' => 'United States',
885 'locale' => 'en_US.UTF-8',
886 ],
887 [
888 'languageId' => 2,
889 'title' => 'UK',
890 'locale' => 'en_UK.UTF-8',
891 ]
892 ]
893 ]);
894 $GLOBALS['TYPO3_REQUEST'] = new ServerRequest();
895 $GLOBALS['TYPO3_REQUEST'] = $GLOBALS['TYPO3_REQUEST']->withAttribute('language', $site->getLanguageById(0));
896 $this->getFreshConditionMatcher();
897 $this->assertTrue($this->subject->match('[siteLanguage = locale = en_US.UTF-8]'));
898 $this->assertTrue($this->subject->match('[siteLanguage = locale = de_DE, locale = en_US.UTF-8]'));
899 // Test expression language
900 $this->assertTrue($this->subject->match('[siteLanguage("locale") == "en_US.UTF-8"]'));
901 $this->assertTrue($this->subject->match('[siteLanguage("locale") in ["de_DE", "en_US.UTF-8"]]'));
902 }
903
904 /**
905 * Tests whether any property of a site language does NOT match the request
906 *
907 * @test
908 */
909 public function siteLanguageDoesNotMatchCondition(): void
910 {
911 $site = new Site('angelo', 13, [
912 'languages' => [
913 [
914 'languageId' => 0,
915 'title' => 'United States',
916 'locale' => 'en_US.UTF-8',
917 ],
918 [
919 'languageId' => 2,
920 'title' => 'UK',
921 'locale' => 'en_UK.UTF-8',
922 ]
923 ]
924 ]);
925 $GLOBALS['TYPO3_REQUEST'] = new ServerRequest();
926 $GLOBALS['TYPO3_REQUEST'] = $GLOBALS['TYPO3_REQUEST']->withAttribute('language', $site->getLanguageById(0));
927 $this->getFreshConditionMatcher();
928 $this->assertFalse($this->subject->match('[siteLanguage = locale = en_UK.UTF-8]'));
929 $this->assertFalse($this->subject->match('[siteLanguage = locale = de_DE, title = UK]'));
930 // Test expression language
931 $this->assertFalse($this->subject->match('[siteLanguage("locale") == "en_UK.UTF-8"]'));
932 $this->assertFalse($this->subject->match('[siteLanguage("locale") == "de_DE" && siteLanguage("title") == "UK"]'));
933 }
934
935 /**
936 * Tests whether any property of a site matches the request
937 *
938 * @test
939 */
940 public function siteMatchesCondition(): void
941 {
942 $site = new Site('angelo', 13, ['languages' => [], 'base' => 'https://typo3.org/']);
943 $GLOBALS['TYPO3_REQUEST'] = new ServerRequest();
944 $GLOBALS['TYPO3_REQUEST'] = $GLOBALS['TYPO3_REQUEST']->withAttribute('site', $site);
945 $this->getFreshConditionMatcher();
946 $this->assertTrue($this->subject->match('[site = identifier = angelo]'));
947 $this->assertTrue($this->subject->match('[site = rootPageId = 13]'));
948 $this->assertTrue($this->subject->match('[site = base = https://typo3.org/]'));
949 // Test expression language
950 $this->assertTrue($this->subject->match('[site("identifier") == "angelo"]'));
951 $this->assertTrue($this->subject->match('[site("rootPageId") == 13]'));
952 $this->assertTrue($this->subject->match('[site("base") == "https://typo3.org/"]'));
953 }
954
955 /**
956 * Tests whether any property of a site that does NOT match the request
957 *
958 * @test
959 */
960 public function siteDoesNotMatchCondition(): void
961 {
962 $site = new Site('angelo', 13, [
963 'languages' => [
964 [
965 'languageId' => 0,
966 'title' => 'United States',
967 'locale' => 'en_US.UTF-8',
968 ],
969 [
970 'languageId' => 2,
971 'title' => 'UK',
972 'locale' => 'en_UK.UTF-8',
973 ]
974 ]
975 ]);
976 $GLOBALS['TYPO3_REQUEST'] = new ServerRequest();
977 $GLOBALS['TYPO3_REQUEST'] = $GLOBALS['TYPO3_REQUEST']->withAttribute('site', $site);
978 $this->getFreshConditionMatcher();
979 $this->assertFalse($this->subject->match('[site = identifier = berta]'));
980 $this->assertFalse($this->subject->match('[site = rootPageId = 14, rootPageId=23]'));
981 // Test expression language
982 $this->assertFalse($this->subject->match('[site("identifier") == "berta"]'));
983 $this->assertFalse($this->subject->match('[site("rootPageId") == 14 && site("rootPageId") == 23]'));
984 }
985
986 /**
987 * @test
988 */
989 public function matchThrowsExceptionIfConditionClassDoesNotInheritFromAbstractCondition(): void
990 {
991 $this->expectException(InvalidTypoScriptConditionException::class);
992 $this->expectExceptionCode(1410286153);
993 $this->getFreshConditionMatcher();
994 $loggerProphecy = $this->prophesize(Logger::class);
995 $this->subject->setLogger($loggerProphecy->reveal());
996 $this->subject->match('[stdClass = foo]');
997 }
998
999 /**
1000 * @test
1001 */
1002 public function matchCallsTestConditionAndHandsOverParameters(): void
1003 {
1004 $this->expectException(TestConditionException::class);
1005 $this->expectExceptionCode(1411581139);
1006 $this->getFreshConditionMatcher();
1007 $loggerProphecy = $this->prophesize(Logger::class);
1008 $this->subject->setLogger($loggerProphecy->reveal());
1009 $this->subject->match('[TYPO3\\CMS\\Frontend\\Tests\\Unit\\Configuration\\TypoScript\\ConditionMatching\\Fixtures\\TestCondition = 7, != 6]');
1010 }
1011 }