previousLocaleSetting = null; // Set test handler ShortcodeParser::get('htmltest') ->register('test_shortcode', [TestShortcode::class, 'handle_shortcode']); ShortcodeParser::set_active('htmltest'); } protected function tearDown(): void { // If a test sets the locale, reset it on teardown if ($this->previousLocaleSetting) { setlocale(LC_CTYPE, $this->previousLocaleSetting); } ShortcodeParser::set_active('default'); parent::tearDown(); } /** * Test {@link Text->LimitCharacters()} */ public function providerLimitCharacters() { // HTML characters are stripped safely return [ ['The little brown fox jumped over the lazy cow.', 'The little brown fox…'], ['

Short & Sweet

', 'Short & Sweet'], ['This text contains & in it', 'This text contains &…'], ]; } /** * Test {@link DBHTMLText->LimitCharacters()} * * @dataProvider providerLimitCharacters * @param string $originalValue * @param string $expectedValue */ public function testLimitCharacters($originalValue, $expectedValue) { $textObj = DBField::create_field('HTMLFragment', $originalValue); $result = $textObj->obj('LimitCharacters')->forTemplate(); $this->assertEquals($expectedValue, $result); } /** * @return array */ public function providerLimitCharactersToClosestWord() { // HTML is converted safely to plain text return [ // Standard words limited, ellipsis added if truncated ['

Lorem ipsum dolor sit amet

