2 namespace TYPO3\CMS\Core\Tests\Unit\Html
;
5 * This file is part of the TYPO3 CMS project.
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Core\Html\HtmlParser
;
20 * Testcase for \TYPO3\CMS\Core\Html\HtmlParser
22 class HtmlParserTest
extends \TYPO3\CMS\Core\Tests\UnitTestCase
{
25 * @var \TYPO3\CMS\Core\Html\HtmlParser
27 protected $subject = NULL;
29 protected function setUp() {
30 $this->subject
= new HtmlParser();
34 * Data provider for getSubpart
38 public function getSubpartDataProvider() {
40 'No start marker' => array(
45 'No stop marker' => array(
47 <!-- ###SUBPART### Start -->
53 'Start and stop marker in HTML comment' => array(
55 <!-- ###SUBPART### Start -->
57 <!-- ###SUBPART### End -->
64 'Stop marker in HTML comment' => array(
68 <!-- ###SUBPART### End -->
75 'Start marker in HTML comment' => array(
77 <!-- ###SUBPART### Start -->
86 'Start and stop marker direct' => array(
102 * @dataProvider getSubpartDataProvider
104 public function getSubpart($content, $marker, $expected) {
105 $this->assertSame($expected, HtmlParser
::getSubpart($content, $marker));
109 * Data provider for substituteMarkerAndSubpartArrayRecursiveResolvesMarkersAndSubpartsArray
111 public function substituteMarkerAndSubpartArrayRecursiveResolvesMarkersAndSubpartsArrayDataProvider() {
112 $template = '###SINGLEMARKER1###
113 <!-- ###FOO### begin -->
114 <!-- ###BAR### begin -->
116 <!-- ###BAR### end -->
117 <!-- ###FOOTER### begin -->
119 <!-- ###FOOTER### end -->
120 <!-- ###FOO### end -->';
137 'Single marker' => array(
138 '###SINGLEMARKER###',
140 '###SINGLEMARKER###' => 'Value 1'
147 'Subpart marker' => array(
150 '###SINGLEMARKER1###' => 'Value 1',
151 '###FOO###' => array(
153 '###BAR###' => array(
155 '###SINGLEMARKER2###' => 'Value 2.1'
158 '###SINGLEMARKER2###' => 'Value 2.2'
161 '###FOOTER###' => array(
163 '###SINGLEMARKER3###' => 'Value 3.1'
166 '###SINGLEMARKER3###' => 'Value 3.2'
177 'Subpart marker with wrap' => array(
180 'SINGLEMARKER1' => 'Value 1',
185 'SINGLEMARKER2' => 'Value 2.1'
188 'SINGLEMARKER2' => 'Value 2.2'
193 'SINGLEMARKER3' => 'Value 3.1'
196 'SINGLEMARKER3' => 'Value 3.2'
207 'Subpart marker with lower marker array keys' => array(
210 '###singlemarker1###' => 'Value 1',
211 '###foo###' => array(
213 '###bar###' => array(
215 '###singlemarker2###' => 'Value 2.1'
218 '###singlemarker2###' => 'Value 2.2'
221 '###footer###' => array(
223 '###singlemarker3###' => 'Value 3.1'
226 '###singlemarker3###' => 'Value 3.2'
237 'Subpart marker with unused markers' => array(
240 '###FOO###' => array(
242 '###BAR###' => array(
244 '###SINGLEMARKER2###' => 'Value 2.1'
247 '###FOOTER###' => array(
249 '###SINGLEMARKER3###' => 'Value 3.1'
268 'Subpart marker with empty subpart' => array(
271 '###SINGLEMARKER1###' => 'Value 1',
272 '###FOO###' => array(
274 '###BAR###' => array(
276 '###SINGLEMARKER2###' => 'Value 2.1'
279 '###SINGLEMARKER2###' => 'Value 2.2'
282 '###FOOTER###' => array()
304 * @dataProvider substituteMarkerAndSubpartArrayRecursiveResolvesMarkersAndSubpartsArrayDataProvider
306 public function substituteMarkerAndSubpartArrayRecursiveResolvesMarkersAndSubpartsArray($template, $markersAndSubparts, $wrap, $uppercase, $deleteUnused, $expected) {
307 $this->assertSame($expected, HtmlParser
::substituteMarkerAndSubpartArrayRecursive($template, $markersAndSubparts, $wrap, $uppercase, $deleteUnused));
313 public function cDataWillRemainUnmodifiedDataProvider() {
315 'single-line CDATA' => array(
316 '/*<![CDATA[*/ <hello world> /*]]>*/',
317 '/*<![CDATA[*/ <hello world> /*]]>*/',
319 'multi-line CDATA #1' => array(
320 '/*<![CDATA[*/' . LF
. '<hello world> /*]]>*/',
321 '/*<![CDATA[*/' . LF
. '<hello world> /*]]>*/',
323 'multi-line CDATA #2' => array(
324 '/*<![CDATA[*/ <hello world>' . LF
. '/*]]>*/',
325 '/*<![CDATA[*/ <hello world>' . LF
. '/*]]>*/',
327 'multi-line CDATA #3' => array(
328 '/*<![CDATA[*/' . LF
. '<hello world>' . LF
. '/*]]>*/',
329 '/*<![CDATA[*/' . LF
. '<hello world>' . LF
. '/*]]>*/',
336 * @param string $source
337 * @param string $expected
338 * @dataProvider cDataWillRemainUnmodifiedDataProvider
340 public function xHtmlCleaningDoesNotModifyCDATA($source, $expected) {
341 $result = $this->subject
->XHTML_clean($source);
342 $this->assertSame($expected, $result);
346 * Data provider for spanTagCorrectlyRemovedWhenRmTagIfNoAttribIsConfigured
348 public static function spanTagCorrectlyRemovedWhenRmTagIfNoAttribIsConfiguredDataProvider() {
350 'Span tag with no attrib' => array(
354 'Span tag with allowed id attrib' => array(
355 '<span id="id">text</span>',
356 '<span id="id">text</span>'
358 'Span tag with disallowed style attrib' => array(
359 '<span style="line-height: 12px;">text</span>',
367 * @dataProvider spanTagCorrectlyRemovedWhenRmTagIfNoAttribIsConfiguredDataProvider
369 public function tagCorrectlyRemovedWhenRmTagIfNoAttribIsConfigured($content, $expectedResult) {
371 'allowTags' => 'span',
374 'allowedAttribs' => 'id',
375 'rmTagIfNoAttrib' => 1
379 $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
385 public function rmTagIfNoAttribIsConfiguredDoesNotChangeNestingType() {
387 'allowTags' => 'div,span',
388 'rmTagIfNoAttrib' => 'span',
389 'globalNesting' => 'div,span'
391 $content = '<span></span><span id="test"><div></span></div>';
392 $expectedResult = '<span id="test"></span>';
393 $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
397 * Data provider for localNestingCorrectlyRemovesInvalidTags
399 public static function localNestingCorrectlyRemovesInvalidTagsDataProvider() {
401 'Valid nesting is untouched' => array(
405 'Valid nesting with content is untouched' => array(
406 'testa<B>test1<I>test2</B>test3</I>testb',
407 'testa<B>test1<I>test2</B>test3</I>testb'
409 'Superflous tags are removed' => array(
410 '</B><B><I></B></I></B>',
413 'Superflous tags with content are removed' => array(
414 'test1</B>test2<B>test3<I>test4</B>test5</I>test6</B>test7',
415 'test1test2<B>test3<I>test4</B>test5</I>test6test7'
417 'Another valid nesting test' => array(
418 '<span><div></span></div>',
419 '<span><div></span></div>',
426 * @dataProvider localNestingCorrectlyRemovesInvalidTagsDataProvider
427 * @param string $content
428 * @param string $expectedResult
430 public function localNestingCorrectlyRemovesInvalidTags($content, $expectedResult) {
432 'allowTags' => 'div,span,b,i',
433 'localNesting' => 'div,span,b,i',
435 $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
439 * Data provider for globalNestingCorrectlyRemovesInvalidTags
441 public static function globalNestingCorrectlyRemovesInvalidTagsDataProvider() {
443 'Valid nesting is untouched' => array(
447 'Valid nesting with content is untouched' => array(
448 'testa<B>test1<I>test2</I>test3</B>testb',
449 'testa<B>test1<I>test2</I>test3</B>testb'
451 'Invalid nesting is cleaned' => array(
452 '</B><B><I></B></I></B>',
455 'Invalid nesting with content is cleaned' => array(
456 'test1</B>test2<B>test3<I>test4</B>test5</I>test6</B>test7',
457 'test1test2<B>test3test4</B>test5test6test7'
459 'Another invalid nesting test' => array(
460 '<span><div></span></div>',
468 * @dataProvider globalNestingCorrectlyRemovesInvalidTagsDataProvider
469 * @param string $content
470 * @param string $expectedResult
472 public function globalNestingCorrectlyRemovesInvalidTags($content, $expectedResult) {
474 'allowTags' => 'span,div,b,i',
475 'globalNesting' => 'span,div,b,i',
477 $this->assertEquals($expectedResult, $this->parseConfigAndCleanHtml($tsConfig, $content));
483 public function emptyTagsDataProvider() {
485 array(0 , NULL, FALSE, '<h1></h1>', '<h1></h1>'),
486 array(1 , NULL, FALSE, '<h1></h1>', ''),
487 array(1 , NULL, FALSE, '<h1>hallo</h1>', '<h1>hallo</h1>'),
488 array(1 , NULL, FALSE, '<h1 class="something"></h1>', ''),
489 array(1 , NULL, FALSE, '<h1 class="something"></h1><h2></h2>', ''),
490 array(1 , 'h2', FALSE, '<h1 class="something"></h1><h2></h2>', '<h1 class="something"></h1>'),
491 array(1 , 'h2, h1', FALSE, '<h1 class="something"></h1><h2></h2>', ''),
492 array(1 , NULL, FALSE, '<div><p></p></div>', ''),
493 array(1 , NULL, FALSE, '<div><p> </p></div>', '<div><p> </p></div>'),
494 array(1 , NULL, TRUE, '<div><p> </p></div>', ''),
495 array(1 , NULL, TRUE, '<div> <p></p></div>', ''),
496 array(1 , NULL, FALSE, '<div>Some content<p></p></div>', '<div>Some content</div>'),
497 array(1 , NULL, TRUE, '<div>Some content<p></p></div>', '<div>Some content</div>'),
498 array(1 , NULL, FALSE, '<div>Some content</div>', '<div>Some content</div>'),
499 array(1 , NULL, TRUE, '<div>Some content</div>', '<div>Some content</div>'),
500 array(1 , NULL, FALSE, '<a href="#skiplinks">Skiplinks </a><b></b>', '<a href="#skiplinks">Skiplinks </a>'),
501 array(1 , NULL, TRUE, '<a href="#skiplinks">Skiplinks </a><b></b>', '<a href="#skiplinks">Skiplinks </a>'),
507 * @dataProvider emptyTagsDataProvider
508 * @param bool $stripOn TRUE if stripping should be activated.
509 * @param string $tagList Comma seperated list of tags that should be stripped.
510 * @param bool $treatNonBreakingSpaceAsEmpty If TRUE will be considered empty.
511 * @param string $content The HTML code that should be modified.
512 * @param string $expectedResult The expected HTML code result.
514 public function stripEmptyTags($stripOn, $tagList, $treatNonBreakingSpaceAsEmpty, $content, $expectedResult) {
516 'keepNonMatchedTags' => 1,
517 'stripEmptyTags' => $stripOn,
518 'stripEmptyTags.' => array(
520 'treatNonBreakingSpaceAsEmpty' => $treatNonBreakingSpaceAsEmpty
524 $result = $this->parseConfigAndCleanHtml($tsConfig, $content);
525 $this->assertEquals($expectedResult, $result);
529 * Calls HTMLparserConfig() and passes the generated config to the HTMLcleaner() method on the current subject.
531 * @param array $tsConfig The TypoScript that should be used to generate the HTML parser config.
532 * @param string $content The content that should be parsed by the HTMLcleaner.
533 * @return string The parsed content.
535 protected function parseConfigAndCleanHtml(array $tsConfig, $content) {
536 $config = $this->subject
->HTMLparserConfig($tsConfig);
537 return $this->subject
->HTMLcleaner($content, $config[0], $config[1], $config[2], $config[3]);