‪TYPO3CMS  ‪main
LocalizedSiteContentRenderingTest.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 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
23 use TYPO3\TestingFramework\Core\Functional\Framework\Constraint\RequestSection\DoesNotHaveRecordConstraint;
24 use TYPO3\TestingFramework\Core\Functional\Framework\Constraint\RequestSection\HasRecordConstraint;
25 use TYPO3\TestingFramework\Core\Functional\Framework\Constraint\RequestSection\StructureDoesNotHaveRecordConstraint;
26 use TYPO3\TestingFramework\Core\Functional\Framework\Constraint\RequestSection\StructureHasRecordConstraint;
27 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
28 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\ResponseContent;
29 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
30 
105 final class ‪LocalizedSiteContentRenderingTest extends FunctionalTestCase
106 {
108 
109  protected array ‪$pathsToLinkInTestInstance = [
110  'typo3/sysext/frontend/Tests/Functional/Fixtures/Images' => 'fileadmin/user_upload',
111  ];
112 
113  private const ‪VALUE_PageId = 89;
114  private const ‪TABLE_Content = 'tt_content';
115  private const ‪LANGUAGE_PRESETS = [
116  'EN' => ['id' => 0, 'title' => 'English', 'locale' => 'en_US.UTF8'],
117  'DA' => ['id' => 1, 'title' => 'Dansk', 'locale' => 'da_DK.UTF8'],
118  'DE' => ['id' => 2, 'title' => 'Deutsch', 'locale' => 'de_DE.UTF8'],
119  'PL' => ['id' => 3, 'title' => 'Polski', 'locale' => 'pl_PL.UTF8'],
120  ];
121 
122  protected function ‪setUp(): void
123  {
124  parent::setUp();
125  $this->importCSVDataSet(__DIR__ . '/../Fixtures/sys_file_storage.csv');
126  $this->importCSVDataSet(__DIR__ . '/DataSet/LiveDefaultPages.csv');
127  $this->importCSVDataSet(__DIR__ . '/DataSet/LiveDefaultElements.csv');
128  $this->setUpFrontendRootPage(1, [
129  'EXT:core/Tests/Functional/Fixtures/Frontend/JsonRenderer.typoscript',
130  ]);
131  }
132 
136  private function ‪getNonVisibleHeaders(array $visibleHeaders): array
137  {
138  $allElements = [
139  'Regular Element #1',
140  'Regular Element #2',
141  'Regular Element #3',
142  'Hidden Element #4',
143  '[Translate to Dansk:] Regular Element #1',
144  '[Translate to Dansk:] Regular Element #3',
145  '[DA] Without default language',
146  '[DA] UnHidden Element #4',
147  '[DE] Without default language',
148  '[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1',
149  '[Translate to Polski:] Regular Element #1',
150  '[PL] Without default language',
151  '[PL] Hidden Regular Element #2',
152  ];
153  return array_diff($allElements, $visibleHeaders);
154  }
155 
159  private function ‪getNonVisibleFileTitles(array $visibleTitles): array
160  {
161  $allElements = [
162  'T3BOARD',
163  'Kasper',
164  '[Kasper] Image translated to Dansk',
165  '[T3BOARD] Image added in Dansk (without parent)',
166  '[T3BOARD] Image added to DA element without default language',
167  '[T3BOARD] image translated to DE from DA',
168  'Kasper2',
169  ];
170  return array_diff($allElements, $visibleTitles);
171  }
172 
177  #[Test]
179  {
181  'test',
182  $this->‪buildSiteConfiguration(1, 'https://website.local/'),
183  [
184  $this->‪buildDefaultLanguageConfiguration('EN', '/en/'),
185  $this->‪buildLanguageConfiguration('DA', '/da/'),
186  ],
187  $this->‪buildErrorHandlingConfiguration('Fluid', [404]),
188  );
189 
190  $response = $this->executeFrontendSubRequest(
191  new InternalRequest('https://website.local/en/?id=' . self::VALUE_PageId)
192  );
193  $responseStructure = ResponseContent::fromString((string)$response->getBody());
194 
195  $responseSections = $responseStructure->getSections();
196  $visibleHeaders = ['Regular Element #1', 'Regular Element #2', 'Regular Element #3'];
197  self::assertThat(
198  $responseSections,
199  (new HasRecordConstraint())
200  ->setTable(self::TABLE_Content)
201  ->setField('header')
202  ->setValues(...$visibleHeaders)
203  );
204  self::assertThat(
205  $responseSections,
206  (new DoesNotHaveRecordConstraint())
207  ->setTable(self::TABLE_Content)
208  ->setField('header')
209  ->setValues(...$this->‪getNonVisibleHeaders($visibleHeaders))
210  );
211 
212  //assert FAL relations
213  $visibleFiles = ['T3BOARD'];
214  self::assertThat($responseSections, (new StructureHasRecordConstraint())
215  ->setRecordIdentifier(self::TABLE_Content . ':297')->setRecordField('image')
216  ->setTable('sys_file_reference')->setField('title')->setValues(...$visibleFiles));
217 
218  self::assertThat($responseSections, (new StructureDoesNotHaveRecordConstraint())
219  ->setRecordIdentifier(self::TABLE_Content . ':297')->setRecordField('image')
220  ->setTable('sys_file_reference')->setField('title')->setValues(...$this->‪getNonVisibleFileTitles($visibleFiles)));
221 
222  $visibleFiles = ['Kasper2'];
223  self::assertThat($responseSections, (new StructureHasRecordConstraint())
224  ->setRecordIdentifier(self::TABLE_Content . ':298')->setRecordField('image')
225  ->setTable('sys_file_reference')->setField('title')->setValues(...$visibleFiles));
226 
227  self::assertThat($responseSections, (new StructureDoesNotHaveRecordConstraint())
228  ->setRecordIdentifier(self::TABLE_Content . ':298')->setRecordField('image')
229  ->setTable('sys_file_reference')->setField('title')->setValues(...$this->‪getNonVisibleFileTitles($visibleFiles)));
230 
231  // Assert language settings and page record title
232  self::assertEquals('Default language Page', $responseStructure->getScopePath('page/title'));
233  self::assertEquals(0, $responseStructure->getScopePath('languageInfo/id'), 'languageId does not match');
234  self::assertEquals(0, $responseStructure->getScopePath('languageInfo/contentId'), 'contentId does not match');
235  self::assertEquals('strict', $responseStructure->getScopePath('languageInfo/fallbackType'), 'fallbackType does not match');
236  self::assertEquals('pageNotFound', $responseStructure->getScopePath('languageInfo/fallbackChain'), 'fallbackChain does not match');
237  self::assertEquals('includeFloating', $responseStructure->getScopePath('languageInfo/overlayType'), 'language overlayType does not match');
238  }
239 
243  public static function ‪dutchDataProvider(): array
244  {
245  return [
246  [
247  // Only records with language=1 are shown
248  'languageConfiguration' => [
249  'fallbackType' => 'free',
250  ],
251  'visibleRecords' => [
252  300 => [
253  'header' => '[Translate to Dansk:] Regular Element #3',
254  'image' => ['[Kasper] Image translated to Dansk', '[T3BOARD] Image added in Dansk (without parent)'],
255  ],
256  301 => [
257  'header' => '[Translate to Dansk:] Regular Element #1',
258  'image' => [],
259  ],
260  303 => [
261  'header' => '[DA] Without default language',
262  'image' => ['[T3BOARD] Image added to DA element without default language'],
263  ],
264  308 => [
265  'header' => '[DA] UnHidden Element #4',
266  'image' => [],
267  ],
268  ],
269  'fallbackType' => 'free',
270  'fallbackChain' => 'pageNotFound',
271  'overlayMode' => 'off',
272  ],
273  // Expected behaviour:
274  // Not translated element #2 is shown because "fallback" is enabled, which defaults to L=0 elements
275  [
276  'languageConfiguration' => [
277  'fallbackType' => 'fallback',
278  ],
279  'visibleRecords' => [
280  297 => [
281  'header' => '[Translate to Dansk:] Regular Element #1',
282  'image' => [],
283  ],
284  298 => [
285  'header' => 'Regular Element #2',
286  'image' => ['Kasper2'],
287  ],
288  299 => [
289  'header' => '[Translate to Dansk:] Regular Element #3',
290  'image' => ['[Kasper] Image translated to Dansk', '[T3BOARD] Image added in Dansk (without parent)'],
291  ],
292  ],
293  'fallbackType' => 'fallback',
294  'fallbackChain' => 'pageNotFound',
295  'overlayMode' => 'mixed',
296  ],
297  // Expected behaviour:
298  // Non translated default language elements are not shown, but the results include the records without default language as well
299  [
300  'languageConfiguration' => [
301  'fallbackType' => 'strict',
302  ],
303  'visibleRecords' => [
304  297 => [
305  'header' => '[Translate to Dansk:] Regular Element #1',
306  'image' => [],
307  ],
308  299 => [
309  'header' => '[Translate to Dansk:] Regular Element #3',
310  'image' => ['[Kasper] Image translated to Dansk', '[T3BOARD] Image added in Dansk (without parent)'],
311  ],
312  303 => [
313  'header' => '[DA] Without default language',
314  'image' => ['[T3BOARD] Image added to DA element without default language'],
315  ],
316  ],
317  'fallbackType' => 'strict',
318  'fallbackChain' => 'pageNotFound',
319  'overlayMode' => 'includeFloating',
320  ],
321  ];
322  }
323 
328  #[DataProvider('dutchDataProvider')]
329  #[Test]
330  public function ‪renderingOfDutchLanguage(array $languageConfiguration, array $visibleRecords, string $fallbackType, string $fallbackChain, string $overlayMode): void
331  {
333  'test',
334  $this->‪buildSiteConfiguration(1, 'https://website.local/'),
335  [
336  $this->‪buildDefaultLanguageConfiguration('EN', '/en/'),
337  $this->‪buildLanguageConfiguration('DA', '/da/', $languageConfiguration['fallbackChain'] ?? [], $languageConfiguration['fallbackType']),
338  ],
339  $this->‪buildErrorHandlingConfiguration('Fluid', [404]),
340  );
341 
342  $response = $this->executeFrontendSubRequest(
343  new InternalRequest('https://website.local/da/?id=' . self::VALUE_PageId)
344  );
345  $responseStructure = ResponseContent::fromString((string)$response->getBody());
346  $responseSections = $responseStructure->getSections();
347  $visibleHeaders = array_map(static function ($element) {
348  return $element['header'];
349  }, $visibleRecords);
350 
351  self::assertThat(
352  $responseSections,
353  (new HasRecordConstraint())
354  ->setTable(self::TABLE_Content)
355  ->setField('header')
356  ->setValues(...$visibleHeaders)
357  );
358  self::assertThat(
359  $responseSections,
360  (new DoesNotHaveRecordConstraint())
361  ->setTable(self::TABLE_Content)
362  ->setField('header')
363  ->setValues(...$this->‪getNonVisibleHeaders($visibleHeaders))
364  );
365 
366  foreach ($visibleRecords as $ttContentUid => $properties) {
367  $visibleFileTitles = $properties['image'];
368  if (!empty($visibleFileTitles)) {
369  self::assertThat($responseSections, (new StructureHasRecordConstraint())
370  ->setRecordIdentifier(self::TABLE_Content . ':' . $ttContentUid)->setRecordField('image')
371  ->setTable('sys_file_reference')->setField('title')->setValues(...$visibleFileTitles));
372  }
373  self::assertThat($responseSections, (new StructureDoesNotHaveRecordConstraint())
374  ->setRecordIdentifier(self::TABLE_Content . ':' . $ttContentUid)->setRecordField('image')
375  ->setTable('sys_file_reference')->setField('title')->setValues(...$this->‪getNonVisibleFileTitles($visibleFileTitles)));
376  }
377 
378  self::assertEquals('[DA]Page', $responseStructure->getScopePath('page/title'));
379  self::assertEquals(1, $responseStructure->getScopePath('languageInfo/id'), 'languageId does not match');
380  self::assertEquals(1, $responseStructure->getScopePath('languageInfo/contentId'), 'contentId does not match');
381  self::assertEquals($fallbackType, $responseStructure->getScopePath('languageInfo/fallbackType'), 'fallbackType does not match');
382  self::assertEquals($fallbackChain, $responseStructure->getScopePath('languageInfo/fallbackChain'), 'fallbackChain does not match');
383  self::assertEquals($overlayMode, $responseStructure->getScopePath('languageInfo/overlayType'), 'language overlayType does not match');
384  }
385 
386  public static function ‪contentOnNonTranslatedPageDataProvider(): array
387  {
388  //Expected behaviour:
389  //the page is NOT translated so setting sys_language_mode to different values changes the results
390  //- setting sys_language_mode to empty value makes TYPO3 return default language records
391  //- setting it to strict throws 404, independently from other settings
392  //Setting config.sys_language_overlay = 0
393  return [
394  [
395  'languageConfiguration' => [
396  'fallbackType' => 'free',
397  'fallbackChain' => ['EN'],
398  ],
399  'visibleRecords' => [
400  297 => [
401  'header' => 'Regular Element #1',
402  'image' => ['T3BOARD'],
403  ],
404  298 => [
405  'header' => 'Regular Element #2',
406  'image' => ['Kasper2'],
407  ],
408  299 => [
409  'header' => 'Regular Element #3',
410  'image' => ['Kasper'],
411  ],
412  ],
413  'pageTitle' => 'Default language Page',
414  'languageId' => 2,
415  'contentLanguageId' => 0,
416  'fallbackType' => 'free',
417  'fallbackChain' => '0,pageNotFound',
418  'overlayMode' => 'off',
419  ],
420  [
421  'languageConfiguration' => [
422  'fallbackType' => 'free',
423  'fallbackChain' => ['EN'],
424  ],
425  'visibleRecords' => [
426  297 => [
427  'header' => 'Regular Element #1',
428  'image' => ['T3BOARD'],
429  ],
430  298 => [
431  'header' => 'Regular Element #2',
432  'image' => ['Kasper2'],
433  ],
434  299 => [
435  'header' => 'Regular Element #3',
436  'image' => ['Kasper'],
437  ],
438  ],
439  'pageTitle' => 'Default language Page',
440  'languageId' => 2,
441  'contentLanguageId' => 0,
442  'fallbackType' => 'free',
443  'fallbackChain' => '0,pageNotFound',
444  'overlayMode' => 'off',
445  ],
446  [
447  'languageConfiguration' => [
448  'fallbackType' => 'free',
449  'fallbackChain' => ['DA', 'EN'],
450  ],
451  'visibleRecords' => [
452  300 => [
453  'header' => '[Translate to Dansk:] Regular Element #3',
454  'image' => ['[Kasper] Image translated to Dansk', '[T3BOARD] Image added in Dansk (without parent)'],
455  ],
456  301 => [
457  'header' => '[Translate to Dansk:] Regular Element #1',
458  'image' => [],
459  ],
460  303 => [
461  'header' => '[DA] Without default language',
462  'image' => ['[T3BOARD] Image added to DA element without default language'],
463  ],
464  308 => [
465  'header' => '[DA] UnHidden Element #4',
466  'image' => [],
467  ],
468  ],
469  'pageTitle' => '[DA]Page',
470  'languageId' => 2,
471  'contentLanguageId' => 1,
472  'fallbackType' => 'free',
473  'fallbackChain' => '1,0,pageNotFound',
474  'overlayMode' => 'off',
475  ],
476  [
477  'languageConfiguration' => [
478  'fallbackType' => 'free',
479  ],
480  'visibleRecords' => [],
481  'pageTitle' => '',
482  'languageId' => 2,
483  'contentLanguageId' => 2,
484  'fallbackType' => 'free',
485  'fallbackChain' => 'pageNotFound',
486  'overlayMode' => 'off',
487  'statusCode' => 404,
488  ],
489  [
490  'languageConfiguration' => [
491  'fallbackType' => 'fallback',
492  'fallbackChain' => ['EN'],
493  ],
494  'visibleRecords' => [
495  297 => [
496  'header' => 'Regular Element #1',
497  'image' => ['T3BOARD'],
498  ],
499  298 => [
500  'header' => 'Regular Element #2',
501  'image' => ['Kasper2'],
502  ],
503  299 => [
504  'header' => 'Regular Element #3',
505  'image' => ['Kasper'],
506  ],
507  ],
508  'pageTitle' => 'Default language Page',
509  'languageId' => 2,
510  'contentLanguageId' => 0,
511  'fallbackType' => 'fallback',
512  'fallbackChain' => '0,pageNotFound',
513  'overlayMode' => 'mixed',
514  ],
515  //falling back to default language
516  [
517  'languageConfiguration' => [
518  'fallbackType' => 'fallback',
519  'fallbackChain' => ['EN'],
520  ],
521  'visibleRecords' => [
522  297 => [
523  'header' => 'Regular Element #1',
524  'image' => ['T3BOARD'],
525  ],
526  298 => [
527  'header' => 'Regular Element #2',
528  'image' => ['Kasper2'],
529  ],
530  299 => [
531  'header' => 'Regular Element #3',
532  'image' => ['Kasper'],
533  ],
534  ],
535  'pageTitle' => 'Default language Page',
536  'languageId' => 2,
537  'contentLanguageId' => 0,
538  'fallbackType' => 'fallback',
539  'fallbackChain' => '0,pageNotFound',
540  'overlayMode' => 'mixed',
541  ],
542  //Dutch elements are shown because of the content fallback 1,0 - first Dutch, then default language
543  //note that '[DA] Without default language' is NOT shown - due to overlays (fetch default language and overlay it with translations)
544  [
545  'languageConfiguration' => [
546  'fallbackType' => 'fallback',
547  'fallbackChain' => ['DA', 'EN'],
548  ],
549  'visibleRecords' => [
550  297 => [
551  'header' => '[Translate to Dansk:] Regular Element #1',
552  'image' => [],
553  ],
554  298 => [
555  'header' => 'Regular Element #2',
556  'image' => ['Kasper2'],
557  ],
558  299 => [
559  'header' => '[Translate to Dansk:] Regular Element #3',
560  'image' => ['[Kasper] Image translated to Dansk', '[T3BOARD] Image added in Dansk (without parent)'],
561  ],
562  ],
563  'pageTitle' => '[DA]Page',
564  'languageId' => 2,
565  'contentLanguageId' => 1,
566  'fallbackType' => 'fallback',
567  'fallbackChain' => '1,0,pageNotFound',
568  'overlayMode' => 'mixed',
569  ],
570  [
571  'languageConfiguration' => [
572  'fallbackType' => 'fallback',
573  'fallbackChain' => [],
574  ],
575  'visibleRecords' => [],
576  'pageTitle' => '',
577  'languageId' => 2,
578  'contentLanguageId' => 0,
579  'fallbackType' => 'fallback',
580  'fallbackChain' => 'pageNotFound',
581  'overlayMode' => 'mixed',
582  'statusCode' => 404,
583  ],
584  [
585  'languageConfiguration' => [
586  'fallbackType' => 'strict',
587  'fallbackChain' => ['EN'],
588  ],
589  'visibleRecords' => [
590  297 => [
591  'header' => 'Regular Element #1',
592  'image' => ['T3BOARD'],
593  ],
594  298 => [
595  'header' => 'Regular Element #2',
596  'image' => ['Kasper2'],
597  ],
598  299 => [
599  'header' => 'Regular Element #3',
600  'image' => ['Kasper'],
601  ],
602  ],
603  'pageTitle' => 'Default language Page',
604  'languageId' => 2,
605  'contentLanguageId' => 0,
606  'fallbackType' => 'strict',
607  'fallbackChain' => '0,pageNotFound',
608  'overlayMode' => 'includeFloating',
609  ],
610  [
611  'languageConfiguration' => [
612  'fallbackType' => 'strict',
613  'fallbackChain' => ['DA', 'EN'],
614  ],
615  'visibleRecords' => [
616  297 => [
617  'header' => '[Translate to Dansk:] Regular Element #1',
618  'image' => [],
619  ],
620  299 => [
621  'header' => '[Translate to Dansk:] Regular Element #3',
622  'image' => ['[Kasper] Image translated to Dansk', '[T3BOARD] Image added in Dansk (without parent)'],
623  ],
624  303 => [
625  'header' => '[DA] Without default language',
626  'image' => ['[T3BOARD] Image added to DA element without default language'],
627  ],
628  ],
629  'pageTitle' => '[DA]Page',
630  'languageId' => 2,
631  'contentLanguageId' => 1,
632  'fallbackType' => 'strict',
633  'fallbackChain' => '1,0,pageNotFound',
634  'overlayMode' => 'includeFloating',
635  ],
636  [
637  'languageConfiguration' => [
638  'fallbackType' => 'strict',
639  'fallbackChain' => [],
640  ],
641  'visibleRecords' => [],
642  'pageTitle' => '',
643  'languageId' => 2,
644  'contentLanguageId' => 1,
645  'fallbackType' => 'strict',
646  'fallbackChain' => 'pageNotFound',
647  'overlayMode' => 'includeFloating',
648  'statusCode' => 404,
649  ],
650  ];
651  }
652 
659  #[DataProvider('contentOnNonTranslatedPageDataProvider')]
660  #[Test]
661  public function ‪contentOnNonTranslatedPageGerman(array $languageConfiguration, array $visibleRecords, string $pageTitle, int $languageId, int $contentLanguageId, string $fallbackType, string $fallbackChain, string $overlayMode, int ‪$statusCode = 200): void
662  {
664  'main',
665  $this->‪buildSiteConfiguration(1, 'https://website.local/'),
666  [
667  $this->‪buildDefaultLanguageConfiguration('EN', '/en/'),
668  $this->‪buildLanguageConfiguration('DA', '/da/'),
669  $this->‪buildLanguageConfiguration('DE', '/de/', $languageConfiguration['fallbackChain'] ?? [], $languageConfiguration['fallbackType']),
670  ],
671  $this->‪buildErrorHandlingConfiguration('Fluid', [404]),
672  );
673 
674  $response = $this->executeFrontendSubRequest(
675  new InternalRequest('https://website.local/de/?id=' . self::VALUE_PageId)
676  );
677 
678  if (‪$statusCode === 200) {
679  $visibleHeaders = array_column($visibleRecords, 'header');
680  $responseStructure = ResponseContent::fromString((string)$response->getBody());
681  $responseSections = $responseStructure->getSections();
682 
683  self::assertThat(
684  $responseSections,
685  (new HasRecordConstraint())
686  ->setTable(self::TABLE_Content)
687  ->setField('header')
688  ->setValues(...$visibleHeaders)
689  );
690  self::assertThat(
691  $responseSections,
692  (new DoesNotHaveRecordConstraint())
693  ->setTable(self::TABLE_Content)
694  ->setField('header')
695  ->setValues(...$this->‪getNonVisibleHeaders($visibleHeaders))
696  );
697 
698  foreach ($visibleRecords as $ttContentUid => $properties) {
699  $visibleFileTitles = $properties['image'];
700  if (!empty($visibleFileTitles)) {
701  self::assertThat($responseSections, (new StructureHasRecordConstraint())
702  ->setRecordIdentifier(self::TABLE_Content . ':' . $ttContentUid)->setRecordField('image')
703  ->setTable('sys_file_reference')->setField('title')->setValues(...$visibleFileTitles));
704  }
705  self::assertThat($responseSections, (new StructureDoesNotHaveRecordConstraint())
706  ->setRecordIdentifier(self::TABLE_Content . ':' . $ttContentUid)->setRecordField('image')
707  ->setTable('sys_file_reference')->setField('title')->setValues(...$this->‪getNonVisibleFileTitles($visibleFileTitles)));
708  }
709 
710  self::assertEquals($pageTitle, $responseStructure->getScopePath('page/title'));
711  self::assertEquals($languageId, $responseStructure->getScopePath('languageInfo/id'), 'languageId does not match');
712  self::assertEquals($contentLanguageId, $responseStructure->getScopePath('languageInfo/contentId'), 'contentId does not match');
713  self::assertEquals($fallbackType, $responseStructure->getScopePath('languageInfo/fallbackType'), 'fallbackType does not match');
714  self::assertEquals($fallbackChain, $responseStructure->getScopePath('languageInfo/fallbackChain'), 'fallbackChain does not match');
715  self::assertEquals($overlayMode, $responseStructure->getScopePath('languageInfo/overlayType'), 'language overlayType does not match');
716  }
717  self::assertEquals(‪$statusCode, $response->getStatusCode());
718  }
719 
720  public static function ‪contentOnPartiallyTranslatedPageDataProvider(): \Generator
721  {
722  //Expected behaviour:
723  //Setting sys_language_mode to different values doesn't influence the result as the requested page is translated to Polish,
724  //Page title is always [PL]Page, and both languageId/contentId are always 3
725  yield 'free' => [
726  'languageConfiguration' => [
727  'fallbackType' => 'free',
728  ],
729  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', '[PL] Without default language'],
730  'fallbackType' => 'free',
731  'fallbackChain' => 'pageNotFound',
732  'overlayMode' => 'off',
733  ];
734  yield 'free with fallback' => [
735  'languageConfiguration' => [
736  'fallbackType' => 'free',
737  'fallbackChain' => ['EN'],
738  ],
739  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', '[PL] Without default language'],
740  'fallbackType' => 'free',
741  'fallbackChain' => '0,pageNotFound',
742  'overlayMode' => 'off',
743  ];
744  yield 'free with multiple fallbacks' => [
745  'languageConfiguration' => [
746  'fallbackType' => 'free',
747  'fallbackChain' => ['DA', 'EN'],
748  ],
749  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', '[PL] Without default language'],
750  'fallbackType' => 'free',
751  'fallbackChain' => '1,0,pageNotFound',
752  'overlayMode' => 'off',
753  ];
754  // Expected behaviour:
755  // Not translated element #2 is shown because sys_language_overlay = 1 (with sys_language_overlay = hideNonTranslated, it would be hidden)
756  yield 'fallback to EN' => [
757  'languageConfiguration' => [
758  'fallbackType' => 'fallback',
759  'fallbackChain' => ['EN'],
760  ],
761  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', 'Regular Element #2', 'Regular Element #3'],
762  'fallbackType' => 'fallback',
763  'fallbackChain' => '0,pageNotFound',
764  'overlayMode' => 'mixed',
765  ];
766  // Expected behaviour:
767  // Element #3 is not translated in PL, but it is translated in DA. The DA version is shown as it has a fallback chain defined.
768  yield 'fallback with multiple languages' => [
769  'languageConfiguration' => [
770  'fallbackType' => 'fallback',
771  'fallbackChain' => ['DA', 'EN'],
772  ],
773  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', 'Regular Element #2', '[Translate to Dansk:] Regular Element #3'],
774  'fallbackType' => 'fallback',
775  'fallbackChain' => '1,0,pageNotFound',
776  'overlayMode' => 'mixed',
777  ];
778  yield 'fallback but without languages' => [
779  'languageConfiguration' => [
780  'fallbackType' => 'fallback',
781  'fallbackChain' => [],
782  ],
783  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', 'Regular Element #2', 'Regular Element #3'],
784  'fallbackType' => 'fallback',
785  'fallbackChain' => 'pageNotFound',
786  'overlayMode' => 'mixed',
787  ];
788  // Expected behaviour:
789  // Non translated default language elements are not shown, because of hideNonTranslated
790  yield 'strict with fallback to default' => [
791  'languageConfiguration' => [
792  'fallbackType' => 'strict',
793  'fallbackChain' => ['EN'],
794  ],
795  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', '[PL] Without default language'],
796  'fallbackType' => 'strict',
797  'fallbackChain' => '0,pageNotFound',
798  'overlayMode' => 'includeFloating',
799  ];
800  yield 'strict with multiple fallbacks' => [
801  'languageConfiguration' => [
802  'fallbackType' => 'strict',
803  'fallbackChain' => ['DA', 'EN'],
804  ],
805  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', '[PL] Without default language'],
806  'fallbackType' => 'strict',
807  'fallbackChain' => '1,0,pageNotFound',
808  'overlayMode' => 'includeFloating',
809  ];
810  yield 'strict without fallback' => [
811  'languageConfiguration' => [
812  'fallbackType' => 'strict',
813  'fallbackChain' => [],
814  ],
815  'visibleRecordHeaders' => ['[Translate to Polski:] Regular Element #1', '[PL] Without default language'],
816  'fallbackType' => 'strict',
817  'fallbackChain' => 'pageNotFound',
818  'overlayMode' => 'includeFloating',
819  ];
820  }
821 
825  #[DataProvider('contentOnPartiallyTranslatedPageDataProvider')]
826  #[Test]
827  public function ‪contentOnPartiallyTranslatedPage(array $languageConfiguration, array $visibleRecordHeaders, string $fallbackType, string $fallbackChain, string $overlayMode): void
828  {
830  'test',
831  $this->‪buildSiteConfiguration(1, 'https://website.local/'),
832  [
833  $this->‪buildDefaultLanguageConfiguration('EN', '/en/'),
834  $this->‪buildLanguageConfiguration('DA', '/da/'),
835  $this->‪buildLanguageConfiguration('PL', '/pl/', $languageConfiguration['fallbackChain'] ?? [], $languageConfiguration['fallbackType']),
836  ],
837  $this->‪buildErrorHandlingConfiguration('Fluid', [404]),
838  );
839 
840  $response = $this->executeFrontendSubRequest(
841  new InternalRequest('https://website.local/pl/?id=' . self::VALUE_PageId)
842  );
843  $responseStructure = ResponseContent::fromString((string)$response->getBody());
844  $responseSections = $responseStructure->getSections();
845 
846  self::assertEquals(200, $response->getStatusCode());
847 
848  self::assertThat(
849  $responseSections,
850  (new HasRecordConstraint())
851  ->setTable(self::TABLE_Content)
852  ->setField('header')
853  ->setValues(...$visibleRecordHeaders)
854  );
855  self::assertThat(
856  $responseSections,
857  (new DoesNotHaveRecordConstraint())
858  ->setTable(self::TABLE_Content)
859  ->setField('header')
860  ->setValues(...$this->‪getNonVisibleHeaders($visibleRecordHeaders))
861  );
862 
863  self::assertEquals('[PL]Page', $responseStructure->getScopePath('page/title'));
864  self::assertEquals(3, $responseStructure->getScopePath('languageInfo/id'), 'languageId does not match');
865  self::assertEquals(3, $responseStructure->getScopePath('languageInfo/contentId'), 'contentId does not match');
866  self::assertEquals($fallbackType, $responseStructure->getScopePath('languageInfo/fallbackType'), 'fallbackType does not match');
867  self::assertEquals($fallbackChain, $responseStructure->getScopePath('languageInfo/fallbackChain'), 'fallbackChain does not match');
868  self::assertEquals($overlayMode, $responseStructure->getScopePath('languageInfo/overlayType'), 'language overlayType does not match');
869  }
870 }
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildLanguageConfiguration
‪buildLanguageConfiguration(string $identifier, string $base, array $fallbackIdentifiers=[], string $fallbackType=null)
Definition: SiteBasedTestTrait.php:108
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait
Definition: SiteBasedTestTrait.php:37
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\writeSiteConfiguration
‪writeSiteConfiguration(string $identifier, array $site=[], array $languages=[], array $errorHandling=[])
Definition: SiteBasedTestTrait.php:50
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest
Definition: LocalizedSiteContentRenderingTest.php:106
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildSiteConfiguration
‪buildSiteConfiguration(int $rootPageId, string $base='')
Definition: SiteBasedTestTrait.php:88
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\$pathsToLinkInTestInstance
‪array $pathsToLinkInTestInstance
Definition: LocalizedSiteContentRenderingTest.php:108
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildErrorHandlingConfiguration
‪buildErrorHandlingConfiguration(string $handler, array $codes)
Definition: SiteBasedTestTrait.php:142
‪TYPO3\CMS\Redirects\Message\$statusCode
‪identifier readonly UriInterface readonly int $statusCode
Definition: RedirectWasHitMessage.php:34
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\contentOnPartiallyTranslatedPage
‪contentOnPartiallyTranslatedPage(array $languageConfiguration, array $visibleRecordHeaders, string $fallbackType, string $fallbackChain, string $overlayMode)
Definition: LocalizedSiteContentRenderingTest.php:826
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\LANGUAGE_PRESETS
‪const LANGUAGE_PRESETS
Definition: LocalizedSiteContentRenderingTest.php:114
‪TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait\buildDefaultLanguageConfiguration
‪buildDefaultLanguageConfiguration(string $identifier, string $base)
Definition: SiteBasedTestTrait.php:98
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\getNonVisibleHeaders
‪getNonVisibleHeaders(array $visibleHeaders)
Definition: LocalizedSiteContentRenderingTest.php:135
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\contentOnNonTranslatedPageDataProvider
‪static contentOnNonTranslatedPageDataProvider()
Definition: LocalizedSiteContentRenderingTest.php:385
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\contentOnNonTranslatedPageGerman
‪contentOnNonTranslatedPageGerman(array $languageConfiguration, array $visibleRecords, string $pageTitle, int $languageId, int $contentLanguageId, string $fallbackType, string $fallbackChain, string $overlayMode, int $statusCode=200)
Definition: LocalizedSiteContentRenderingTest.php:660
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\onlyEnglishContentIsRenderedForDefaultLanguage
‪onlyEnglishContentIsRenderedForDefaultLanguage()
Definition: LocalizedSiteContentRenderingTest.php:177
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\getNonVisibleFileTitles
‪getNonVisibleFileTitles(array $visibleTitles)
Definition: LocalizedSiteContentRenderingTest.php:158
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\TABLE_Content
‪const TABLE_Content
Definition: LocalizedSiteContentRenderingTest.php:113
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\contentOnPartiallyTranslatedPageDataProvider
‪static contentOnPartiallyTranslatedPageDataProvider()
Definition: LocalizedSiteContentRenderingTest.php:719
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering
Definition: AbsoluteUriPrefixRenderingTest.php:18
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\renderingOfDutchLanguage
‪renderingOfDutchLanguage(array $languageConfiguration, array $visibleRecords, string $fallbackType, string $fallbackChain, string $overlayMode)
Definition: LocalizedSiteContentRenderingTest.php:329
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\setUp
‪setUp()
Definition: LocalizedSiteContentRenderingTest.php:121
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\dutchDataProvider
‪static dutchDataProvider()
Definition: LocalizedSiteContentRenderingTest.php:242
‪TYPO3\CMS\Frontend\Tests\Functional\Rendering\LocalizedSiteContentRenderingTest\VALUE_PageId
‪const VALUE_PageId
Definition: LocalizedSiteContentRenderingTest.php:112