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