[BUGFIX] Avoid double slashes in slug generation
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / DataHandling / SlugHelperTest.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Core\Tests\Unit\DataHandling;
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 TYPO3\CMS\Core\DataHandling\SlugHelper;
19 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
20
21 class SlugHelperTest extends UnitTestCase
22 {
23 /**
24 * @var bool
25 */
26 protected $resetSingletonInstances = true;
27
28 /**
29 * @return array
30 */
31 public function sanitizeDataProvider(): array
32 {
33 return [
34 'empty string' => [
35 [],
36 '',
37 '',
38 ],
39 'existing base' => [
40 [],
41 '/',
42 '',
43 ],
44 'invalid base' => [
45 [],
46 '//',
47 '',
48 ],
49 'invalid slug' => [
50 [],
51 '/slug//',
52 'slug/',
53 ],
54 'lowercase characters' => [
55 [],
56 '1AZÄ',
57 '1azae',
58 ],
59 'strig tags' => [
60 [],
61 '<foo>bar</foo>',
62 'bar'
63 ],
64 'replace special chars to -' => [
65 [],
66 '1 2-3+4_5',
67 '1-2-3-4-5',
68 ],
69 'empty fallback character' => [
70 [
71 'fallbackCharacter' => '',
72 ],
73 '1_2',
74 '12',
75 ],
76 'different fallback character' => [
77 [
78 'fallbackCharacter' => '_',
79 ],
80 '1-2',
81 '1_2',
82 ],
83 'convert umlauts' => [
84 [],
85 'ä ß Ö',
86 'ae-ss-oe'
87 ],
88 'keep slashes' => [
89 [],
90 '1/2',
91 '1/2',
92 ],
93 'keep pending slash' => [
94 [],
95 '/1/2',
96 '1/2',
97 ],
98 'do not remove trailing slash' => [
99 [],
100 '1/2/',
101 '1/2/',
102 ],
103 'keep pending slash and remove fallback' => [
104 [],
105 '/-1/2',
106 '1/2',
107 ],
108 'do not remove trailing slash, but remove fallback' => [
109 [],
110 '1/2-/',
111 '1/2/',
112 ],
113 'reduce multiple fallback chars to one' => [
114 [],
115 '1---2',
116 '1-2',
117 ],
118 'various special chars' => [
119 [],
120 'special-chars-«-∑-€-®-†-Ω-¨-ø-π-å-‚-∂-ƒ-©-ª-º-∆-@-¥-≈-ç-√-∫-~-µ-∞-…-–',
121 'special-chars-eur-r-o-oe-p-aa-f-c-a-o-yen-c-u'
122 ],
123 ];
124 }
125
126 /**
127 * @test
128 * @dataProvider sanitizeDataProvider
129 * @param array $configuration
130 * @param string $input
131 * @param string $expected
132 */
133 public function sanitizeConvertsString(array $configuration, string $input, string $expected)
134 {
135 $subject = new SlugHelper(
136 'dummyTable',
137 'dummyField',
138 $configuration
139 );
140 static::assertEquals(
141 $expected,
142 $subject->sanitize($input)
143 );
144 }
145
146 public function generateNeverDeliversEmptySlugDataProvider()
147 {
148 return [
149 'simple title' => [
150 'Products',
151 'products'
152 ],
153 'title with spaces' => [
154 'Product Cow',
155 'product-cow'
156 ],
157 'title with invalid characters' => [
158 'Products - Cows',
159 'products-cows'
160 ],
161 'title with only invalid characters' => [
162 '!!!',
163 'default-51cf35392c'
164 ],
165 ];
166 }
167
168 /**
169 * @dataProvider generateNeverDeliversEmptySlugDataProvider
170 * @param string $input
171 * @param string $expected
172 * @test
173 */
174 public function generateNeverDeliversEmptySlug(string $input, string $expected)
175 {
176 $GLOBALS['dummyTable']['ctrl'] = [];
177 $subject = new SlugHelper(
178 'dummyTable',
179 'dummyField',
180 ['generatorOptions' => ['fields' => ['title']]]
181 );
182 static::assertEquals(
183 $expected,
184 $subject->generate(['title' => $input, 'uid' => 13], 13)
185 );
186 }
187
188 /**
189 * @return array
190 */
191 public function sanitizeForPagesDataProvider(): array
192 {
193 return [
194 'empty string' => [
195 [],
196 '',
197 '/',
198 ],
199 'existing base' => [
200 [],
201 '/',
202 '/',
203 ],
204 'invalid base' => [
205 [],
206 '//',
207 '/',
208 ],
209 'invalid slug' => [
210 [],
211 '/slug//',
212 '/slug/',
213 ],
214 'lowercase characters' => [
215 [],
216 '1AZÄ',
217 '/1azae',
218 ],
219 'strig tags' => [
220 [],
221 '<foo>bar</foo>',
222 '/bar'
223 ],
224 'replace special chars to -' => [
225 [],
226 '1 2-3+4_5',
227 '/1-2-3-4-5',
228 ],
229 'empty fallback character' => [
230 [
231 'fallbackCharacter' => '',
232 ],
233 '1_2',
234 '/12',
235 ],
236 'different fallback character' => [
237 [
238 'fallbackCharacter' => '_',
239 ],
240 '1-2',
241 '/1_2',
242 ],
243 'convert umlauts' => [
244 [],
245 'ä ß Ö',
246 '/ae-ss-oe'
247 ],
248 'keep slashes' => [
249 [],
250 '1/2',
251 '/1/2',
252 ],
253 'keep pending slash' => [
254 [],
255 '/1/2',
256 '/1/2',
257 ],
258 'do not remove trailing slash' => [
259 [],
260 '1/2/',
261 '/1/2/',
262 ],
263 'keep pending slash and remove fallback' => [
264 [],
265 '/-1/2',
266 '/1/2',
267 ],
268 'do not remove trailing slash, but remove fallback' => [
269 [],
270 '1/2-/',
271 '/1/2/',
272 ],
273 'reduce multiple fallback chars to one' => [
274 [],
275 '1---2',
276 '/1-2',
277 ],
278 'various special chars' => [
279 [],
280 'special-chars-«-∑-€-®-†-Ω-¨-ø-π-å-‚-∂-ƒ-©-ª-º-∆-@-¥-≈-ç-√-∫-~-µ-∞-…-–',
281 '/special-chars-eur-r-o-oe-p-aa-f-c-a-o-yen-c-u'
282 ],
283 ];
284 }
285
286 /**
287 * @test
288 * @dataProvider sanitizeForPagesDataProvider
289 * @param array $configuration
290 * @param string $input
291 * @param string $expected
292 */
293 public function sanitizeConvertsStringForPages(array $configuration, string $input, string $expected)
294 {
295 $subject = new SlugHelper(
296 'pages',
297 'slug',
298 $configuration
299 );
300 static::assertEquals(
301 $expected,
302 $subject->sanitize($input)
303 );
304 }
305
306 public function generateNeverDeliversEmptySlugForPagesDataProvider()
307 {
308 return [
309 'simple title' => [
310 'Products',
311 '/products'
312 ],
313 'title with spaces' => [
314 'Product Cow',
315 '/product-cow'
316 ],
317 'title with invalid characters' => [
318 'Products - Cows',
319 '/products-cows'
320 ],
321 'title with only invalid characters' => [
322 '!!!',
323 '/default-51cf35392c'
324 ],
325 ];
326 }
327
328 /**
329 * @dataProvider generateNeverDeliversEmptySlugForPagesDataProvider
330 * @param string $input
331 * @param string $expected
332 * @test
333 */
334 public function generateNeverDeliversEmptySlugForPages(string $input, string $expected)
335 {
336 $GLOBALS['dummyTable']['ctrl'] = [];
337 $subject = new SlugHelper(
338 'pages',
339 'slug',
340 ['generatorOptions' => ['fields' => ['title']]]
341 );
342 static::assertEquals(
343 $expected,
344 $subject->generate(['title' => $input, 'uid' => 13], 13)
345 );
346 }
347
348 /**
349 * @return array
350 */
351 public function generatePrependsSlugsForPagesDataProvider(): array
352 {
353 return [
354 'simple title' => [
355 'Products',
356 '/parent-page/products'
357 ],
358 'title with spaces' => [
359 'Product Cow',
360 '/parent-page/product-cow'
361 ],
362 'title with invalid characters' => [
363 'Products - Cows',
364 '/parent-page/products-cows'
365 ],
366 'title with only invalid characters' => [
367 '!!!',
368 '/parent-page/default-51cf35392c'
369 ],
370 ];
371 }
372
373 /**
374 * @dataProvider generatePrependsSlugsForPagesDataProvider
375 * @param string $input
376 * @param string $expected
377 * @test
378 */
379 public function generatePrependsSlugsForPages(string $input, string $expected)
380 {
381 $GLOBALS['dummyTable']['ctrl'] = [];
382 $rootLine = [
383 [
384 'uid' => '13',
385 'pid' => '10',
386 'title' => 'Parent Page',
387 ]
388 ];
389 $subject = $this->getAccessibleMock(
390 SlugHelper::class,
391 ['resolveRootLine'],
392 [
393 'pages',
394 'slug',
395 [
396 'generatorOptions' => [
397 'fields' => ['title'],
398 'prefixParentPageSlug' => true,
399 ],
400 ]
401 ]
402 );
403 $subject->expects(static::at(0))
404 ->method('resolveRootLine')->with(13)->willReturn($rootLine);
405 $subject->expects(static::at(1))
406 ->method('resolveRootLine')->with(10)->willReturn([]);
407 static::assertEquals(
408 $expected,
409 $subject->generate(['title' => $input, 'uid' => 13], 13)
410 );
411 }
412 }