', 24, 'Lorem ipsum dolor sit…'], // Complete words less than the character limit don't get truncated, ellipsis not added ['

Lorem ipsum

', 24, 'Lorem ipsum'], ['

Lorem

', 24, 'Lorem'], ['', 24, ''], // No words produces nothing! // Special characters are encoded safely ['Nice & Easy', 24, 'Nice & Easy'], // HTML is safely converted to plain text ['

Lorem ipsum dolor sit amet

', 24, 'Lorem ipsum dolor sit…'], ['

Lorem ipsum dolor sit amet

', 24, 'Lorem ipsum dolor sit…'], ['

Lorem ipsum

', 24, 'Lorem ipsum'], ['Lorem & ipsum dolor sit amet', 24, 'Lorem & ipsum dolor sit…'] ]; } /** * Test {@link DBHTMLText->LimitCharactersToClosestWord()} * * @dataProvider providerLimitCharactersToClosestWord * * @param string $originalValue Raw string input * @param int $limit * @param string $expectedValue Expected template value */ public function testLimitCharactersToClosestWord($originalValue, $limit, $expectedValue) { $textObj = DBField::create_field('HTMLFragment', $originalValue); $result = $textObj->obj('LimitCharactersToClosestWord', [$limit])->forTemplate(); $this->assertEquals($expectedValue, $result); } public function providerSummary() { return [ [ '

Should strip tags, but leave text

', 50, 'Should strip tags, but leave text', ], [ // Line breaks are preserved '

Unclosed tags
should not phase it

', 50, "Unclosed tags
\nshould not phase it", ], [ // Paragraphs converted to linebreak '

Second paragraph

should not cause errors or appear in output

', 50, "Second paragraph
\n
\nshould not cause errors or appear in output", ], [ '

Second paragraph

should not cause errors or appear in output

', 50, "Second paragraph
\n
\nshould not cause errors or appear in output", ], [ '

Second paragraph

should not cause errors or appear in output

', 50, "Second paragraph
\n
\nshould not cause errors or appear in output", ], [ '

example text words hello

', 50, 'example text words hello', ], // Shorter limits [ '

A long paragraph should be cut off if limit is set

', 5, 'A long paragraph should be…', ], [ '

No matter how many tags are in it

', 5, 'No matter how many tags…', ], [ '

A sentence is. nicer than hard limits

', 5, 'A sentence is.', ], ]; } /** * @dataProvider providerSummary * @param string $originalValue * @param int $limit * @param string $expectedValue */ public function testSummary($originalValue, $limit, $expectedValue) { $textObj = DBField::create_field('HTMLFragment', $originalValue); $result = $textObj->obj('Summary', [$limit])->forTemplate(); $this->assertEquals($expectedValue, $result); } public function testSummaryEndings() { $cases = [ '...', ' -> more', '' ]; $orig = '

Cut it off, cut it off

'; $match = 'Cut it off, cut'; foreach ($cases as $add) { $textObj = DBField::create_field('HTMLFragment', $orig); $result = $textObj->obj('Summary', [4, $add])->forTemplate(); $this->assertEquals($match . Convert::raw2xml($add), $result); } } public function providerFirstSentence() { return [ // Same behaviour as DBTextTest ['', ''], ['First sentence.', 'First sentence.'], ['First sentence. Second sentence', 'First sentence.'], ['First sentence? Second sentence', 'First sentence?'], ['First sentence! Second sentence', 'First sentence!'], // DBHTHLText strips HTML first ['
First sentence.', 'First sentence.'], ['

First sentence. Second sentence. Third sentence

', 'First sentence.'], ]; } /** * @dataProvider providerFirstSentence * @param string $originalValue * @param string $expectedValue */ public function testFirstSentence($originalValue, $expectedValue) { $textObj = DBField::create_field('HTMLFragment', $originalValue); $result = $textObj->obj('FirstSentence')->forTemplate(); $this->assertEquals($expectedValue, $result); } public function testCreate() { /** @var DBHTMLText $field */ $field = Injector::inst()->create("HTMLFragment(['whitelist' => 'link'])", 'MyField'); $this->assertEquals(['link'], $field->getWhitelist()); $field = Injector::inst()->create("HTMLFragment(['whitelist' => 'link,a'])", 'MyField'); $this->assertEquals(['link', 'a'], $field->getWhitelist()); $field = Injector::inst()->create("HTMLFragment(['whitelist' => ['link', 'a']])", 'MyField'); $this->assertEquals(['link', 'a'], $field->getWhitelist()); $field = Injector::inst()->create("HTMLFragment", 'MyField'); $this->assertEmpty($field->getWhitelist()); // Test shortcodes $field = Injector::inst()->create("HTMLFragment(['shortcodes' => true])", 'MyField'); $this->assertEquals(true, $field->getProcessShortcodes()); $field = Injector::inst()->create("HTMLFragment(['shortcodes' => false])", 'MyField'); $this->assertEquals(false, $field->getProcessShortcodes()); // Mix options $field = Injector::inst()->create("HTMLFragment(['shortcodes' => true, 'whitelist' => ['a'])", 'MyField'); $this->assertEquals(true, $field->getProcessShortcodes()); $this->assertEquals(['a'], $field->getWhitelist()); } public function providerToPlain() { return [ [ '

Lots of HTML nested tags', 'Lots of HTML nested tags', ], [ '

Multi

Paragraph
Also has multilines.

', "Multi\n\nParagraph\nAlso has multilines.", ], [ '

Collapses

Excessive


Newlines

', "Collapses\n\nExcessive\n\nNewlines", ], 'Unicode character that could be confused for a line break' =>[ '充美好实的一天', '充美好实的一天' ] ]; } /** * @dataProvider providerToPlain * @param string $html * @param string $plain */ public function testToPlain($html, $plain) { /** * @var DBHTMLText $textObj */ $textObj = DBField::create_field('HTMLFragment', $html); $this->assertEquals($plain, $textObj->Plain()); } /** * each test is in the format input, charactere limit, highlight, expected output * * @return array */ public function providerContextSummary() { return [ [ 'This is some text. It is a test', 20, 'test', '… text. It is a test' ], [ // Retains case of original string 'This is some test text. Test test what if you have multiple keywords.', 50, 'some test', 'This is some test text.' . ' Test test what if you have…' ], [ 'Here is some text & HTML included', 20, 'html', '… text & HTML inc…' ], [ 'A dog ate a cat while looking at a Foobar', 100, 'a', // test that it does not highlight too much (eg every a) 'A dog ate a cat while looking at a Foobar', ], [ 'A dog ate a cat while looking at a Foobar', 100, 'ate', // it should highlight 3 letters or more. 'A dog ate a cat while looking at a Foobar', ], // HTML Content is plain-textified, and incorrect tags removed [ '

A dog ate a cat while looking at a Foobar

', 100, 'ate', // it should highlight 3 letters or more. 'A dog ate a cat while looking at a Foobar', ], [ '

This is a lot of text before this but really, this is a test sentence

with about more stuff after the line break

', 35, 'test', '… really, this is a test sentence…' ], [ '

This is a lot of text before this but really, this is a test sentence

with about more stuff after the line break

', 50, 'with', '… sentence

with about more stuff…' ] ]; } /** * @dataProvider providerContextSummary * @param string $originalValue Input * @param int $limit Numer of characters * @param string $keywords Keywords to highlight * @param string $expectedValue Expected output (XML encoded safely) */ public function testContextSummary($originalValue, $limit, $keywords, $expectedValue) { $text = DBField::create_field('HTMLFragment', $originalValue); $result = $text->obj('ContextSummary', [$limit, $keywords])->forTemplate(); // it should highlight 3 letters or more. $this->assertEquals($expectedValue, $result); } public function testRAW() { $data = DBField::create_field('HTMLFragment', 'This & This'); $this->assertEquals('This & This', $data->RAW()); $data = DBField::create_field('HTMLFragment', 'This & This'); $this->assertEquals('This & This', $data->RAW()); } public function testXML() { $data = DBField::create_field('HTMLFragment', 'This & This'); $this->assertEquals('This & This', $data->XML()); $data = DBField::create_field('HTMLFragment', 'This & This'); $this->assertEquals('This & This', $data->XML()); } public function testHTML() { $data = DBField::create_field('HTMLFragment', 'This & This'); $this->assertEquals('This & This', $data->HTML()); $data = DBField::create_field('HTMLFragment', 'This & This'); $this->assertEquals('This & This', $data->HTML()); } public function testJS() { $data = DBField::create_field('HTMLText', '"this is & test"'); $this->assertEquals('\"this is \x26amp; test\"', $data->JS()); } public function testATT() { // HTML Fragment $data = DBField::create_field('HTMLFragment', '"this is a test"'); $this->assertEquals('"this is a test"', $data->ATT()); // HTML Text (passes shortcodes + tidy) $data = DBField::create_field('HTMLText', '"'); $this->assertEquals('"', $data->ATT()); } public function testShortcodesProcessed() { /** * @var DBHTMLText $obj */ $obj = DBField::create_field( 'HTMLText', '

Some content [test_shortcode] with shortcode

' ); // Basic DBField methods process shortcodes $this->assertEquals( 'Some content shortcode content with shortcode', $obj->Plain() ); $this->assertEquals( '

Some content shortcode content with shortcode

', $obj->RAW() ); $this->assertEquals( '<p>Some content <strong>shortcode content</strong> with shortcode</p>', $obj->XML() ); $this->assertEquals( '<p>Some content <strong>shortcode content</strong> with shortcode</p>', $obj->HTML() ); // Test summary methods $this->assertEquals( 'Some content shortcode…', $obj->Summary(3) ); $this->assertEquals( 'Some content shortcode content with shortcode', $obj->LimitSentences(1) ); $this->assertEquals( 'Some content shortco…', $obj->LimitCharacters(20) ); } function testExists() { $h = new DBHTMLText(); $h->setValue(""); $this->assertFalse($h->exists()); $h->setValue("

content

"); $this->assertTrue($h->exists()); } function testWhitelist() { $textObj = new DBHTMLText('Test', ['whitelist'=> 'meta,link']); $this->assertEquals( '', $textObj->whitelistContent('

Remove

Remove Text'), 'Removes any elements not in whitelist excluding text elements' ); $textObj = new DBHTMLText('Test', ['whitelist'=> 'meta,link,text()']); $this->assertEquals( 'Keep Text', $textObj->whitelistContent('

Remove

Keep Text'), 'Removes any elements not in whitelist including text elements' ); } public function testShortCodeParsedInRAW() { $parser = ShortcodeParser::get('HTMLTextTest'); $parser->register( 'shortcode', function ($arguments, $content, $parser, $tagName, $extra) { return 'replaced'; } ); ShortcodeParser::set_active('HTMLTextTest'); /** * @var DBHTMLText $field */ $field = DBField::create_field('HTMLText', '

[shortcode]

'); $this->assertEquals('

replaced

', $field->RAW()); $this->assertEquals('

replaced

', (string)$field); $field->setOptions( [ 'shortcodes' => false, ] ); $this->assertEquals('

[shortcode]

', $field->RAW()); $this->assertEquals('

[shortcode]

', (string)$field); ShortcodeParser::set_active('default'); } public function testShortCodeParsedInTemplateHelpers() { $parser = ShortcodeParser::get('HTMLTextTest'); $parser->register( 'shortcode', function ($arguments, $content, $parser, $tagName, $extra) { return 'Replaced short code with this. home'; } ); ShortcodeParser::set_active('HTMLTextTest'); /** * @var DBHTMLText $field */ $field = DBField::create_field('HTMLText', '

[shortcode]

'); $this->assertEquals( '<p>Replaced short code with this. <a href="home">home</a></p>', $field->HTMLATT() ); $this->assertEquals( '%3Cp%3EReplaced+short+code+with+this.+%3Ca+href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E', $field->URLATT() ); $this->assertEquals( '%3Cp%3EReplaced%20short%20code%20with%20this.%20%3Ca%20href%3D%22home%22%3Ehome%3C%2Fa%3E%3C%2Fp%3E', $field->RAWURLATT() ); $this->assertEquals( '<p>Replaced short code with this. <a href="home">home</a></p>', $field->ATT() ); $this->assertEquals( '

Replaced short code with this. home

', $field->RAW() ); $this->assertEquals( '\x3cp\x3eReplaced short code with this. \x3ca href=\"home\"\x3ehome\x3c/a\x3e\x3c/p\x3e', $field->JS() ); $this->assertEquals( '<p>Replaced short code with this. <a href="home">home</a></p>', $field->HTML() ); $this->assertEquals( '<p>Replaced short code with this. <a href="home">home</a></p>', $field->XML() ); $this->assertEquals( 'Repl...', $field->LimitCharacters(4, '...') ); $this->assertEquals( 'Replaced...', $field->LimitCharactersToClosestWord(10, '...') ); $this->assertEquals( 'Replaced...', $field->LimitWordCount(1, '...') ); $this->assertEquals( '

replaced short code with this. home

', $field->LowerCase() ); $this->assertEquals( '

REPLACED SHORT CODE WITH THIS. HOME

', $field->UpperCase() ); $this->assertEquals( 'Replaced short code with this. home', $field->Plain() ); Config::nest(); Director::config()->set('alternate_base_url', 'http://example.com/'); $this->assertEquals( '

Replaced short code with this. home

', $field->AbsoluteLinks() ); Config::unnest(); $this->assertEquals( 'Replaced short code with this.', $field->LimitSentences(1) ); $this->assertEquals( 'Replaced short code with this.', $field->FirstSentence() ); $this->assertEquals( 'Replaced short…', $field->Summary(2) ); $this->assertEquals( 'Replaced short code with this. home', $field->FirstParagraph() ); $this->assertEquals( 'Replaced short code with this. home', $field->ContextSummary(500, 'short code') ); ShortcodeParser::set_active('default'); } public function testValidUtf8() { // Install a UTF-8 locale $this->previousLocaleSetting = setlocale(LC_CTYPE, 0); $locales = ['en_US.UTF-8', 'en_NZ.UTF-8', 'de_DE.UTF-8']; $localeInstalled = false; foreach ($locales as $locale) { if ($localeInstalled = setlocale(LC_CTYPE, $locale)) { break; } } // If the system doesn't have any of the UTF-8 locales, exit early if ($localeInstalled === false) { $this->markTestIncomplete('Unable to run this test because of missing locale!'); return; } $problematicText = html_entity_decode('

This is a Test with non-breaking space!

', ENT_COMPAT, 'UTF-8'); $textObj = new DBHTMLText('Test'); $textObj->setValue($problematicText); $this->assertTrue(mb_check_encoding($textObj->FirstSentence(), 'UTF-8')); $this->assertTrue(mb_check_encoding($textObj->Summary(), 'UTF-8')); } }