diff options
Diffstat (limited to 'tests/bookmark')
-rw-r--r-- | tests/bookmark/LinkUtilsTest.php | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php new file mode 100644 index 00000000..1b8688e6 --- /dev/null +++ b/tests/bookmark/LinkUtilsTest.php | |||
@@ -0,0 +1,333 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli\Bookmark; | ||
4 | |||
5 | use ReferenceLinkDB; | ||
6 | |||
7 | require_once 'tests/utils/CurlUtils.php'; | ||
8 | |||
9 | /** | ||
10 | * Class LinkUtilsTest. | ||
11 | */ | ||
12 | class LinkUtilsTest extends \PHPUnit\Framework\TestCase | ||
13 | { | ||
14 | /** | ||
15 | * Test html_extract_title() when the title is found. | ||
16 | */ | ||
17 | public function testHtmlExtractExistentTitle() | ||
18 | { | ||
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)); | ||
24 | } | ||
25 | |||
26 | /** | ||
27 | * Test html_extract_title() when the title is not found. | ||
28 | */ | ||
29 | public function testHtmlExtractNonExistentTitle() | ||
30 | { | ||
31 | $html = '<html><meta>stuff</meta></html>'; | ||
32 | $this->assertFalse(html_extract_title($html)); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * Test headers_extract_charset() when the charset is found. | ||
37 | */ | ||
38 | public function testHeadersExtractExistentCharset() | ||
39 | { | ||
40 | $charset = 'x-MacCroatian'; | ||
41 | $headers = 'text/html; charset=' . $charset; | ||
42 | $this->assertEquals(strtolower($charset), header_extract_charset($headers)); | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * Test headers_extract_charset() when the charset is not found. | ||
47 | */ | ||
48 | public function testHeadersExtractNonExistentCharset() | ||
49 | { | ||
50 | $headers = ''; | ||
51 | $this->assertFalse(header_extract_charset($headers)); | ||
52 | |||
53 | $headers = 'text/html'; | ||
54 | $this->assertFalse(header_extract_charset($headers)); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Test html_extract_charset() when the charset is found. | ||
59 | */ | ||
60 | public function testHtmlExtractExistentCharset() | ||
61 | { | ||
62 | $charset = 'x-MacCroatian'; | ||
63 | $html = '<html><meta>stuff2</meta><meta charset="' . $charset . '"/></html>'; | ||
64 | $this->assertEquals(strtolower($charset), html_extract_charset($html)); | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * Test html_extract_charset() when the charset is not found. | ||
69 | */ | ||
70 | public function testHtmlExtractNonExistentCharset() | ||
71 | { | ||
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)); | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * Test the download callback with valid value | ||
80 | */ | ||
81 | public function testCurlDownloadCallbackOk() | ||
82 | { | ||
83 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_ok'); | ||
84 | $data = [ | ||
85 | 'HTTP/1.1 200 OK', | ||
86 | 'Server: GitHub.com', | ||
87 | 'Date: Sat, 28 Oct 2017 12:01:33 GMT', | ||
88 | 'Content-Type: text/html; charset=utf-8', | ||
89 | 'Status: 200 OK', | ||
90 | 'end' => 'th=device-width">' | ||
91 | . '<title>Refactoring · GitHub</title>' | ||
92 | . '<link rel="search" type="application/opensea', | ||
93 | '<title>ignored</title>', | ||
94 | ]; | ||
95 | foreach ($data as $key => $line) { | ||
96 | $ignore = null; | ||
97 | $expected = $key !== 'end' ? strlen($line) : false; | ||
98 | $this->assertEquals($expected, $callback($ignore, $line)); | ||
99 | if ($expected === false) { | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | $this->assertEquals('utf-8', $charset); | ||
104 | $this->assertEquals('Refactoring · GitHub', $title); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * Test the download callback with valid values and no charset | ||
109 | */ | ||
110 | public function testCurlDownloadCallbackOkNoCharset() | ||
111 | { | ||
112 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_no_charset'); | ||
113 | $data = [ | ||
114 | 'HTTP/1.1 200 OK', | ||
115 | 'end' => 'th=device-width">' | ||
116 | . '<title>Refactoring · GitHub</title>' | ||
117 | . '<link rel="search" type="application/opensea', | ||
118 | '<title>ignored</title>', | ||
119 | ]; | ||
120 | foreach ($data as $key => $line) { | ||
121 | $ignore = null; | ||
122 | $this->assertEquals(strlen($line), $callback($ignore, $line)); | ||
123 | } | ||
124 | $this->assertEmpty($charset); | ||
125 | $this->assertEquals('Refactoring · GitHub', $title); | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * Test the download callback with valid values and no charset | ||
130 | */ | ||
131 | public function testCurlDownloadCallbackOkHtmlCharset() | ||
132 | { | ||
133 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_no_charset'); | ||
134 | $data = [ | ||
135 | 'HTTP/1.1 200 OK', | ||
136 | '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', | ||
137 | 'end' => 'th=device-width">' | ||
138 | . '<title>Refactoring · GitHub</title>' | ||
139 | . '<link rel="search" type="application/opensea', | ||
140 | '<title>ignored</title>', | ||
141 | ]; | ||
142 | foreach ($data as $key => $line) { | ||
143 | $ignore = null; | ||
144 | $expected = $key !== 'end' ? strlen($line) : false; | ||
145 | $this->assertEquals($expected, $callback($ignore, $line)); | ||
146 | if ($expected === false) { | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | $this->assertEquals('utf-8', $charset); | ||
151 | $this->assertEquals('Refactoring · GitHub', $title); | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * Test the download callback with valid values and no title | ||
156 | */ | ||
157 | public function testCurlDownloadCallbackOkNoTitle() | ||
158 | { | ||
159 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_ok'); | ||
160 | $data = [ | ||
161 | 'HTTP/1.1 200 OK', | ||
162 | 'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea', | ||
163 | 'ignored', | ||
164 | ]; | ||
165 | foreach ($data as $key => $line) { | ||
166 | $ignore = null; | ||
167 | $this->assertEquals(strlen($line), $callback($ignore, $line)); | ||
168 | } | ||
169 | $this->assertEquals('utf-8', $charset); | ||
170 | $this->assertEmpty($title); | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * Test the download callback with an invalid content type. | ||
175 | */ | ||
176 | public function testCurlDownloadCallbackInvalidContentType() | ||
177 | { | ||
178 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_ct_ko'); | ||
179 | $ignore = null; | ||
180 | $this->assertFalse($callback($ignore, '')); | ||
181 | $this->assertEmpty($charset); | ||
182 | $this->assertEmpty($title); | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * Test the download callback with an invalid response code. | ||
187 | */ | ||
188 | public function testCurlDownloadCallbackInvalidResponseCode() | ||
189 | { | ||
190 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_rc_ko'); | ||
191 | $ignore = null; | ||
192 | $this->assertFalse($callback($ignore, '')); | ||
193 | $this->assertEmpty($charset); | ||
194 | $this->assertEmpty($title); | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Test the download callback with an invalid content type and response code. | ||
199 | */ | ||
200 | public function testCurlDownloadCallbackInvalidContentTypeAndResponseCode() | ||
201 | { | ||
202 | $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_rs_ct_ko'); | ||
203 | $ignore = null; | ||
204 | $this->assertFalse($callback($ignore, '')); | ||
205 | $this->assertEmpty($charset); | ||
206 | $this->assertEmpty($title); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * Test count_private. | ||
211 | */ | ||
212 | public function testCountPrivateLinks() | ||
213 | { | ||
214 | $refDB = new ReferenceLinkDB(); | ||
215 | $this->assertEquals($refDB->countPrivateLinks(), count_private($refDB->getLinks())); | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * Test text2clickable without a redirector being set. | ||
220 | */ | ||
221 | public function testText2clickableWithoutRedirector() | ||
222 | { | ||
223 | $text = 'stuff http://hello.there/is=someone#here otherstuff'; | ||
224 | $expectedText = 'stuff <a href="http://hello.there/is=someone#here">' | ||
225 | . 'http://hello.there/is=someone#here</a> otherstuff'; | ||
226 | $processedText = text2clickable($text, ''); | ||
227 | $this->assertEquals($expectedText, $processedText); | ||
228 | |||
229 | $text = 'stuff http://hello.there/is=someone#here(please) otherstuff'; | ||
230 | $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">' | ||
231 | . 'http://hello.there/is=someone#here(please)</a> otherstuff'; | ||
232 | $processedText = text2clickable($text, ''); | ||
233 | $this->assertEquals($expectedText, $processedText); | ||
234 | |||
235 | $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff'; | ||
236 | $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">' | ||
237 | . 'http://hello.there/is=someone#here(please)&no</a> otherstuff'; | ||
238 | $processedText = text2clickable($text, ''); | ||
239 | $this->assertEquals($expectedText, $processedText); | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * Test text2clickable with a redirector set. | ||
244 | */ | ||
245 | public function testText2clickableWithRedirector() | ||
246 | { | ||
247 | $text = 'stuff http://hello.there/is=someone#here otherstuff'; | ||
248 | $redirector = 'http://redirector.to'; | ||
249 | $expectedText = 'stuff <a href="' . | ||
250 | $redirector . | ||
251 | urlencode('http://hello.there/is=someone#here') . | ||
252 | '">http://hello.there/is=someone#here</a> otherstuff'; | ||
253 | $processedText = text2clickable($text, $redirector); | ||
254 | $this->assertEquals($expectedText, $processedText); | ||
255 | } | ||
256 | |||
257 | /** | ||
258 | * Test text2clickable a redirector set and without URL encode. | ||
259 | */ | ||
260 | public function testText2clickableWithRedirectorDontEncode() | ||
261 | { | ||
262 | $text = 'stuff http://hello.there/?is=someone&or=something#here otherstuff'; | ||
263 | $redirector = 'http://redirector.to'; | ||
264 | $expectedText = 'stuff <a href="' . | ||
265 | $redirector . | ||
266 | 'http://hello.there/?is=someone&or=something#here' . | ||
267 | '">http://hello.there/?is=someone&or=something#here</a> otherstuff'; | ||
268 | $processedText = text2clickable($text, $redirector, false); | ||
269 | $this->assertEquals($expectedText, $processedText); | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * Test testSpace2nbsp. | ||
274 | */ | ||
275 | public function testSpace2nbsp() | ||
276 | { | ||
277 | $text = ' Are you thrilled by flags ?' . PHP_EOL . ' Really?'; | ||
278 | $expectedText = ' Are you thrilled by flags ?' . PHP_EOL . ' Really?'; | ||
279 | $processedText = space2nbsp($text); | ||
280 | $this->assertEquals($expectedText, $processedText); | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * Test hashtags auto-link. | ||
285 | */ | ||
286 | public function testHashtagAutolink() | ||
287 | { | ||
288 | $index = 'http://domain.tld/'; | ||
289 | $rawDescription = '#hashtag\n | ||
290 | # nothashtag\n | ||
291 | test#nothashtag #hashtag \#nothashtag\n | ||
292 | test #hashtag #hashtag test #hashtag.test\n | ||
293 | #hashtag #hashtag-nothashtag #hashtag_hashtag\n | ||
294 | What is #ашок anyway?\n | ||
295 | カタカナ #カタカナ」カタカナ\n'; | ||
296 | $autolinkedDescription = hashtag_autolink($rawDescription, $index); | ||
297 | |||
298 | $this->assertContains($this->getHashtagLink('hashtag', $index), $autolinkedDescription); | ||
299 | $this->assertNotContains(' #hashtag', $autolinkedDescription); | ||
300 | $this->assertNotContains('>#nothashtag', $autolinkedDescription); | ||
301 | $this->assertContains($this->getHashtagLink('ашок', $index), $autolinkedDescription); | ||
302 | $this->assertContains($this->getHashtagLink('カタカナ', $index), $autolinkedDescription); | ||
303 | $this->assertContains($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription); | ||
304 | $this->assertNotContains($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription); | ||
305 | } | ||
306 | |||
307 | /** | ||
308 | * Test hashtags auto-link without index URL. | ||
309 | */ | ||
310 | public function testHashtagAutolinkNoIndex() | ||
311 | { | ||
312 | $rawDescription = 'blabla #hashtag x#nothashtag'; | ||
313 | $autolinkedDescription = hashtag_autolink($rawDescription); | ||
314 | |||
315 | $this->assertContains($this->getHashtagLink('hashtag'), $autolinkedDescription); | ||
316 | $this->assertNotContains(' #hashtag', $autolinkedDescription); | ||
317 | $this->assertNotContains('>#nothashtag', $autolinkedDescription); | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * Util function to build an hashtag link. | ||
322 | * | ||
323 | * @param string $hashtag Hashtag name. | ||
324 | * @param string $index Index URL. | ||
325 | * | ||
326 | * @return string HTML hashtag link. | ||
327 | */ | ||
328 | private function getHashtagLink($hashtag, $index = '') | ||
329 | { | ||
330 | $hashtagLink = '<a href="' . $index . '?addtag=$1" title="Hashtag $1">#$1</a>'; | ||
331 | return str_replace('$1', $hashtag, $hashtagLink); | ||
332 | } | ||
333 | } | ||