‪TYPO3CMS  10.4
SlugHelperTest.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 
21 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
22 
23 class ‪SlugHelperTest extends UnitTestCase
24 {
28  protected ‪$resetSingletonInstances = true;
29 
33  public function ‪sanitizeDataProvider(): array
34  {
35  return [
36  'empty string' => [
37  [],
38  '',
39  '',
40  ],
41  'existing base' => [
42  [],
43  '/',
44  '',
45  ],
46  'invalid base' => [
47  [],
48  '//',
49  '',
50  ],
51  'invalid slug' => [
52  [],
53  '/slug//',
54  'slug/',
55  ],
56  'lowercase characters' => [
57  [],
58  '1AZÄ',
59  '1azae',
60  ],
61  'strig tags' => [
62  [],
63  '<foo>bar</foo>',
64  'bar'
65  ],
66  'replace special chars to -' => [
67  [],
68  '1 2-3+4_5',
69  '1-2-3-4-5',
70  ],
71  'empty fallback character' => [
72  [
73  'fallbackCharacter' => '',
74  ],
75  '1_2',
76  '12',
77  ],
78  'different fallback character' => [
79  [
80  'fallbackCharacter' => '_',
81  ],
82  '1-2',
83  '1_2',
84  ],
85  'convert umlauts' => [
86  [],
87  'ä ß Ö',
88  'ae-ss-oe'
89  ],
90  'keep slashes' => [
91  [],
92  '1/2',
93  '1/2',
94  ],
95  'keep pending slash' => [
96  [],
97  '/1/2',
98  '1/2',
99  ],
100  'do not remove trailing slash' => [
101  [],
102  '1/2/',
103  '1/2/',
104  ],
105  'keep pending slash and remove fallback' => [
106  [],
107  '/-1/2',
108  '1/2',
109  ],
110  'do not remove trailing slash, but remove fallback' => [
111  [],
112  '1/2-/',
113  '1/2/',
114  ],
115  'reduce multiple fallback chars to one' => [
116  [],
117  '1---2',
118  '1-2',
119  ],
120  'various special chars' => [
121  [],
122  'special-chars-«-∑-€-®-†-Ω-¨-ø-π-å-‚-∂-ƒ-©-ª-º-∆-@-¥-≈-ç-√-∫-~-µ-∞-…-–',
123  'special-chars-eur-r-o-oe-p-aa-f-c-a-o-yen-c-u'
124  ],
125  'ensure colon and other http related parts are disallowed' => [
126  [],
127  'https://example.com:80/my/page/slug/',
128  'https//examplecom80/my/page/slug/'
129  ],
130  'non-ASCII characters are kept' => [
131  [],
132  'bla-arg应---用-ascii',
133  'bla-arg应-用-ascii'
134  ],
135  'non-normalized characters' => [
136  [],
137  hex2bin('667275cc88686e65757a6569746c696368656e'),
138  'fruehneuzeitlichen'
139  ],
140  ];
141  }
142 
150  public function ‪sanitizeConvertsString(array $configuration, string $input, string $expected)
151  {
152  $subject = new ‪SlugHelper(
153  'dummyTable',
154  'dummyField',
155  $configuration
156  );
157  self::assertEquals(
158  $expected,
159  $subject->sanitize($input)
160  );
161  }
162 
164  {
165  return [
166  'simple title' => [
167  'Products',
168  'products'
169  ],
170  'title with spaces' => [
171  'Product Cow',
172  'product-cow'
173  ],
174  'title with invalid characters' => [
175  'Products - Cows',
176  'products-cows'
177  ],
178  'title with only invalid characters' => [
179  '!!!',
180  'default-51cf35392c'
181  ],
182  ];
183  }
184 
191  public function ‪generateNeverDeliversEmptySlug(string $input, string $expected)
192  {
193  ‪$GLOBALS['dummyTable']['ctrl'] = [];
194  $subject = new ‪SlugHelper(
195  'dummyTable',
196  'dummyField',
197  ['generatorOptions' => ['fields' => ['title']]]
198  );
199  self::assertEquals(
200  $expected,
201  $subject->generate(['title' => $input, 'uid' => 13], 13)
202  );
203  }
204 
208  public function ‪sanitizeForPagesDataProvider(): array
209  {
210  return [
211  'empty string' => [
212  [],
213  '',
214  '/',
215  ],
216  'existing base' => [
217  [],
218  '/',
219  '/',
220  ],
221  'invalid base' => [
222  [],
223  '//',
224  '/',
225  ],
226  'invalid slug' => [
227  [],
228  '/slug//',
229  '/slug/',
230  ],
231  'lowercase characters' => [
232  [],
233  '1AZÄ',
234  '/1azae',
235  ],
236  'strig tags' => [
237  [],
238  '<foo>bar</foo>',
239  '/bar'
240  ],
241  'replace special chars to -' => [
242  [],
243  '1 2-3+4_5',
244  '/1-2-3-4-5',
245  ],
246  'empty fallback character' => [
247  [
248  'fallbackCharacter' => '',
249  ],
250  '1_2',
251  '/12',
252  ],
253  'different fallback character' => [
254  [
255  'fallbackCharacter' => '_',
256  ],
257  '1-2',
258  '/1_2',
259  ],
260  'convert umlauts' => [
261  [],
262  'ä ß Ö',
263  '/ae-ss-oe'
264  ],
265  'keep slashes' => [
266  [],
267  '1/2',
268  '/1/2',
269  ],
270  'keep pending slash' => [
271  [],
272  '/1/2',
273  '/1/2',
274  ],
275  'do not remove trailing slash' => [
276  [],
277  '1/2/',
278  '/1/2/',
279  ],
280  'keep pending slash and remove fallback' => [
281  [],
282  '/-1/2',
283  '/1/2',
284  ],
285  'do not remove trailing slash, but remove fallback' => [
286  [],
287  '1/2-/',
288  '/1/2/',
289  ],
290  'reduce multiple fallback chars to one' => [
291  [],
292  '1---2',
293  '/1-2',
294  ],
295  'various special chars' => [
296  [],
297  'special-chars-«-∑-€-®-†-Ω-¨-ø-π-å-‚-∂-ƒ-©-ª-º-∆-@-¥-≈-ç-√-∫-~-µ-∞-…-–',
298  '/special-chars-eur-r-o-oe-p-aa-f-c-a-o-yen-c-u'
299  ],
300  'ensure colon and other http related parts are disallowed' => [
301  [],
302  'https://example.com:80/my/page/slug/',
303  '/https//examplecom80/my/page/slug/'
304  ],
305  'chinese' => [
306  [],
307  '应用',
308  '/应用'
309  ],
310  'hindi' => [
311  [],
312  'कंपनी',
313  '/कंपनी'
314  ],
315  'hindi with plain accent character' => [
316  [],
317  'कंपनी^',
318  '/कंपनी'
319  ],
320  'hindi with combined accent character' => [
321  [],
322  'कंपनीâ',
323  '/कंपनीa'
324  ],
325  'japanese numbers (sino-japanese)' => [
326  [],
327  'さん',
328  '/さん'
329  ],
330  'japanese numbers (kanji)' => [
331  [],
332  '三つ',
333  '/三つ'
334  ],
335  'persian numbers' => [
336  [],
337  '۴',
338  '/4'
339  ],
340  ];
341  }
342 
350  public function ‪sanitizeConvertsStringForPages(array $configuration, string $input, string $expected)
351  {
352  $subject = new SlugHelper(
353  'pages',
354  'slug',
355  $configuration
356  );
357  self::assertEquals(
358  $expected,
359  $subject->sanitize($input)
360  );
361  }
362 
364  {
365  return [
366  'simple title' => [
367  'Products',
368  '/products'
369  ],
370  'title with spaces' => [
371  'Product Cow',
372  '/product-cow'
373  ],
374  'title with invalid characters' => [
375  'Products - Cows',
376  '/products-cows'
377  ],
378  'title with only invalid characters' => [
379  '!!!',
380  '/default-51cf35392c'
381  ],
382  ];
383  }
384 
391  public function ‪generateNeverDeliversEmptySlugForPages(string $input, string $expected)
392  {
393  ‪$GLOBALS['dummyTable']['ctrl'] = [];
394  $subject = new ‪SlugHelper(
395  'pages',
396  'slug',
397  ['generatorOptions' => ['fields' => ['title']]]
398  );
399  self::assertEquals(
400  $expected,
401  $subject->generate(['title' => $input, 'uid' => 13], 13)
402  );
403  }
404 
408  public function ‪generatePrependsSlugsForPagesDataProvider(): array
409  {
410  return [
411  'simple title' => [
412  'Products',
413  '/parent-page/products',
414  [
415  'generatorOptions' => [
416  'fields' => ['title'],
417  'prefixParentPageSlug' => true,
418  ],
419  ]
420  ],
421  'title with spaces' => [
422  'Product Cow',
423  '/parent-page/product-cow',
424  [
425  'generatorOptions' => [
426  'fields' => ['title'],
427  'prefixParentPageSlug' => true,
428  ],
429  ]
430  ],
431  'title with slash' => [
432  'Product/Cow',
433  '/parent-page/product/cow',
434  [
435  'generatorOptions' => [
436  'fields' => ['title'],
437  'prefixParentPageSlug' => true,
438  ],
439  ]
440  ],
441  'title with slash and replace' => [
442  'Product/Cow',
443  '/parent-page/productcow',
444  [
445  'generatorOptions' => [
446  'fields' => ['title'],
447  'prefixParentPageSlug' => true,
448  'replacements' => [
449  '/' => ''
450  ]
451  ],
452  ]
453  ],
454  'title with slash and replace #2' => [
455  'Some Job in city1/city2 (m/w)',
456  '/parent-page/some-job-in-city1-city2',
457  [
458  'generatorOptions' => [
459  'fields' => ['title'],
460  'prefixParentPageSlug' => true,
461  'replacements' => [
462  '(m/w)' => '',
463  '/' => '-'
464  ]
465  ],
466  ]
467  ],
468  'title with invalid characters' => [
469  'Products - Cows',
470  '/parent-page/products-cows',
471  [
472  'generatorOptions' => [
473  'fields' => ['title'],
474  'prefixParentPageSlug' => true,
475  ],
476  ]
477  ],
478  'title with only invalid characters' => [
479  '!!!',
480  '/parent-page/default-51cf35392c',
481  [
482  'generatorOptions' => [
483  'fields' => ['title'],
484  'prefixParentPageSlug' => true,
485  ],
486  ]
487  ],
488  ];
489  }
490 
497  public function ‪generatePrependsSlugsForPages(string $input, string $expected, array $options)
498  {
499  ‪$GLOBALS['dummyTable']['ctrl'] = [];
500  $parentPage = [
501  'uid' => '13',
502  'pid' => '10',
503  'title' => 'Parent Page',
504  ];
505  $subject = $this->getAccessibleMock(
506  SlugHelper::class,
507  ['resolveParentPageRecord'],
508  [
509  'pages',
510  'slug',
511  $options
512  ]
513  );
514  $subject->expects(self::atLeast(2))
515  ->method('resolveParentPageRecord')
516  ->withConsecutive([13], [10])
517  ->willReturn($parentPage, null);
518  self::assertEquals(
519  $expected,
520  $subject->generate(['title' => $input, 'uid' => 13], 13)
521  );
522  }
523 
528  {
529  return [
530  'title and empty nav_title' => [
531  ['title' => 'Products', 'nav_title' => '', 'subtitle' => ''],
532  '/products',
533  [
534  'generatorOptions' => [
535  'fields' => [
536  ['nav_title', 'title']
537  ],
538  ],
539  ]
540  ],
541  'title and nav_title' => [
542  ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => ''],
543  '/best-products',
544  [
545  'generatorOptions' => [
546  'fields' => [
547  ['nav_title', 'title']
548  ],
549  ],
550  ]
551  ],
552  'title and nav_title and subtitle' => [
553  ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle'],
554  '/product-subtitle',
555  [
556  'generatorOptions' => [
557  'fields' => [
558  ['subtitle', 'nav_title', 'title']
559  ],
560  ],
561  ]
562  ],
563  'definition with a non existing field (misconfiguration)' => [
564  ['title' => 'Products', 'nav_title' => '', 'subtitle' => ''],
565  '/products',
566  [
567  'generatorOptions' => [
568  'fields' => [
569  ['custom_field', 'title']
570  ],
571  ],
572  ]
573  ],
574  'empty fields deliver default slug' => [
575  ['title' => '', 'nav_title' => '', 'subtitle' => ''],
576  '/default-b4dac929c2',
577  [
578  'generatorOptions' => [
579  'fields' => [
580  ['nav_title', 'title']
581  ],
582  ],
583  ]
584  ],
585  'fallback combined with a second field' => [
586  ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle'],
587  '/best-products/product-subtitle',
588  [
589  'generatorOptions' => [
590  'fields' => [
591  ['nav_title', 'title'], 'subtitle'
592  ],
593  ],
594  ]
595  ],
596  'empty config array deliver default slug' => [
597  ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle'],
598  '/default-e13d142b36',
599  [
600  'generatorOptions' => [
601  'fields' => [
602  []
603  ],
604  ],
605  ]
606  ],
607  'empty config deliver default slug' => [
608  ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle'],
609  '/default-e13d142b36',
610  [
611  'generatorOptions' => [
612  'fields' => [],
613  ],
614  ]
615  ],
616  'combine two fallbacks' => [
617  ['title' => 'Products', 'nav_title' => 'Best products', 'subtitle' => 'Product subtitle', 'seo_title' => 'SEO product title'],
618  '/seo-product-title/best-products',
619  [
620  'generatorOptions' => [
621  'fields' => [
622  ['seo_title', 'title'], ['nav_title', 'subtitle']
623  ],
624  ],
625  ]
626  ],
627  ];
628  }
629 
637  public function ‪generateSlugWithNavTitleAndFallbackForPages(array $input, string $expected, array $options)
638  {
639  ‪$GLOBALS['dummyTable']['ctrl'] = [];
640  $subject = new SlugHelper(
641  'pages',
642  'slug',
643  ['generatorOptions' => $options['generatorOptions']]
644  );
645  self::assertEquals(
646  $expected,
647  $subject->generate([
648  'title' => $input['title'],
649  'nav_title' => $input['nav_title'],
650  'subtitle' => $input['subtitle'],
651  'seo_title' => $input['seo_title'] ?? '',
652  'uid' => 13
653  ], 13)
654  );
655  }
656 
660  public function ‪generateSlugWithHookModifiers()
661  {
662  $options = [];
663  $options['fallbackCharacter'] = '-';
664  $options['generatorOptions'] = [
665  'fields' => ['title'],
666  'postModifiers' => [
667  0 => function ($parameters, $subject) {
668  $slug = $parameters['slug'];
669  if ($parameters['pid'] == 13) {
670  $slug = 'prepend' . $slug;
671  }
672  return $slug;
673  }
674  ]
675  ];
676  $subject = new SlugHelper(
677  'pages',
678  'slug',
679  $options
680  );
681  $expected = '/prepend/products';
682  self::assertEquals(
683  $expected,
684  $subject->generate([
685  'title' => 'Products',
686  'nav_title' => 'Best products',
687  'subtitle' => 'Product subtitle',
688  'seo_title' => 'SEO product title',
689  'uid' => 23
690  ], 13)
691  );
692  }
693 
697  public function ‪generateSlugWithPid0DataProvider(): array
698  {
699  return [
700  'pages' => [
701  ['table' => 'pages', 'title' => 'Products'],
702  '/'
703  ],
704  'dummyTable' => [
705  ['table' => 'dummyTable', 'title' => 'Products'],
706  'products'
707  ],
708  ];
709  }
710 
717  public function ‪generateSlugWithPid0(array $input, string $expected)
718  {
719  if (empty(‪$GLOBALS[$input['table']]['ctrl'])) {
720  ‪$GLOBALS[$input['table']]['ctrl'] = [];
721  }
722  $subject = new SlugHelper(
723  $input['table'],
724  'title',
725  ['generatorOptions' => ['fields' => ['title']]]
726  );
727  self::assertEquals(
728  $expected,
729  $subject->generate(['title' => $input['title'], 'uid' => 13], 0)
730  );
731  }
732 
733  public function ‪generatePrependsSlugsForNonPagesDataProvider(): array
734  {
735  return [
736  'simple title' => [
737  'Product Name',
738  'product-name',
739  [
740  'generatorOptions' => [
741  'fields' => ['title'],
742  'prefixParentPageSlug' => true,
743  ],
744  ],
745  ],
746  ];
747  }
748 
753  public function ‪generatePrependsSlugsForNonPages(string $input, string $expected, array $options): void
754  {
755  ‪$GLOBALS['dummyTable']['ctrl'] = [];
756  $parentPage = [
757  'uid' => '0',
758  'pid' => null,
759  ];
760  $subject = $this->getAccessibleMock(
761  SlugHelper::class,
762  ['resolveParentPageRecord'],
763  [
764  'another_table',
765  'slug',
766  $options,
767  ]
768  );
769  $subject->expects(self::any())
770  ->method('resolveParentPageRecord')
771  ->withAnyParameters()
772  ->willReturn($parentPage);
773  self::assertEquals(
774  $expected,
775  $subject->generate(['title' => $input, 'uid' => 13], 13)
776  );
777  }
778 }
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateNeverDeliversEmptySlugDataProvider
‪generateNeverDeliversEmptySlugDataProvider()
Definition: SlugHelperTest.php:162
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateNeverDeliversEmptySlug
‪generateNeverDeliversEmptySlug(string $input, string $expected)
Definition: SlugHelperTest.php:190
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateNeverDeliversEmptySlugForPagesDataProvider
‪generateNeverDeliversEmptySlugForPagesDataProvider()
Definition: SlugHelperTest.php:362
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateSlugWithPid0DataProvider
‪array generateSlugWithPid0DataProvider()
Definition: SlugHelperTest.php:696
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generatePrependsSlugsForNonPagesDataProvider
‪generatePrependsSlugsForNonPagesDataProvider()
Definition: SlugHelperTest.php:732
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generatePrependsSlugsForNonPages
‪generatePrependsSlugsForNonPages(string $input, string $expected, array $options)
Definition: SlugHelperTest.php:752
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateSlugWithHookModifiers
‪generateSlugWithHookModifiers()
Definition: SlugHelperTest.php:659
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateNeverDeliversEmptySlugForPages
‪generateNeverDeliversEmptySlugForPages(string $input, string $expected)
Definition: SlugHelperTest.php:390
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\sanitizeConvertsStringForPages
‪sanitizeConvertsStringForPages(array $configuration, string $input, string $expected)
Definition: SlugHelperTest.php:349
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\sanitizeDataProvider
‪array sanitizeDataProvider()
Definition: SlugHelperTest.php:32
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generatePrependsSlugsForPagesDataProvider
‪array generatePrependsSlugsForPagesDataProvider()
Definition: SlugHelperTest.php:407
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\sanitizeConvertsString
‪sanitizeConvertsString(array $configuration, string $input, string $expected)
Definition: SlugHelperTest.php:149
‪TYPO3\CMS\Core\DataHandling\SlugHelper
Definition: SlugHelper.php:42
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateSlugWithNavTitleAndFallbackForPages
‪generateSlugWithNavTitleAndFallbackForPages(array $input, string $expected, array $options)
Definition: SlugHelperTest.php:636
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\sanitizeForPagesDataProvider
‪array sanitizeForPagesDataProvider()
Definition: SlugHelperTest.php:207
‪TYPO3\CMS\Core\Tests\Unit\DataHandling
Definition: DataHandlerTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateSlugWithPid0
‪generateSlugWithPid0(array $input, string $expected)
Definition: SlugHelperTest.php:716
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest
Definition: SlugHelperTest.php:24
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generatePrependsSlugsForPages
‪generatePrependsSlugsForPages(string $input, string $expected, array $options)
Definition: SlugHelperTest.php:496
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: SlugHelperTest.php:27
‪TYPO3\CMS\Core\Tests\Unit\DataHandling\SlugHelperTest\generateSlugWithNavTitleAndFallbackForPagesDataProvider
‪array generateSlugWithNavTitleAndFallbackForPagesDataProvider()
Definition: SlugHelperTest.php:526