<?php
namespace Shaarli\Formatter;
use DateTime;
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\TestCase;
/**
* Class BookmarkDefaultFormatterTest
* @package Shaarli\Formatter
*/
class BookmarkDefaultFormatterTest extends TestCase
{
/** @var string Path of test config file */
protected static $testConf = 'sandbox/config';
/** @var BookmarkFormatter */
protected $formatter;
/** @var ConfigManager instance */
protected $conf;
/**
* Initialize formatter instance.
*/
protected function setUp(): void
{
copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
$this->conf = new ConfigManager(self::$testConf);
$this->formatter = new BookmarkDefaultFormatter($this->conf, true);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatFull()
{
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setShortUrl($short = 'abcdef');
$bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash');
$bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
$bookmark->setDescription($desc = '<h2>Content</h2><p>`Here is some content</p>');
$bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
$bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png');
$bookmark->setSticky(true);
$bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
$bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
$bookmark->setPrivate(true);
$link = $this->formatter->format($bookmark);
$this->assertEquals($id, $link['id']);
$this->assertEquals($short, $link['shorturl']);
$this->assertEquals('https://sub.domain.tld?query=here&for=real#hash', $link['url']);
$this->assertEquals(
'https://sub.domain.tld?query=here&for=real#hash',
$link['real_url']
);
$this->assertEquals('This is a <strong>bookmark</strong>', $link['title']);
$this->assertEquals(
'<h2>Content</h2><p>`Here is some content</p>',
$link['description']
);
$tags[3] = '<script>alert("xss");</script>';
$this->assertEquals($tags, $link['taglist']);
$this->assertEquals(implode(' ', $tags), $link['tags']);
$this->assertEquals(
'http://domain2.tdl2/?type=img&name=file.png',
$link['thumbnail']
);
$this->assertEquals($created, $link['created']);
$this->assertEquals($created->getTimestamp(), $link['timestamp']);
$this->assertEquals($updated, $link['updated']);
$this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
$this->assertTrue($link['private']);
$this->assertTrue($link['sticky']);
$this->assertEquals('private', $link['class']);
}
/**
* Test formatting a bookmark with all its attribute filled.
*/
public function testFormatMinimal()
{
$bookmark = new Bookmark();
$link = $this->formatter->format($bookmark);
$this->assertEmpty($link['id']);
$this->assertEmpty($link['shorturl']);
$this->assertEmpty($link['url']);
$this->assertEmpty($link['real_url']);
$this->assertEmpty($link['title']);
$this->assertEmpty($link['description']);
$this->assertEmpty($link['taglist']);
$this->assertEmpty($link['tags']);
$this->assertEmpty($link['thumbnail']);
$this->assertEmpty($link['created']);
$this->assertEmpty($link['timestamp']);
$this->assertEmpty($link['updated']);
$this->assertEmpty($link['updated_timestamp']);
$this->assertFalse($link['private']);
$this->assertFalse($link['sticky']);
$this->assertEmpty($link['class']);
}
/**
* Make sure that the description is properly formatted by the default formatter.
*/
public function testFormatDescription()
{
$description = [];
$description[] = 'This a <strong>description</strong>' . PHP_EOL;
$description[] = 'text https://sub.domain.tld?query=here&for=real#hash more text'. PHP_EOL;
$description[] = 'Also, there is an #hashtag added'. PHP_EOL;
$description[] = ' A N D KEEP SPACES ! '. PHP_EOL;
$bookmark = new Bookmark();
$bookmark->setDescription(implode('', $description));
$link = $this->formatter->format($bookmark);
$description[0] = 'This a <strong>description</strong><br />';
$url = 'https://sub.domain.tld?query=here&for=real#hash';
$description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />';
$description[2] = 'Also, there is an <a href="./add-tag/hashtag" '.
'title="Hashtag hashtag">#hashtag</a> added<br />';
$description[3] = ' A N D KEEP '.
'SPACES ! <br />';
$this->assertEquals(implode(PHP_EOL, $description) . PHP_EOL, $link['description']);
}
/**
* Test formatting URL with an index_url set
* It should prepend relative links.
*/
public function testFormatNoteWithIndexUrl()
{
$bookmark = new Bookmark();
$bookmark->setUrl($short = '?abcdef');
$description = 'Text #hashtag more text';
$bookmark->setDescription($description);
$this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
$link = $this->formatter->format($bookmark);
$this->assertEquals($root . $short, $link['url']);
$this->assertEquals($root . $short, $link['real_url']);
$this->assertEquals(
'Text <a href="'. $root .'./add-tag/hashtag" title="Hashtag hashtag">'.
'#hashtag</a> more text',
$link['description']
);
}
/**
* Make sure that private tags are properly filtered out when the user is logged out.
*/
public function testFormatTagListRemovePrivate(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setId($id = 11);
$bookmark->setTags($tags = ['bookmark', '.private', 'othertag']);
$link = $this->formatter->format($bookmark);
unset($tags[1]);
$tags = array_values($tags);
$this->assertSame(11, $link['id']);
$this->assertSame($tags, $link['taglist']);
$this->assertSame(implode(' ', $tags), $link['tags']);
}
/**
* Test formatTitleHtml with search result highlight.
*/
public function testFormatTitleHtmlWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setTitle('PSR-2: Coding Style Guide');
$bookmark->addAdditionalContentEntry(
'search_highlight',
['title' => [
['start' => 0, 'end' => 5], // "psr-2"
['start' => 7, 'end' => 13], // coding
['start' => 20, 'end' => 25], // guide
]]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
'<span class="search-highlight">PSR-2</span>: ' .
'<span class="search-highlight">Coding</span> Style ' .
'<span class="search-highlight">Guide</span>',
$link['title_html']
);
}
/**
* Test formatDescription with search result highlight.
*/
public function testFormatDescriptionWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setDescription(
'This guide extends and expands on PSR-1, the basic coding standard.' . PHP_EOL .
'https://www.php-fig.org/psr/psr-1/'
);
$bookmark->addAdditionalContentEntry(
'search_highlight',
['description' => [
['start' => 0, 'end' => 10], // "This guide"
['start' => 45, 'end' => 50], // basic
['start' => 58, 'end' => 67], // standard.
['start' => 84, 'end' => 87], // fig
]]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
'<span class="search-highlight">This guide</span> extends and expands on PSR-1, the ' .
'<span class="search-highlight">basic</span> coding ' .
'<span class="search-highlight">standard.</span><br />' . PHP_EOL .
'<a href="https://www.php-fig.org/psr/psr-1/">' .
'https://www.php-<span class="search-highlight">fig</span>.org/psr/psr-1/' .
'</a>',
$link['description']
);
}
/**
* Test formatUrlHtml with search result highlight.
*/
public function testFormatUrlHtmlWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setUrl('http://www.php-fig.org/psr/psr-2/');
$bookmark->addAdditionalContentEntry(
'search_highlight',
['url' => [
['start' => 0, 'end' => 4], // http
['start' => 15, 'end' => 18], // fig
['start' => 27, 'end' => 33], // "psr-2/"
]]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
'<span class="search-highlight">http</span>://www.php-' .
'<span class="search-highlight">fig</span>.org/psr/' .
'<span class="search-highlight">psr-2/</span>',
$link['url_html']
);
}
/**
* Test formatTagListHtml with search result highlight.
*/
public function testFormatTagListHtmlWithSearchHighlight(): void
{
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setTagsString('coding-style standards quality assurance');
$bookmark->addAdditionalContentEntry(
'search_highlight',
['tags' => [
['start' => 0, 'end' => 12], // coding-style
['start' => 23, 'end' => 30], // quality
['start' => 31, 'end' => 40], // assurance
],]
);
$link = $this->formatter->format($bookmark);
$this->assertSame(
[
'<span class="search-highlight">coding-style</span>',
'standards',
'<span class="search-highlight">quality</span>',
'<span class="search-highlight">assurance</span>',
],
$link['taglist_html']
);
}
/**
* Test default formatting with formatter_settings.autolink set to false:
* URLs and hashtags should not be transformed
*/
public function testFormatDescriptionWithoutLinkification(): void
{
$this->conf->set('formatter_settings.autolink', false);
$this->formatter = new BookmarkDefaultFormatter($this->conf, false);
$bookmark = new Bookmark();
$bookmark->setDescription('Hi!' . PHP_EOL . 'https://thisisaurl.tld #hashtag');
$link = $this->formatter->format($bookmark);
static::assertSame(
'Hi!<br />' . PHP_EOL . 'https://thisisaurl.tld #hashtag',
$link['description']
);
}
}