‪TYPO3CMS  10.4
PersistedPatternMapperTest.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 
29 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
30 use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
31 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
32 
33 class ‪PersistedPatternMapperTest extends FunctionalTestCase
34 {
35  private const ‪ASPECT_CONFIGURATION = [
36  'tableName' => 'tt_content',
37  'routeFieldName' => 'header',
38  // `layout` does not really much sense here since it's always `0`
39  // just to have an additional field for the pattern mapper
40  'routeFieldPattern' => '^(?P<header>.+)-(?P<layout>\d+)$',
41  'routeFieldResult' => '{header}-{layout}',
42  ];
43 
44  private const ‪SLUG_CONFIGURATION = [
45  'type' => 'slug',
46  'generatorOptions' => [
47  'prefixParentPageSlug' => false,
48  ],
49  'fallbackCharacter' => '-',
50  'eval' => 'required,uniqueInSite',
51  'default' => ''
52  ];
53 
54  private const ‪LANGUAGE_MAP = [
55  'es-es' => 3,
56  'fr-ca' => 2,
57  'fr-fr' => 1,
58  'default' => 0,
59  ];
60 
61  private const ‪SITE_ADDITION = [
62  'acme' => 0,
63  'other' => 4000,
64  ];
65 
69  private ‪$subject;
70 
74  private ‪$sites;
75 
76  public static function ‪setUpBeforeClass(): void
77  {
78  parent::setUpBeforeClass();
79  static::initializeDatabaseSnapshot();
80  }
81 
82  public static function ‪tearDownAfterClass(): void
83  {
84  static::destroyDatabaseSnapshot();
85  parent::tearDownAfterClass();
86  }
87 
88  protected function ‪setUp(): void
89  {
90  parent::setUp();
91  $this->withDatabaseSnapshot(function () {
92  $this->‪setUpDatabase();
93  });
94 
95  // declare tt_content.header as `slug` field having `uniqueInSite` set
96  $tableName = self::ASPECT_CONFIGURATION['tableName'];
97  $fieldName = self::ASPECT_CONFIGURATION['routeFieldName'];
98  ‪$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'] = ‪self::SLUG_CONFIGURATION;
99 
100  $languages = [
101  [
102  'languageId' => 3,
103  'base' => '/es-es/',
104  'locale' => 'es_ES.UTF-8',
105  'fallbackType' => 'fallback',
106  'fallbacks' => [0],
107  'title' => 'Spanish',
108  ],
109  [
110  'languageId' => 2,
111  'base' => '/fr-ca/',
112  'locale' => 'fr_CA.UTF-8',
113  'fallbackType' => 'fallback',
114  'fallbacks' => [1, 0],
115  'title' => 'Franco-Canadian',
116  ],
117  [
118  'languageId' => 1,
119  'base' => '/fr-fr/',
120  'locale' => 'fr_FR.UTF-8',
121  'fallbackType' => 'fallback',
122  'fallbacks' => [0],
123  'French',
124  ],
125  [
126  'languageId' => 0,
127  'base' => 'en_US.UTF-8',
128  'locale' => '/en-us/',
129  ],
130  ];
131  $this->sites = [
132  'acme' => new Site('acme-inc', 1000, [
133  'identifier' => 'acme-inc',
134  'rootPageId' => 1000,
135  'base' => 'https://acme.com/',
136  'languages' => $languages,
137  ]),
138  'other' => new Site('other-inc', 5000, [
139  'identifier' => 'other-inc',
140  'rootPageId' => 5000,
141  'base' => 'https://other.com/',
142  'languages' => $languages,
143  ]),
144  ];
145  $this->‪writeSiteConfiguration($this->sites['acme']);
146  $this->‪writeSiteConfiguration($this->sites['other']);
147  $this->subject = new PersistedPatternMapper(self::ASPECT_CONFIGURATION);
148  $this->subject->setSiteLanguage($this->sites['acme']->getLanguageById(0));
149  $this->subject->setSite($this->sites['acme']);
150  }
151 
152  protected function ‪setUpDatabase()
153  {
154  $backendUser = $this->setUpBackendUserFromFixture(1);
156 
157  $scenarioFile = __DIR__ . '/Fixtures/AspectScenario.yaml';
158  $factory = DataHandlerFactory::fromYamlFile($scenarioFile);
159  $writer = DataHandlerWriter::withBackendUser($backendUser);
160  $writer->invokeFactory($factory);
161  if (!empty($writer->getErrors())) {
162  self::fail(var_export($writer->getErrors(), true));
163  }
164  }
165 
166  protected function ‪tearDown(): void
167  {
168  unset($this->subject, $this->sites);
169  parent::tearDown();
170  }
171 
172  public function ‪languageAwareRecordsAreResolvedDataProvider(): array
173  {
174  $baseDataSet = [
175  'non-existing, default language' => ['this-value-does-not-exist', 'default', null],
176 
177  '30xx-slug, default language' => ['30xx-slug-0', 'default', '3010'],
178  '30xx-slug, fr-fr language' => ['30xx-slug-0', 'fr-fr', '3010'],
179  '30xx-slug, fr-ca language' => ['30xx-slug-0', 'fr-ca', '3010'],
180 
181  '30xx-slug-fr-ca, fr-ca language' => ['30xx-slug-fr-ca-0', 'fr-ca', '3010'],
182  // '30xx-slug-fr-ca-0' available in default language as well, fallbacks to that one
183  '30xx-slug-fr-ca, fr-fr language' => ['30xx-slug-fr-ca-0', 'fr-fr', '3030'],
184  // '30xx-slug-fr-ca-0' available in default language, use it directly
185  '30xx-slug-fr-ca, default language' => ['30xx-slug-fr-ca-0', 'default', '3030'],
186 
187  '30xx-slug-fr, fr-ca language' => ['30xx-slug-fr-0', 'fr-ca', '3010'],
188  '30xx-slug-fr, fr-fr language' => ['30xx-slug-fr-0', 'fr-fr', '3010'],
189  // '30xx-slug-fr-0' available in default language, use it directly
190  '30xx-slug-fr, default language' => ['30xx-slug-fr-0', 'default', '3020'],
191 
192  // basically the same, but being stored in reverse order in database
193  '40xx-slug, default language' => ['40xx-slug-0', 'default', '4040'],
194  '40xx-slug, fr-fr language' => ['40xx-slug-0', 'fr-fr', '4040'],
195  '40xx-slug, fr-ca language' => ['40xx-slug-0', 'fr-ca', '4040'],
196 
197  '40xx-slug-fr-ca, fr-ca language' => ['40xx-slug-fr-ca-0', 'fr-ca', '4040'],
198  // '40xx-slug-fr-ca-0' available in default language as well, fallbacks to that one
199  '40xx-slug-fr-ca, fr-fr language' => ['40xx-slug-fr-ca-0', 'fr-fr', '4030'],
200  // '40xx-slug-fr-ca-0' available in default language, use it directly
201  '40xx-slug-fr-ca, default language' => ['40xx-slug-fr-ca-0', 'default', '4030'],
202 
203  '40xx-slug-fr, fr-ca language' => ['40xx-slug-fr-0', 'fr-ca', '4040'],
204  '40xx-slug-fr, fr-fr language' => ['40xx-slug-fr-0', 'fr-fr', '4040'],
205  // '40xx-slug-fr-ca-0' available in default language, use it directly
206  '40xx-slug-fr, default language' => ['40xx-slug-fr-0', 'default', '4020'],
207  ];
208  // permute $baseDataSet to be either prepended
209  // with site identifier argument 'acme' or 'other'
210  $dataSet = [];
211  foreach (['acme', 'other'] as $site) {
212  foreach ($baseDataSet as $key => $arguments) {
213  array_unshift($arguments, $site);
214  $dataSet[$site . ':' . $key] = $arguments;
215  }
216  }
217  return $dataSet;
218  }
219 
229  public function ‪languageAwareRecordsAreResolved(string $identifier, string $requestValue, string $language, ?string $expectation): void
230  {
231  $this->subject->setSiteLanguage(
232  $this->sites[$identifier]->getLanguageById(self::LANGUAGE_MAP[$language])
233  );
234  $this->subject->setSite(
235  $this->sites[$identifier]
236  );
237  if ($expectation !== null) {
238  $expectation += self::SITE_ADDITION[$identifier];
239  $expectation = (string)$expectation;
240  }
241  self::assertSame($expectation, $this->subject->resolve($requestValue));
242  }
243 
244  public function ‪recordVisibilityDataProvider(): array
245  {
246  $rawContext = new ‪Context();
247  $visibleContext = new ‪Context();
248  $visibleContext->setAspect(
249  'visibility',
250  new ‪VisibilityAspect(false, true, false)
251  );
252  $frontendGroupsContext = new ‪Context();
253  $frontendGroupsContext->setAspect(
254  'frontend.user',
255  new ‪UserAspect(null, [13])
256  );
257  $scheduledContext = new ‪Context();
258  $scheduledContext->setAspect(
259  'date',
260  new ‪DateTimeAspect(new \DateTimeImmutable('@20000'))
261  );
262 
263  return [
264  'hidden-visibility-slug, raw context' => [
265  $rawContext,
266  ['slug' => 'hidden-visibility-slug-0', 'uid' => '4051'],
267  false,
268  ],
269  // fe_group slugs are always considered
270  'restricted-visibility-slug, raw context' => [
271  $rawContext,
272  ['slug' => 'restricted-visibility-slug-0', 'uid' => '4052'],
273  true,
274  ],
275  'scheduled-visibility-slug, raw context' => [
276  $rawContext,
277  ['slug' => 'scheduled-visibility-slug-0', 'uid' => '4053'],
278  false,
279  ],
280  'hidden-visibility-slug, visibility context (include hidden content)' => [
281  $visibleContext,
282  ['slug' => 'hidden-visibility-slug-0', 'uid' => '4051'],
283  true,
284  ],
285  // fe_group slugs are always considered
286  'restricted-visibility-slug, frontend-groups context (13)' => [
287  $frontendGroupsContext,
288  ['slug' => 'restricted-visibility-slug-0', 'uid' => '4052'],
289  true,
290  ],
291  'scheduled-visibility-slug, scheduled context (timestamp 20000)' => [
292  $scheduledContext,
293  ['slug' => 'scheduled-visibility-slug-0', 'uid' => '4053'],
294  false, // @todo actually `true`, Start-/EndTimeRestriction do not support Context, yet
295  ],
296  ];
297  }
298 
307  public function ‪recordVisibilityIsConsideredForResolving(Context $context, array $parameters, bool $expectation): void
308  {
309  $this->subject->setContext($context);
310  $expectedResult = $expectation ? $parameters['uid'] : null;
311  self::assertSame($expectedResult, $this->subject->resolve($parameters['slug']));
312  }
313 
322  public function ‪recordVisibilityIsConsideredForGeneration(Context $context, array $parameters, bool $expectation): void
323  {
324  $this->subject->setContext($context);
325  $expectedResult = $expectation ? $parameters['slug'] : null;
326  self::assertSame($expectedResult, $this->subject->generate($parameters['uid']));
327  }
328 
329  private function ‪writeSiteConfiguration(‪Site $site): void
330  {
331  try {
332  // ensure no previous site configuration influences the test
333  $path = $this->instancePath . '/typo3conf/sites';
334  ‪GeneralUtility::rmdir($path . '/' . $site->‪getIdentifier(), true);
335  GeneralUtility::makeInstance(SiteConfiguration::class, $path)->write($site->‪getIdentifier(), $site->‪getConfiguration());
336  } catch (\‪Exception $exception) {
337  self::markTestSkipped($exception->getMessage());
338  }
339  }
340 }
‪TYPO3\CMS\Core\Context\VisibilityAspect
Definition: VisibilityAspect.php:31
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest
Definition: PersistedPatternMapperTest.php:34
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\recordVisibilityIsConsideredForGeneration
‪recordVisibilityIsConsideredForGeneration(Context $context, array $parameters, bool $expectation)
Definition: PersistedPatternMapperTest.php:320
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\$subject
‪PersistedPatternMapper $subject
Definition: PersistedPatternMapperTest.php:68
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\writeSiteConfiguration
‪writeSiteConfiguration(Site $site)
Definition: PersistedPatternMapperTest.php:327
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\recordVisibilityDataProvider
‪recordVisibilityDataProvider()
Definition: PersistedPatternMapperTest.php:242
‪TYPO3\CMS\Core\Exception
Definition: Exception.php:22
‪TYPO3\CMS\Core\Routing\Aspect\PersistedPatternMapper
Definition: PersistedPatternMapper.php:58
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect
Definition: PersistedAliasMapperTest.php:18
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:53
‪TYPO3\CMS\Core\Configuration\SiteConfiguration
Definition: SiteConfiguration.php:41
‪TYPO3\CMS\Core\Site\Entity\Site\getConfiguration
‪array getConfiguration()
Definition: Site.php:311
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\$sites
‪Site[] $sites
Definition: PersistedPatternMapperTest.php:72
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:40
‪TYPO3\CMS\Core\Site\Entity\Site\getIdentifier
‪string getIdentifier()
Definition: Site.php:177
‪TYPO3\CMS\Core\Core\Bootstrap\initializeLanguageObject
‪static initializeLanguageObject()
Definition: Bootstrap.php:617
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\ASPECT_CONFIGURATION
‪const ASPECT_CONFIGURATION
Definition: PersistedPatternMapperTest.php:35
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\recordVisibilityIsConsideredForResolving
‪recordVisibilityIsConsideredForResolving(Context $context, array $parameters, bool $expectation)
Definition: PersistedPatternMapperTest.php:305
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\tearDownAfterClass
‪static tearDownAfterClass()
Definition: PersistedPatternMapperTest.php:80
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\setUp
‪setUp()
Definition: PersistedPatternMapperTest.php:86
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\LANGUAGE_MAP
‪const LANGUAGE_MAP
Definition: PersistedPatternMapperTest.php:54
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\languageAwareRecordsAreResolved
‪languageAwareRecordsAreResolved(string $identifier, string $requestValue, string $language, ?string $expectation)
Definition: PersistedPatternMapperTest.php:227
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\SITE_ADDITION
‪const SITE_ADDITION
Definition: PersistedPatternMapperTest.php:61
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\setUpDatabase
‪setUpDatabase()
Definition: PersistedPatternMapperTest.php:150
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\SLUG_CONFIGURATION
‪const SLUG_CONFIGURATION
Definition: PersistedPatternMapperTest.php:44
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Core\Bootstrap
Definition: Bootstrap.php:66
‪TYPO3\CMS\Core\Utility\GeneralUtility\rmdir
‪static bool rmdir($path, $removeNonEmpty=false)
Definition: GeneralUtility.php:2075
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\tearDown
‪tearDown()
Definition: PersistedPatternMapperTest.php:164
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\setUpBeforeClass
‪static setUpBeforeClass()
Definition: PersistedPatternMapperTest.php:74
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Tests\Functional\Routing\Aspect\PersistedPatternMapperTest\languageAwareRecordsAreResolvedDataProvider
‪languageAwareRecordsAreResolvedDataProvider()
Definition: PersistedPatternMapperTest.php:170
‪TYPO3\CMS\Core\Context\DateTimeAspect
Definition: DateTimeAspect.php:35
‪TYPO3\CMS\Core\Context\UserAspect
Definition: UserAspect.php:38