3 namespace Shaarli\Bookmark
;
5 use PHPUnit\Framework\TestCase
;
7 require_once 'tests/utils/CurlUtils.php';
10 * Class LinkUtilsTest.
12 class LinkUtilsTest
extends TestCase
15 * Test html_extract_title() when the title is found.
17 public function testHtmlExtractExistentTitle()
19 $title = 'Read me please.';
20 $html = '<html><meta>stuff</meta><title>' . $title . '</title></html>';
21 $this->assertEquals($title, html_extract_title($html));
22 $html = '<html><title>' . $title . '</title>blabla<title>another</title></html>';
23 $this->assertEquals($title, html_extract_title($html));
27 * Test html_extract_title() when the title is not found.
29 public function testHtmlExtractNonExistentTitle()
31 $html = '<html><meta>stuff</meta></html>';
32 $this->assertFalse(html_extract_title($html));
36 * Test headers_extract_charset() when the charset is found.
38 public function testHeadersExtractExistentCharset()
40 $charset = 'x-MacCroatian';
41 $headers = 'text/html; charset=' . $charset;
42 $this->assertEquals(strtolower($charset), header_extract_charset($headers));
46 * Test headers_extract_charset() when the charset is not found.
48 public function testHeadersExtractNonExistentCharset()
51 $this->assertFalse(header_extract_charset($headers));
53 $headers = 'text/html';
54 $this->assertFalse(header_extract_charset($headers));
58 * Test html_extract_charset() when the charset is found.
60 public function testHtmlExtractExistentCharset()
62 $charset = 'x-MacCroatian';
63 $html = '<html><meta>stuff2</meta><meta charset="' . $charset . '"/></html>';
64 $this->assertEquals(strtolower($charset), html_extract_charset($html));
68 * Test html_extract_charset() when the charset is not found.
70 public function testHtmlExtractNonExistentCharset()
72 $html = '<html><meta>stuff</meta></html>';
73 $this->assertFalse(html_extract_charset($html));
74 $html = '<html><meta>stuff</meta><meta charset=""/></html>';
75 $this->assertFalse(html_extract_charset($html));
79 * Test html_extract_tag() when the tag <meta name= is found.
81 public function testHtmlExtractExistentNameTag()
83 $description = 'Bob and Alice share cookies.';
86 $html = '<html><meta>stuff2</meta><meta name="description" content="' . $description . '"/></html>';
87 $this->assertEquals($description, html_extract_tag('description', $html));
90 $html = '<meta property="og:description" content="' . $description . '">';
91 $this->assertEquals($description, html_extract_tag('description', $html));
93 // Simple reversed OpenGraph
94 $html = '<meta content="' . $description . '" property="og:description">';
95 $this->assertEquals($description, html_extract_tag('description', $html));
98 $html = '<meta itemprop="og:description" content="' . $description . '">';
99 $this->assertEquals($description, html_extract_tag('description', $html));
101 // OpenGraph without quotes
102 $html = '<meta property=og:description content="' . $description . '">';
103 $this->assertEquals($description, html_extract_tag('description', $html));
105 // OpenGraph reversed without quotes
106 $html = '<meta content="' . $description . '" property=og:description>';
107 $this->assertEquals($description, html_extract_tag('description', $html));
109 // OpenGraph with noise
110 $html = '<meta tag1="content1" property="og:description" tag2="content2" content="' .
111 $description . '" tag3="content3">';
112 $this->assertEquals($description, html_extract_tag('description', $html));
114 // OpenGraph reversed with noise
115 $html = '<meta tag1="content1" content="' . $description . '" ' .
116 'tag3="content3" tag2="content2" property="og:description">';
117 $this->assertEquals($description, html_extract_tag('description', $html));
119 // OpenGraph multiple properties start
120 $html = '<meta property="unrelated og:description" content="' . $description . '">';
121 $this->assertEquals($description, html_extract_tag('description', $html));
123 // OpenGraph multiple properties end
124 $html = '<meta property="og:description unrelated" content="' . $description . '">';
125 $this->assertEquals($description, html_extract_tag('description', $html));
127 // OpenGraph multiple properties both end
128 $html = '<meta property="og:unrelated1 og:description og:unrelated2" content="' . $description . '">';
129 $this->assertEquals($description, html_extract_tag('description', $html));
131 // OpenGraph multiple properties both end with noise
132 $html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" '.
133 'tag2="content2" content="' . $description . '" tag3="content3">';
134 $this->assertEquals($description, html_extract_tag('description', $html));
136 // OpenGraph reversed multiple properties start
137 $html = '<meta content="' . $description . '" property="unrelated og:description">';
138 $this->assertEquals($description, html_extract_tag('description', $html));
140 // OpenGraph reversed multiple properties end
141 $html = '<meta content="' . $description . '" property="og:description unrelated">';
142 $this->assertEquals($description, html_extract_tag('description', $html));
144 // OpenGraph reversed multiple properties both end
145 $html = '<meta content="' . $description . '" property="og:unrelated1 og:description og:unrelated2">';
146 $this->assertEquals($description, html_extract_tag('description', $html));
148 // OpenGraph reversed multiple properties both end with noise
149 $html = '<meta tag1="content1" content="' . $description . '" tag2="content2" '.
150 'property="og:unrelated1 og:description og:unrelated2" tag3="content3">';
151 $this->assertEquals($description, html_extract_tag('description', $html));
153 // Suggestion from #1375
154 $html = '<meta property="og:description" name="description" content="' . $description . '">';
155 $this->assertEquals($description, html_extract_tag('description', $html));
159 * Test html_extract_tag() when the tag <meta name= is not found.
161 public function testHtmlExtractNonExistentNameTag()
163 $html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>';
164 $this->assertFalse(html_extract_tag('description', $html));
167 $html = '<meta content="Brief description">';
168 $this->assertFalse(html_extract_tag('description', $html));
170 $html = '<meta property="og:description">';
171 $this->assertFalse(html_extract_tag('description', $html));
173 $html = '<meta tag1="content1" property="og:description">';
174 $this->assertFalse(html_extract_tag('description', $html));
176 $html = '<meta property="og:description" tag1="content1">';
177 $this->assertFalse(html_extract_tag('description', $html));
179 $html = '<meta tag1="content1" content="Brief description">';
180 $this->assertFalse(html_extract_tag('description', $html));
182 $html = '<meta content="Brief description" tag1="content1">';
183 $this->assertFalse(html_extract_tag('description', $html));
187 * Test html_extract_tag() when the tag <meta property="og: is found.
189 public function testHtmlExtractExistentOgTag()
191 $description = 'Bob and Alice share cookies.';
192 $html = '<html><meta>stuff2</meta><meta property="og:description" content="' . $description . '"/></html>';
193 $this->assertEquals($description, html_extract_tag('description', $html));
197 * Test html_extract_tag() when the tag <meta property="og: is not found.
199 public function testHtmlExtractNonExistentOgTag()
201 $html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>';
202 $this->assertFalse(html_extract_tag('description', $html));
206 * Test the download callback with valid value
208 public function testCurlDownloadCallbackOk()
210 $callback = get_curl_download_callback(
220 'Server: GitHub.com',
221 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
222 'Content-Type: text/html; charset=utf-8',
224 'end' => 'th=device-width">'
225 . '<title>Refactoring · GitHub</title>'
226 . '<link rel="search" type="application/opensea',
227 '<title>ignored</title>'
228 . '<meta name="description" content="desc" />'
229 . '<meta name="keywords" content="key1,key2" />',
231 foreach ($data as $key => $line) {
233 $expected = $key !== 'end' ? strlen($line) : false;
234 $this->assertEquals($expected, $callback($ignore, $line));
235 if ($expected === false) {
239 $this->assertEquals('utf-8', $charset);
240 $this->assertEquals('Refactoring · GitHub', $title);
241 $this->assertEmpty($desc);
242 $this->assertEmpty($keywords);
246 * Test the download callback with valid values and no charset
248 public function testCurlDownloadCallbackOkNoCharset()
250 $callback = get_curl_download_callback(
256 'ut_curl_getinfo_no_charset'
260 'end' => 'th=device-width">'
261 . '<title>Refactoring · GitHub</title>'
262 . '<link rel="search" type="application/opensea',
263 '<title>ignored</title>'
264 . '<meta name="description" content="desc" />'
265 . '<meta name="keywords" content="key1,key2" />',
267 foreach ($data as $key => $line) {
269 $this->assertEquals(strlen($line), $callback($ignore, $line));
271 $this->assertEmpty($charset);
272 $this->assertEquals('Refactoring · GitHub', $title);
273 $this->assertEmpty($desc);
274 $this->assertEmpty($keywords);
278 * Test the download callback with valid values and no charset
280 public function testCurlDownloadCallbackOkHtmlCharset()
282 $callback = get_curl_download_callback(
288 'ut_curl_getinfo_no_charset'
292 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
293 'end' => 'th=device-width">'
294 . '<title>Refactoring · GitHub</title>'
295 . '<link rel="search" type="application/opensea',
296 '<title>ignored</title>'
297 . '<meta name="description" content="desc" />'
298 . '<meta name="keywords" content="key1,key2" />',
300 foreach ($data as $key => $line) {
302 $expected = $key !== 'end' ? strlen($line) : false;
303 $this->assertEquals($expected, $callback($ignore, $line));
304 if ($expected === false) {
308 $this->assertEquals('utf-8', $charset);
309 $this->assertEquals('Refactoring · GitHub', $title);
310 $this->assertEmpty($desc);
311 $this->assertEmpty($keywords);
315 * Test the download callback with valid values and no title
317 public function testCurlDownloadCallbackOkNoTitle()
319 $callback = get_curl_download_callback(
329 'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea',
332 foreach ($data as $key => $line) {
334 $this->assertEquals(strlen($line), $callback($ignore, $line));
336 $this->assertEquals('utf-8', $charset);
337 $this->assertEmpty($title);
338 $this->assertEmpty($desc);
339 $this->assertEmpty($keywords);
343 * Test the download callback with an invalid content type.
345 public function testCurlDownloadCallbackInvalidContentType()
347 $callback = get_curl_download_callback(
353 'ut_curl_getinfo_ct_ko'
356 $this->assertFalse($callback($ignore, ''));
357 $this->assertEmpty($charset);
358 $this->assertEmpty($title);
362 * Test the download callback with an invalid response code.
364 public function testCurlDownloadCallbackInvalidResponseCode()
366 $callback = $callback = get_curl_download_callback(
372 'ut_curl_getinfo_rc_ko'
375 $this->assertFalse($callback($ignore, ''));
376 $this->assertEmpty($charset);
377 $this->assertEmpty($title);
381 * Test the download callback with an invalid content type and response code.
383 public function testCurlDownloadCallbackInvalidContentTypeAndResponseCode()
385 $callback = $callback = get_curl_download_callback(
391 'ut_curl_getinfo_rs_ct_ko'
394 $this->assertFalse($callback($ignore, ''));
395 $this->assertEmpty($charset);
396 $this->assertEmpty($title);
400 * Test the download callback with valid value, and retrieve_description option enabled.
402 public function testCurlDownloadCallbackOkWithDesc()
404 $callback = get_curl_download_callback(
414 'Server: GitHub.com',
415 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
416 'Content-Type: text/html; charset=utf-8',
419 . '<title>Refactoring · GitHub</title>'
420 . '<link rel="search" type="application/opensea',
421 'end' => '<title>ignored</title>'
422 . '<meta name="description" content="link desc" />'
423 . '<meta name="keywords" content="key1,key2" />',
425 foreach ($data as $key => $line) {
427 $expected = $key !== 'end' ? strlen($line) : false;
428 $this->assertEquals($expected, $callback($ignore, $line));
429 if ($expected === false) {
433 $this->assertEquals('utf-8', $charset);
434 $this->assertEquals('Refactoring · GitHub', $title);
435 $this->assertEquals('link desc', $desc);
436 $this->assertEquals('key1 key2', $keywords);
440 * Test the download callback with valid value, and retrieve_description option enabled,
441 * but no desc or keyword defined in the page.
443 public function testCurlDownloadCallbackOkWithDescNotFound()
445 $callback = get_curl_download_callback(
455 'Server: GitHub.com',
456 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
457 'Content-Type: text/html; charset=utf-8',
460 . '<title>Refactoring · GitHub</title>'
461 . '<link rel="search" type="application/opensea',
462 'end' => '<title>ignored</title>',
464 foreach ($data as $key => $line) {
466 $expected = $key !== 'end' ? strlen($line) : false;
467 $this->assertEquals($expected, $callback($ignore, $line));
468 if ($expected === false) {
472 $this->assertEquals('utf-8', $charset);
473 $this->assertEquals('Refactoring · GitHub', $title);
474 $this->assertEmpty($desc);
475 $this->assertEmpty($keywords);
479 * Test text2clickable.
481 public function testText2clickable()
483 $text = 'stuff http://hello.there/is=someone#here otherstuff';
484 $expectedText = 'stuff <a href="http://hello.there/is=someone#here">'
485 . 'http://hello.there/is=someone#here</a> otherstuff';
486 $processedText = text2clickable($text);
487 $this->assertEquals($expectedText, $processedText);
489 $text = 'stuff http://hello.there/is=someone#here(please) otherstuff';
490 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">'
491 . 'http://hello.there/is=someone#here(please)</a> otherstuff';
492 $processedText = text2clickable($text);
493 $this->assertEquals($expectedText, $processedText);
495 $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
496 $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
497 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">'
498 . 'http://hello.there/is=someone#here(please)&no</a> otherstuff';
499 $processedText = text2clickable($text);
500 $this->assertEquals($expectedText, $processedText);
504 * Test testSpace2nbsp.
506 public function testSpace2nbsp()
508 $text = ' Are you thrilled by flags ?' . PHP_EOL
. ' Really?';
509 $expectedText = ' Are you thrilled by flags ?' . PHP_EOL
. ' Really?';
510 $processedText = space2nbsp($text);
511 $this->assertEquals($expectedText, $processedText);
515 * Test hashtags auto-link.
517 public function testHashtagAutolink()
519 $index = 'http://domain.tld/';
520 $rawDescription = '#hashtag\n
522 test#nothashtag #hashtag \#nothashtag\n
523 test #hashtag #hashtag test #hashtag.test\n
524 #hashtag #hashtag-nothashtag #hashtag_hashtag\n
525 What is #ашок anyway?\n
527 $autolinkedDescription = hashtag_autolink($rawDescription, $index);
529 $this->assertContains($this->getHashtagLink('hashtag', $index), $autolinkedDescription);
530 $this->assertNotContains(' #hashtag', $autolinkedDescription);
531 $this->assertNotContains('>#nothashtag', $autolinkedDescription);
532 $this->assertContains($this->getHashtagLink('ашок', $index), $autolinkedDescription);
533 $this->assertContains($this->getHashtagLink('カタカナ', $index), $autolinkedDescription);
534 $this->assertContains($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription);
535 $this->assertNotContains($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription);
539 * Test hashtags auto-link without index URL.
541 public function testHashtagAutolinkNoIndex()
543 $rawDescription = 'blabla #hashtag x#nothashtag';
544 $autolinkedDescription = hashtag_autolink($rawDescription);
546 $this->assertContains($this->getHashtagLink('hashtag'), $autolinkedDescription);
547 $this->assertNotContains(' #hashtag', $autolinkedDescription);
548 $this->assertNotContains('>#nothashtag', $autolinkedDescription);
552 * Test is_note with note URLs.
554 public function testIsNote()
556 $this->assertTrue(is_note('?'));
557 $this->assertTrue(is_note('?abcDEf'));
558 $this->assertTrue(is_note('?_abcDEf#123'));
562 * Test is_note with non note URLs.
564 public function testIsNotNote()
566 $this->assertFalse(is_note(''));
567 $this->assertFalse(is_note('nope'));
568 $this->assertFalse(is_note('https://github.com/shaarli/Shaarli/?hi'));
572 * Util function to build an hashtag link.
574 * @param string $hashtag Hashtag name.
575 * @param string $index Index URL.
577 * @return string HTML hashtag link.
579 private function getHashtagLink($hashtag, $index = '')
581 $hashtagLink = '<a href="' . $index . './add-tag/$1" title="Hashtag $1">#$1</a>';
582 return str_replace('$1', $hashtag, $hashtagLink);