‪TYPO3CMS  ‪main
RteHtmlParserTest.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;
22 use Psr\EventDispatcher\EventDispatcherInterface;
24 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
25 
26 final class ‪RteHtmlParserTest extends UnitTestCase
27 {
28  protected bool ‪$resetSingletonInstances = true;
29 
30  protected array ‪$procOptions = ['overruleMode' => 'default', 'allowTagsOutside' => 'hr,abbr,figure'];
31 
36  {
37  return [
38  'Single hr' => [
39  '<hr />',
40  '<hr />',
41  ],
42  'Non-xhtml single hr' => [
43  '<hr/>',
44  '<hr />',
45  ],
46  'Double hr' => [
47  '<hr /><hr />',
48  '<hr />' . CRLF . '<hr />',
49  ],
50  'Linebreak followed by hr' => [
51  CRLF . '<hr />',
52  '<hr />',
53  ],
54  'White space followed by hr' => [
55  ' <hr />',
56  ' ' . CRLF . '<hr />',
57  ],
58  'White space followed linebreak and hr' => [
59  ' ' . CRLF . '<hr />',
60  ' ' . CRLF . '<hr />',
61  ],
62  'br followed by hr' => [
63  '<br /><hr />',
64  '<br />' . CRLF . '<hr />',
65  ],
66  'br followed by linebreak and hr' => [
67  '<br />' . CRLF . '<hr />',
68  '<br />' . CRLF . '<hr />',
69  ],
70  'Preserved div followed by hr' => [
71  '<div>Some text</div><hr />',
72  '<div>Some text</div>' . CRLF . '<hr />',
73  ],
74  'Preserved div followed by linebreak and hr' => [
75  '<div>Some text</div>' . CRLF . '<hr />',
76  '<div>Some text</div>' . CRLF . '<hr />',
77  ],
78  'h1 followed by linebreak and hr' => [
79  '<h1>Some text</h1>' . CRLF . '<hr />',
80  '<h1>Some text</h1>' . CRLF . '<hr />',
81  ],
82  'Paragraph followed by linebreak and hr' => [
83  '<p>Some text</p>' . CRLF . '<hr />',
84  '<p>Some text</p>' . CRLF . '<hr />',
85  ],
86  'Some text without HTML tags' => [
87  'Some text',
88  'Some text',
89  ],
90  'Some text followed by hr' => [
91  'Some text<hr />',
92  'Some text' . CRLF . '<hr />',
93  ],
94  'Some text followed by linebreak and hr' => [
95  'Some text' . CRLF . '<hr />',
96  'Some text' . CRLF . '<hr />',
97  ],
98  ];
99  }
100 
101  #[DataProvider('hrTagCorrectlyTransformedOnWayToDataBaseDataProvider')]
102  #[Test]
103  public function ‪hrTagCorrectlyTransformedOnWayToDataBase($content, $expectedResult): void
104  {
105  // @todo Explicitly disabled HTML Sanitizer (since it is based on HTML5)
106  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.htmlSanitizeRte'] = false;
107  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
108  $subject = new ‪RteHtmlParser($eventDispatcher);
109  self::assertEquals($expectedResult, $subject->transformTextForPersistence($content, $this->procOptions));
110  }
111 
116  {
117  return [
118  'Single hr' => [
119  '<hr />',
120  '<hr />',
121  ],
122  'Non-xhtml single hr' => [
123  '<hr/>',
124  '<hr />',
125  ],
126  'Double hr' => [
127  '<hr /><hr />',
128  '<hr />' . CRLF . '<hr />',
129  ],
130  'Linebreak followed by hr' => [
131  CRLF . '<hr />',
132  '<hr />',
133  ],
134  'White space followed by hr' => [
135  ' <hr />',
136  '<p>&nbsp;</p>' . CRLF . '<hr />',
137  ],
138  'White space followed by linebreak and hr' => [
139  ' ' . CRLF . '<hr />',
140  '<p>&nbsp;</p>' . CRLF . '<hr />',
141  ],
142  'br followed by hr' => [
143  '<br /><hr />',
144  '<p><br /></p>' . CRLF . '<hr />',
145  ],
146  'br followed by linebreak and hr' => [
147  '<br />' . CRLF . '<hr />',
148  '<p><br /></p>' . CRLF . '<hr />',
149  ],
150  'Preserved div followed by hr' => [
151  '<div>Some text</div><hr />',
152  '<div><p>Some text</p></div>' . CRLF . '<hr />',
153  ],
154  'Preserved div followed by linebreak and hr' => [
155  '<div>Some text</div>' . CRLF . '<hr />',
156  '<div><p>Some text</p></div>' . CRLF . '<hr />',
157  ],
158  'h1 followed by linebreak and hr' => [
159  '<h1>Some text</h1>' . CRLF . '<hr />',
160  '<h1>Some text</h1>' . CRLF . '<hr />',
161  ],
162  'Paragraph followed by linebreak and hr' => [
163  '<p>Some text</p>' . CRLF . '<hr />',
164  '<p>Some text</p>' . CRLF . '<hr />',
165  ],
166  'Some text followed by hr' => [
167  'Some text<hr />',
168  '<p>Some text</p>' . CRLF . '<hr />',
169  ],
170  'Some text followed by linebreak and hr' => [
171  'Some text' . CRLF . '<hr />',
172  '<p>Some text</p>' . CRLF . '<hr />',
173  ],
174  ];
175  }
176 
177  #[DataProvider('hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider')]
178  #[Test]
179  public function ‪hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult): void
180  {
181  // @todo Explicitly disabled HTML Sanitizer (since it is based on HTML5)
182  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.htmlSanitizeRte'] = false;
183  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
184  $subject = new ‪RteHtmlParser($eventDispatcher);
185  self::assertEquals($expectedResult, $subject->transformTextForRichTextEditor($subject->transformTextForPersistence($content, $this->procOptions), $this->procOptions));
186  }
187 
192  {
193  return [
194  '<br>' => [
195  '<p>foo<br>bar</p>',
196  '<p>foo<br />bar</p>',
197  ],
198  '<br/> without white space' => [
199  '<p>foo<br/>bar</p>',
200  '<p>foo<br />bar</p>',
201  ],
202  '<br /> with whitespace' => [
203  '<p>foo<br />bar</p>',
204  '<p>foo<br />bar</p>',
205  ],
206  ];
207  }
208 
209  #[DataProvider('brTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider')]
210  #[Test]
211  public function ‪brTagCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult): void
212  {
213  // @todo Explicitly disabled HTML Sanitizer (since it is based on HTML5)
214  ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['security.backend.htmlSanitizeRte'] = false;
215  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
216  $subject = new ‪RteHtmlParser($eventDispatcher);
217  self::assertEquals($expectedResult, $subject->transformTextForRichTextEditor($subject->transformTextForPersistence($content, $this->procOptions), $this->procOptions));
218  }
219 
224  {
225  return [
226  'Empty string' => [
227  '',
228  '',
229  ],
230  'Linebreak' => [
231  CRLF,
232  '',
233  ],
234  'Double linebreak' => [
235  CRLF . CRLF,
236  '',
237  ],
238  'Empty paragraph' => [
239  '<p></p>',
240  CRLF,
241  ],
242  'Double empty paragraph' => [
243  '<p></p><p></p>',
244  CRLF . CRLF,
245  ],
246  'Spacing paragraph' => [
247  '<p>&nbsp;</p>',
248  CRLF,
249  ],
250  'Double spacing paragraph' => [
251  '<p>&nbsp;</p><p>&nbsp;</p>',
252  CRLF . CRLF,
253  ],
254  'Plain text' => [
255  'plain text',
256  'plain text',
257  ],
258  'Plain text followed by linebreak' => [
259  'plain text' . CRLF,
260  'plain text ',
261  ],
262  'Paragraph' => [
263  '<p>paragraph</p>',
264  '<p>paragraph</p>',
265  ],
266  'Paragraph followed by paragraph' => [
267  '<p>paragraph1</p><p>paragraph2</p>',
268  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
269  ],
270  'Paragraph followed by paragraph, linebreak-separated' => [
271  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
272  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
273  ],
274  'Double spacing paragraph with text' => [
275  '<p>&nbsp;</p><p>&nbsp;</p><p>paragraph1</p>',
276  CRLF . CRLF . '<p>paragraph1</p>',
277  ],
278  'Paragraph followed by linebreak' => [
279  '<p>paragraph</p>' . CRLF,
280  '<p>paragraph</p>',
281  ],
282  'Paragraph followed by spacing paragraph' => [
283  '<p>paragraph</p><p>&nbsp;</p>',
284  '<p>paragraph</p>' . CRLF . CRLF,
285  ],
286  'Paragraph followed by spacing paragraph, linebreak-separated' => [
287  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
288  '<p>paragraph</p>' . CRLF . CRLF,
289  ],
290  'Paragraph followed by double spacing paragraph' => [
291  '<p>paragraph</p><p>&nbsp;</p><p>&nbsp;</p>',
292  '<p>paragraph</p>' . CRLF . CRLF . CRLF,
293  ],
294  'Paragraph followed by double spacing paragraph, linebreak-separated' => [
295  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
296  '<p>paragraph</p>' . CRLF . CRLF . CRLF,
297  ],
298  'Paragraph followed by spacing paragraph and by paragraph' => [
299  '<p>paragraph1</p><p>&nbsp;</p><p>paragraph2</p>',
300  '<p>paragraph1</p>' . CRLF . CRLF . '<p>paragraph2</p>',
301  ],
302  'Paragraph followed by spacing paragraph and by paragraph, linebreak-separated' => [
303  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
304  '<p>paragraph1</p>' . CRLF . CRLF . '<p>paragraph2</p>',
305  ],
306  'Paragraph followed by double spacing paragraph and by paragraph' => [
307  '<p>paragraph1</p><p>&nbsp;</p><p>&nbsp;</p><p>paragraph2</p>',
308  '<p>paragraph1</p>' . CRLF . CRLF . CRLF . '<p>paragraph2</p>',
309  ],
310  'Paragraph followed by double spacing paragraph and by paragraph, linebreak-separated' => [
311  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
312  '<p>paragraph1</p>' . CRLF . CRLF . CRLF . '<p>paragraph2</p>',
313  ],
314  'Paragraph followed by block' => [
315  '<p>paragraph</p><h1>block</h1>',
316  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
317  ],
318  'Paragraph followed by block, linebreak-separated' => [
319  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
320  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
321  ],
322  'Paragraph followed by spacing paragraph and block' => [
323  '<p>paragraph</p><p>&nbsp;</p><h1>block</h1>',
324  '<p>paragraph</p>' . CRLF . CRLF . '<h1>block</h1>',
325  ],
326  'Paragraph followed by spacing paragraph and block, linebreak-separated' => [
327  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
328  '<p>paragraph</p>' . CRLF . CRLF . '<h1>block</h1>',
329  ],
330  'Paragraph followed by double spacing paragraph and block' => [
331  '<p>paragraph</p><p>&nbsp;</p><p>&nbsp;</p><h1>block</h1>',
332  '<p>paragraph</p>' . CRLF . CRLF . CRLF . '<h1>block</h1>',
333  ],
334  'Paragraph followed by double spacing paragraph and block, linebreak-separated' => [
335  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
336  '<p>paragraph</p>' . CRLF . CRLF . CRLF . '<h1>block</h1>',
337  ],
338  'Block followed by block' => [
339  '<h1>block1</h1><h1>block2</h1>',
340  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
341  ],
342  'Block followed by block, linebreak-separated' => [
343  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
344  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
345  ],
346  'Block followed by empty paragraph and block' => [
347  '<h1>block1</h1><p></p><h1>block2</h1>',
348  '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
349  ],
350  'Block followed by empty paragraph and block, linebreak-separated' => [
351  '<h1>block1</h1>' . CRLF . '<p></p>' . CRLF . '<h1>block2</h1>',
352  '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
353  ],
354  'Block followed by spacing paragraph' => [
355  '<h1>block1</h1><p>&nbsp;</p>',
356  '<h1>block1</h1>' . CRLF . CRLF,
357  ],
358  'Block followed by spacing paragraph, linebreak-separated' => [
359  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>',
360  '<h1>block1</h1>' . CRLF . CRLF,
361  ],
362  'Block followed by spacing paragraph and block' => [
363  '<h1>block1</h1><p>&nbsp;</p><h1>block2</h1>',
364  '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
365  ],
366  'Block followed by spacing paragraph and block, linebreak-separated' => [
367  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
368  '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
369  ],
370  'Block followed by double spacing paragraph and by block' => [
371  '<h1>block1</h1><p>&nbsp;</p><p>&nbsp;</p><h1>block2</h1>',
372  '<h1>block1</h1>' . CRLF . CRLF . CRLF . '<h1>block2</h1>',
373  ],
374  'Block followed by double spacing paragraph and by block, linebreak-separated' => [
375  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
376  '<h1>block1</h1>' . CRLF . CRLF . CRLF . '<h1>block2</h1>',
377  ],
378  'Block followed by paragraph and block' => [
379  '<h1>block1</h1><p>paragraph</p><h1>block2</h1>',
380  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<h1>block2</h1>',
381  ],
382  'Block followed by paragraph and block, linebreak-separated' => [
383  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<h1>block2</h1>',
384  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<h1>block2</h1>',
385  ],
386  'Block followed by paragraph, spacing paragraph and block' => [
387  '<h1>block1</h1><p>paragraph</p><p>&nbsp;</p><h1>block2</h1>',
388  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . CRLF . '<h1>block2</h1>',
389  ],
390  'Block followed by paragraph, spacing paragraph and block, linebreak-separated' => [
391  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
392  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . CRLF . '<h1>block2</h1>',
393  ],
394  ];
395  }
396 
397  #[DataProvider('paragraphCorrectlyTransformedOnWayToDatabaseProvider')]
398  #[Test]
399  public function ‪paragraphCorrectlyTransformedOnWayToDatabase($content, $expectedResult): void
400  {
401  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
402  $subject = new ‪RteHtmlParser($eventDispatcher);
403  self::assertEquals($expectedResult, $subject->transformTextForPersistence($content, $this->procOptions));
404  }
405 
410  {
411  return [
412  'Empty string' => [
413  '',
414  '',
415  ],
416  'Single linebreak' => [
417  CRLF,
418  '<p>&nbsp;</p>',
419  ],
420  'Double linebreak' => [
421  CRLF . CRLF,
422  '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
423  ],
424  'Triple linebreak' => [
425  CRLF . CRLF . CRLF,
426  '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
427  ],
428  'Paragraph' => [
429  'paragraph',
430  '<p>paragraph</p>',
431  ],
432  'Paragraph followed by single linebreak' => [
433  'paragraph' . CRLF,
434  '<p>paragraph</p>',
435  ],
436  'Paragraph followed by double linebreak' => [
437  'paragraph' . CRLF . CRLF,
438  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
439  ],
440  'Paragraph followed by triple linebreak' => [
441  'paragraph' . CRLF . CRLF . CRLF,
442  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
443  ],
444  'Paragraph followed by paragraph' => [
445  'paragraph1' . CRLF . 'paragraph2',
446  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
447  ],
448  'Paragraph followed by double linebreak and paragraph' => [
449  'paragraph1' . CRLF . CRLF . 'paragraph2',
450  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
451  ],
452  'Paragraph followed by triple linebreak and paragraph' => [
453  'paragraph1' . CRLF . CRLF . CRLF . 'paragraph2',
454  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
455  ],
456  'Paragraph followed by block' => [
457  'paragraph<h1>block</h1>',
458  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
459  ],
460  'Paragraph followed by linebreak and block' => [
461  'paragraph' . CRLF . '<h1>block</h1>',
462  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
463  ],
464  'Paragraph followed by double linebreak and block' => [
465  'paragraph' . CRLF . CRLF . '<h1>block</h1>',
466  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
467  ],
468  'Paragraph followed by triple linebreak and block' => [
469  'paragraph' . CRLF . CRLF . CRLF . '<h1>block</h1>',
470  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
471  ],
472  'Block followed by block' => [
473  '<h1>block1</h1><h1>block2</h1>',
474  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
475  ],
476  'Block followed by single linebreak and block' => [
477  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
478  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
479  ],
480  'Block followed by double linebreak and block' => [
481  '<h1>block1</h1>' . CRLF . CRLF . '<h1>block2</h1>',
482  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
483  ],
484  'Block followed by triple linebreak and block' => [
485  '<h1>block1</h1>' . CRLF . CRLF . CRLF . '<h1>block2</h1>',
486  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
487  ],
488  'Block followed by paragraph and block' => [
489  '<h1>block1</h1>' . CRLF . 'paragraph' . CRLF . '<h1>block2</h1>',
490  '<h1>block1</h1>' . CRLF . '<p>paragraph</p>' . CRLF . '<h1>block2</h1>',
491  ],
492  ];
493  }
494 
495  #[DataProvider('lineBreakCorrectlyTransformedOnWayToRTEProvider')]
496  #[Test]
497  public function ‪lineBreakCorrectlyTransformedOnWayToRTE($content, $expectedResult): void
498  {
499  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
500  $subject = new ‪RteHtmlParser($eventDispatcher);
501  self::assertEquals($expectedResult, $subject->transformTextForRichTextEditor($content, $this->procOptions));
502  }
503 
508  {
509  return [
510  'Empty string' => [
511  '',
512  '',
513  ],
514  'Empty paragraph' => [
515  '<p></p>',
516  '<p>&nbsp;</p>',
517  ],
518  'Double empty paragraph' => [
519  '<p></p><p></p>',
520  '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
521  ],
522  'Triple empty paragraph' => [
523  '<p></p><p></p><p></p>',
524  '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
525  ],
526  'Plain text' => [
527  'plain text',
528  '<p>plain text</p>',
529  ],
530  'Plain text followed by linebreak' => [
531  'plain text' . CRLF,
532  '<p>plain text </p>',
533  ],
534  'Plain text followed by paragraph' => [
535  'plain text<p>paragraph</p>',
536  '<p>plain text</p>' . CRLF . '<p>paragraph</p>',
537  ],
538  'Spacing paragraph' => [
539  '<p>&nbsp;</p>',
540  '<p>&nbsp;</p>',
541  ],
542  'Double spacing paragraph' => [
543  '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
544  '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
545  ],
546  'Paragraph' => [
547  '<p>paragraph</p>',
548  '<p>paragraph</p>',
549  ],
550  'Paragraph followed by linebreak' => [
551  '<p>paragraph</p>' . CRLF,
552  '<p>paragraph</p>',
553  ],
554  'Paragraph followed by spacing paragraph' => [
555  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
556  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>',
557  ],
558  'Paragraph followed by double spacing paragraph' => [
559  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
560  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>',
561  ],
562  'Paragraph followed by paragraph' => [
563  '<p>paragraph1</p><p>paragraph2</p>',
564  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
565  ],
566  'Paragraph followed by paragraph, linebreak-separated' => [
567  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
568  '<p>paragraph1</p>' . CRLF . '<p>paragraph2</p>',
569  ],
570  'Paragraph followed by spacing paragraph and by paragraph' => [
571  '<p>paragraph1</p><p>&nbsp;</p><p>paragraph2</p>',
572  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
573  ],
574  'Paragraph followed by spacing paragraph and by paragraph, linebreak-separated' => [
575  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
576  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
577  ],
578  'Paragraph followed by double spacing paragraph and by paragraph' => [
579  '<p>paragraph1</p><p>&nbsp;</p><p>&nbsp;</p><p>paragraph2</p>',
580  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
581  ],
582  'Paragraph followed by double spacing paragraph and by paragraph, linebreak-separated' => [
583  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
584  '<p>paragraph1</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>paragraph2</p>',
585  ],
586  'Paragraph followed by block' => [
587  '<p>paragraph</p><h1>block</h1>',
588  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
589  ],
590  'Paragraph followed by block, linebreak-separated' => [
591  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
592  '<p>paragraph</p>' . CRLF . '<h1>block</h1>',
593  ],
594  'Paragraph followed by spacing paragraph and by block' => [
595  '<p>paragraph</p><p>&nbsp;</p><h1>block</h1>',
596  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
597  ],
598  'Paragraph followed by spacing paragraph and by block, linebreak-separated' => [
599  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
600  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
601  ],
602  'Paragraph followed by double spacing paragraph and by block' => [
603  '<p>paragraph</p><p>&nbsp;</p><p>&nbsp;</p><h1>block</h1>',
604  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
605  ],
606  'Paragraph followed by double spacing paragraph and by block, linebreak-separated' => [
607  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
608  '<p>paragraph</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block</h1>',
609  ],
610  'Block followed by block' => [
611  '<h1>block1</h1><h1>block2</h1>',
612  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
613  ],
614  'Block followed by block, linebreak-separated' => [
615  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
616  '<h1>block1</h1>' . CRLF . '<h1>block2</h1>',
617  ],
618  'Block followed by empty paragraph and by block' => [
619  '<h1>block1</h1><p></p><h1>block2</h1>',
620  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
621  ],
622  'Block followed by empty paragraph and by block, linebreak-separated' => [
623  '<h1>block1</h1>' . CRLF . '<p></p>' . CRLF . '<h1>block2</h1>',
624  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
625  ],
626  'Block followed by spacing paragraph and by block' => [
627  '<h1>block1</h1><p>&nbsp;</p><h1>block2</h1>',
628  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
629  ],
630  'Block followed by spacing paragraph and by block, linebreak-separated' => [
631  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
632  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
633  ],
634  'Block followed by double spacing paragraph and by block' => [
635  '<h1>block1</h1><p>&nbsp;</p><p>&nbsp;</p><h1>block2</h1>',
636  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
637  ],
638  'Block followed by double spacing paragraph and by block, linebreak-separated' => [
639  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
640  '<h1>block1</h1>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<p>&nbsp;</p>' . CRLF . '<h1>block2</h1>',
641  ],
642  ];
643  }
644 
645  #[DataProvider('paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider')]
646  #[Test]
647  public function ‪paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult): void
648  {
649  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
650  $subject = new ‪RteHtmlParser($eventDispatcher);
651  self::assertEquals($expectedResult, $subject->transformTextForRichTextEditor($subject->transformTextForPersistence($content, $this->procOptions), $this->procOptions));
652  }
653 
658  {
659  return [
660  [
661  '<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
662  '<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
663  ],
664  [
665  '<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
666  '<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
667  ],
668  [
669  '<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
670  '<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
671  ],
672  [
673  '<p><a id="some_anchor">Some text inside the anchor</a></p>',
674  '<p><a id="some_anchor">Some text inside the anchor</a></p>',
675  ],
676  ];
677  }
678 
679  #[DataProvider('anchorCorrectlyTransformedOnWayToDatabaseProvider')]
680  #[Test]
681  public function ‪anchorCorrectlyTransformedOnWayToDatabase(string $content, string $expectedResult): void
682  {
683  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
684  $subject = new ‪RteHtmlParser($eventDispatcher);
685  self::assertEquals($expectedResult, $subject->transformTextForPersistence($content, $this->procOptions));
686  }
687 
692  {
693  return [
694  [
695  '<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
696  '<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
697  ],
698  [
699  '<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
700  '<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
701  ],
702  [
703  '<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
704  '<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
705  ],
706  [
707  '<p><a id="some_anchor">Some text inside the anchor</a></p>',
708  '<p><a id="some_anchor">Some text inside the anchor</a></p>',
709  ],
710  ];
711  }
712 
713  #[DataProvider('anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTEProvider')]
714  #[Test]
715  public function ‪anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTE(string $content, string $expectedResult): void
716  {
717  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
718  $subject = new ‪RteHtmlParser($eventDispatcher);
719  self::assertEquals($expectedResult, $subject->transformTextForRichTextEditor($subject->transformTextForPersistence($content, $this->procOptions), $this->procOptions));
720  }
721 
722  #[Test]
724  {
725  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
726  $subject = new ‪RteHtmlParser($eventDispatcher);
727  self::assertEquals('<abbr>Allowed outside of p-tag</abbr>', $subject->transformTextForRichTextEditor('<abbr>Allowed outside of p-tag</abbr>', $this->procOptions));
728  self::assertEquals('<p><span>Not allowed outside of p-tag</span></p>', $subject->transformTextForRichTextEditor('<span>Not allowed outside of p-tag</span>', $this->procOptions));
729  }
730 
731  #[Test]
733  {
734  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
735  $subject = new ‪RteHtmlParser($eventDispatcher);
736  self::assertEquals('<figure class="table">' . CRLF . '<table>Allowed outside of p-tag</table>' . CRLF . '</figure>', $subject->transformTextForRichTextEditor('<figure class="table">' . CRLF . '<table>Allowed outside of p-tag</table>' . CRLF . '</figure>', $this->procOptions));
737  self::assertEquals('<figure class="table">' . CRLF . '<table>Allowed outside of p-tag</table>' . CRLF . '<figcaption>My Logo</figcaption></figure>', $subject->transformTextForRichTextEditor('<figure class="table">' . CRLF . '<table>Allowed outside of p-tag</table>' . CRLF . '<figcaption>My Logo</figcaption></figure>', $this->procOptions));
738  }
739 
740  #[Test]
742  {
743  $eventDispatcher = $this->createMock(EventDispatcherInterface::class);
744  $subject = new ‪RteHtmlParser($eventDispatcher);
745  $input = '<remove>Foo</remove><keep>Bar</keep>';
746  $transformed = 'Foo<keep>Bar</keep>';
747  $result = $subject->transformTextForPersistence($input, [
748  'mode' => 'default',
749  'allowTags' => 'keep',
750  ]);
751  self::assertEquals($transformed, $result);
752  $result = $subject->transformTextForPersistence($input, [
753  'mode' => 'default',
754  'allowTags' => 'keep,remove',
755  ]);
756  self::assertEquals($input, $result);
757  }
758 }
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTE
‪anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTE(string $content, string $expectedResult)
Definition: RteHtmlParserTest.php:715
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\paragraphCorrectlyTransformedOnWayToDatabaseProvider
‪static paragraphCorrectlyTransformedOnWayToDatabaseProvider()
Definition: RteHtmlParserTest.php:223
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\brTagCorrectlyTransformedOnWayToDatabaseAndBackToRte
‪brTagCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult)
Definition: RteHtmlParserTest.php:211
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\paragraphCorrectlyTransformedOnWayToDatabase
‪paragraphCorrectlyTransformedOnWayToDatabase($content, $expectedResult)
Definition: RteHtmlParserTest.php:399
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\$procOptions
‪array $procOptions
Definition: RteHtmlParserTest.php:30
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\resetsAllowTagsWhenProcessingConfigurationChanges
‪resetsAllowTagsWhenProcessingConfigurationChanges()
Definition: RteHtmlParserTest.php:741
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\tableAndFigureApplyCorrectlyOutsideOfParagraphTags
‪tableAndFigureApplyCorrectlyOutsideOfParagraphTags()
Definition: RteHtmlParserTest.php:732
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\lineBreakCorrectlyTransformedOnWayToRTE
‪lineBreakCorrectlyTransformedOnWayToRTE($content, $expectedResult)
Definition: RteHtmlParserTest.php:497
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\anchorCorrectlyTransformedOnWayToDatabase
‪anchorCorrectlyTransformedOnWayToDatabase(string $content, string $expectedResult)
Definition: RteHtmlParserTest.php:681
‪TYPO3\CMS\Core\Tests\Unit\Html
Definition: HtmlCropperTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTEProvider
‪static anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTEProvider()
Definition: RteHtmlParserTest.php:691
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\anchorCorrectlyTransformedOnWayToDatabaseProvider
‪static anchorCorrectlyTransformedOnWayToDatabaseProvider()
Definition: RteHtmlParserTest.php:657
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\brTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider
‪static brTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider()
Definition: RteHtmlParserTest.php:191
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\lineBreakCorrectlyTransformedOnWayToRteProvider
‪static lineBreakCorrectlyTransformedOnWayToRteProvider()
Definition: RteHtmlParserTest.php:409
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRte
‪paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult)
Definition: RteHtmlParserTest.php:647
‪TYPO3\CMS\Core\Html\RteHtmlParser
Definition: RteHtmlParser.php:40
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider
‪static hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider()
Definition: RteHtmlParserTest.php:115
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\allowTagsOutsidePreventsWrappingTaginPTag
‪allowTagsOutsidePreventsWrappingTaginPTag()
Definition: RteHtmlParserTest.php:723
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest
Definition: RteHtmlParserTest.php:27
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider
‪static paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRteProvider()
Definition: RteHtmlParserTest.php:507
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\hrTagCorrectlyTransformedOnWayToDataBaseDataProvider
‪static hrTagCorrectlyTransformedOnWayToDataBaseDataProvider()
Definition: RteHtmlParserTest.php:35
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: RteHtmlParserTest.php:28
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRte
‪hrTagCorrectlyTransformedOnWayToDatabaseAndBackToRte($content, $expectedResult)
Definition: RteHtmlParserTest.php:179
‪TYPO3\CMS\Core\Tests\Unit\Html\RteHtmlParserTest\hrTagCorrectlyTransformedOnWayToDataBase
‪hrTagCorrectlyTransformedOnWayToDataBase($content, $expectedResult)
Definition: RteHtmlParserTest.php:103