aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/front/controller/visitor
diff options
context:
space:
mode:
Diffstat (limited to 'tests/front/controller/visitor')
-rw-r--r--tests/front/controller/visitor/DailyControllerTest.php497
-rw-r--r--tests/front/controller/visitor/FeedControllerTest.php151
-rw-r--r--tests/front/controller/visitor/FrontControllerMockHelper.php114
-rw-r--r--tests/front/controller/visitor/LoginControllerTest.php144
-rw-r--r--tests/front/controller/visitor/OpenSearchControllerTest.php46
-rw-r--r--tests/front/controller/visitor/PictureWallControllerTest.php125
-rw-r--r--tests/front/controller/visitor/ShaarliPublicControllerTest.php239
-rw-r--r--tests/front/controller/visitor/TagCloudControllerTest.php381
-rw-r--r--tests/front/controller/visitor/TagControllerTest.php241
9 files changed, 1938 insertions, 0 deletions
diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php
new file mode 100644
index 00000000..6ff769fc
--- /dev/null
+++ b/tests/front/controller/visitor/DailyControllerTest.php
@@ -0,0 +1,497 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Feed\CachedPage;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13class DailyControllerTest extends TestCase
14{
15 use FrontControllerMockHelper;
16
17 /** @var DailyController */
18 protected $controller;
19
20 public function setUp(): void
21 {
22 $this->createContainer();
23
24 $this->controller = new DailyController($this->container);
25 DailyController::$DAILY_RSS_NB_DAYS = 2;
26 }
27
28 public function testValidIndexControllerInvokeDefault(): void
29 {
30 $this->createValidContainerMockSet();
31
32 $currentDay = new \DateTimeImmutable('2020-05-13');
33
34 $request = $this->createMock(Request::class);
35 $request->method('getQueryParam')->willReturn($currentDay->format('Ymd'));
36 $response = new Response();
37
38 // Save RainTPL assigned variables
39 $assignedVariables = [];
40 $this->assignTemplateVars($assignedVariables);
41
42 // Links dataset: 2 links with thumbnails
43 $this->container->bookmarkService
44 ->expects(static::once())
45 ->method('days')
46 ->willReturnCallback(function () use ($currentDay): array {
47 return [
48 '20200510',
49 $currentDay->format('Ymd'),
50 '20200516',
51 ];
52 })
53 ;
54 $this->container->bookmarkService
55 ->expects(static::once())
56 ->method('filterDay')
57 ->willReturnCallback(function (): array {
58 return [
59 (new Bookmark())
60 ->setId(1)
61 ->setUrl('http://url.tld')
62 ->setTitle(static::generateContent(50))
63 ->setDescription(static::generateContent(500))
64 ,
65 (new Bookmark())
66 ->setId(2)
67 ->setUrl('http://url2.tld')
68 ->setTitle(static::generateContent(50))
69 ->setDescription(static::generateContent(500))
70 ,
71 (new Bookmark())
72 ->setId(3)
73 ->setUrl('http://url3.tld')
74 ->setTitle(static::generateContent(50))
75 ->setDescription(static::generateContent(500))
76 ,
77 ];
78 })
79 ;
80
81 // Make sure that PluginManager hook is triggered
82 $this->container->pluginManager
83 ->expects(static::at(0))
84 ->method('executeHooks')
85 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
86 static::assertSame('render_daily', $hook);
87
88 static::assertArrayHasKey('linksToDisplay', $data);
89 static::assertCount(3, $data['linksToDisplay']);
90 static::assertSame(1, $data['linksToDisplay'][0]['id']);
91 static::assertSame($currentDay->getTimestamp(), $data['day']);
92 static::assertSame('20200510', $data['previousday']);
93 static::assertSame('20200516', $data['nextday']);
94
95 static::assertArrayHasKey('loggedin', $param);
96
97 return $data;
98 })
99 ;
100
101 $result = $this->controller->index($request, $response);
102
103 static::assertSame(200, $result->getStatusCode());
104 static::assertSame('daily', (string) $result->getBody());
105 static::assertSame(
106 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
107 $assignedVariables['pagetitle']
108 );
109 static::assertEquals($currentDay, $assignedVariables['dayDate']);
110 static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']);
111 static::assertCount(3, $assignedVariables['linksToDisplay']);
112
113 $link = $assignedVariables['linksToDisplay'][0];
114
115 static::assertSame(1, $link['id']);
116 static::assertSame('http://url.tld', $link['url']);
117 static::assertNotEmpty($link['title']);
118 static::assertNotEmpty($link['description']);
119 static::assertNotEmpty($link['formatedDescription']);
120
121 $link = $assignedVariables['linksToDisplay'][1];
122
123 static::assertSame(2, $link['id']);
124 static::assertSame('http://url2.tld', $link['url']);
125 static::assertNotEmpty($link['title']);
126 static::assertNotEmpty($link['description']);
127 static::assertNotEmpty($link['formatedDescription']);
128
129 $link = $assignedVariables['linksToDisplay'][2];
130
131 static::assertSame(3, $link['id']);
132 static::assertSame('http://url3.tld', $link['url']);
133 static::assertNotEmpty($link['title']);
134 static::assertNotEmpty($link['description']);
135 static::assertNotEmpty($link['formatedDescription']);
136
137 static::assertCount(3, $assignedVariables['cols']);
138 static::assertCount(1, $assignedVariables['cols'][0]);
139 static::assertCount(1, $assignedVariables['cols'][1]);
140 static::assertCount(1, $assignedVariables['cols'][2]);
141
142 $link = $assignedVariables['cols'][0][0];
143
144 static::assertSame(1, $link['id']);
145 static::assertSame('http://url.tld', $link['url']);
146 static::assertNotEmpty($link['title']);
147 static::assertNotEmpty($link['description']);
148 static::assertNotEmpty($link['formatedDescription']);
149
150 $link = $assignedVariables['cols'][1][0];
151
152 static::assertSame(2, $link['id']);
153 static::assertSame('http://url2.tld', $link['url']);
154 static::assertNotEmpty($link['title']);
155 static::assertNotEmpty($link['description']);
156 static::assertNotEmpty($link['formatedDescription']);
157
158 $link = $assignedVariables['cols'][2][0];
159
160 static::assertSame(3, $link['id']);
161 static::assertSame('http://url3.tld', $link['url']);
162 static::assertNotEmpty($link['title']);
163 static::assertNotEmpty($link['description']);
164 static::assertNotEmpty($link['formatedDescription']);
165 }
166
167 /**
168 * Daily page - test that everything goes fine with no future or past bookmarks
169 */
170 public function testValidIndexControllerInvokeNoFutureOrPast(): void
171 {
172 $this->createValidContainerMockSet();
173
174 $currentDay = new \DateTimeImmutable('2020-05-13');
175
176 $request = $this->createMock(Request::class);
177 $response = new Response();
178
179 // Save RainTPL assigned variables
180 $assignedVariables = [];
181 $this->assignTemplateVars($assignedVariables);
182
183 // Links dataset: 2 links with thumbnails
184 $this->container->bookmarkService
185 ->expects(static::once())
186 ->method('days')
187 ->willReturnCallback(function () use ($currentDay): array {
188 return [
189 $currentDay->format($currentDay->format('Ymd')),
190 ];
191 })
192 ;
193 $this->container->bookmarkService
194 ->expects(static::once())
195 ->method('filterDay')
196 ->willReturnCallback(function (): array {
197 return [
198 (new Bookmark())
199 ->setId(1)
200 ->setUrl('http://url.tld')
201 ->setTitle(static::generateContent(50))
202 ->setDescription(static::generateContent(500))
203 ,
204 ];
205 })
206 ;
207
208 // Make sure that PluginManager hook is triggered
209 $this->container->pluginManager
210 ->expects(static::at(0))
211 ->method('executeHooks')
212 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
213 static::assertSame('render_daily', $hook);
214
215 static::assertArrayHasKey('linksToDisplay', $data);
216 static::assertCount(1, $data['linksToDisplay']);
217 static::assertSame(1, $data['linksToDisplay'][0]['id']);
218 static::assertSame($currentDay->getTimestamp(), $data['day']);
219 static::assertEmpty($data['previousday']);
220 static::assertEmpty($data['nextday']);
221
222 static::assertArrayHasKey('loggedin', $param);
223
224 return $data;
225 });
226
227 $result = $this->controller->index($request, $response);
228
229 static::assertSame(200, $result->getStatusCode());
230 static::assertSame('daily', (string) $result->getBody());
231 static::assertSame(
232 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
233 $assignedVariables['pagetitle']
234 );
235 static::assertCount(1, $assignedVariables['linksToDisplay']);
236
237 $link = $assignedVariables['linksToDisplay'][0];
238 static::assertSame(1, $link['id']);
239 }
240
241 /**
242 * Daily page - test that height adjustment in columns is working
243 */
244 public function testValidIndexControllerInvokeHeightAdjustment(): void
245 {
246 $this->createValidContainerMockSet();
247
248 $currentDay = new \DateTimeImmutable('2020-05-13');
249
250 $request = $this->createMock(Request::class);
251 $response = new Response();
252
253 // Save RainTPL assigned variables
254 $assignedVariables = [];
255 $this->assignTemplateVars($assignedVariables);
256
257 // Links dataset: 2 links with thumbnails
258 $this->container->bookmarkService
259 ->expects(static::once())
260 ->method('days')
261 ->willReturnCallback(function () use ($currentDay): array {
262 return [
263 $currentDay->format($currentDay->format('Ymd')),
264 ];
265 })
266 ;
267 $this->container->bookmarkService
268 ->expects(static::once())
269 ->method('filterDay')
270 ->willReturnCallback(function (): array {
271 return [
272 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'),
273 (new Bookmark())
274 ->setId(2)
275 ->setUrl('http://url.tld')
276 ->setTitle(static::generateContent(50))
277 ->setDescription(static::generateContent(5000))
278 ,
279 (new Bookmark())->setId(3)->setUrl('http://url.tld')->setTitle('title'),
280 (new Bookmark())->setId(4)->setUrl('http://url.tld')->setTitle('title'),
281 (new Bookmark())->setId(5)->setUrl('http://url.tld')->setTitle('title'),
282 (new Bookmark())->setId(6)->setUrl('http://url.tld')->setTitle('title'),
283 (new Bookmark())->setId(7)->setUrl('http://url.tld')->setTitle('title'),
284 ];
285 })
286 ;
287
288 // Make sure that PluginManager hook is triggered
289 $this->container->pluginManager
290 ->expects(static::at(0))
291 ->method('executeHooks')
292 ->willReturnCallback(function (string $hook, array $data, array $param): array {
293 return $data;
294 })
295 ;
296
297 $result = $this->controller->index($request, $response);
298
299 static::assertSame(200, $result->getStatusCode());
300 static::assertSame('daily', (string) $result->getBody());
301 static::assertCount(7, $assignedVariables['linksToDisplay']);
302
303 $columnIds = function (array $column): array {
304 return array_map(function (array $item): int { return $item['id']; }, $column);
305 };
306
307 static::assertSame([1, 4, 6], $columnIds($assignedVariables['cols'][0]));
308 static::assertSame([2], $columnIds($assignedVariables['cols'][1]));
309 static::assertSame([3, 5, 7], $columnIds($assignedVariables['cols'][2]));
310 }
311
312 /**
313 * Daily page - no bookmark
314 */
315 public function testValidIndexControllerInvokeNoBookmark(): void
316 {
317 $this->createValidContainerMockSet();
318
319 $request = $this->createMock(Request::class);
320 $response = new Response();
321
322 // Save RainTPL assigned variables
323 $assignedVariables = [];
324 $this->assignTemplateVars($assignedVariables);
325
326 // Links dataset: 2 links with thumbnails
327 $this->container->bookmarkService
328 ->expects(static::once())
329 ->method('days')
330 ->willReturnCallback(function (): array {
331 return [];
332 })
333 ;
334 $this->container->bookmarkService
335 ->expects(static::once())
336 ->method('filterDay')
337 ->willReturnCallback(function (): array {
338 return [];
339 })
340 ;
341
342 // Make sure that PluginManager hook is triggered
343 $this->container->pluginManager
344 ->expects(static::at(0))
345 ->method('executeHooks')
346 ->willReturnCallback(function (string $hook, array $data, array $param): array {
347 return $data;
348 })
349 ;
350
351 $result = $this->controller->index($request, $response);
352
353 static::assertSame(200, $result->getStatusCode());
354 static::assertSame('daily', (string) $result->getBody());
355 static::assertCount(0, $assignedVariables['linksToDisplay']);
356 static::assertSame('Today', $assignedVariables['dayDesc']);
357 static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
358 static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']);
359 }
360
361 /**
362 * Daily RSS - default behaviour
363 */
364 public function testValidRssControllerInvokeDefault(): void
365 {
366 $this->createValidContainerMockSet();
367
368 $dates = [
369 new \DateTimeImmutable('2020-05-17'),
370 new \DateTimeImmutable('2020-05-15'),
371 new \DateTimeImmutable('2020-05-13'),
372 ];
373
374 $request = $this->createMock(Request::class);
375 $response = new Response();
376
377 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
378 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
379 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
380 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
381 (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'),
382 ]);
383
384 $this->container->pageCacheManager
385 ->expects(static::once())
386 ->method('getCachePage')
387 ->willReturnCallback(function (): CachedPage {
388 $cachedPage = $this->createMock(CachedPage::class);
389 $cachedPage->expects(static::once())->method('cache')->with('dailyrss');
390
391 return $cachedPage;
392 }
393 );
394
395 // Save RainTPL assigned variables
396 $assignedVariables = [];
397 $this->assignTemplateVars($assignedVariables);
398
399 $result = $this->controller->rss($request, $response);
400
401 static::assertSame(200, $result->getStatusCode());
402 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
403 static::assertSame('dailyrss', (string) $result->getBody());
404 static::assertSame('Shaarli', $assignedVariables['title']);
405 static::assertSame('http://shaarli', $assignedVariables['index_url']);
406 static::assertSame('http://shaarli/daily-rss', $assignedVariables['page_url']);
407 static::assertFalse($assignedVariables['hide_timestamps']);
408 static::assertCount(2, $assignedVariables['days']);
409
410 $day = $assignedVariables['days'][$dates[0]->format('Ymd')];
411
412 static::assertEquals($dates[0], $day['date']);
413 static::assertSame($dates[0]->format(\DateTime::RSS), $day['date_rss']);
414 static::assertSame(format_date($dates[0], false), $day['date_human']);
415 static::assertSame('http://shaarli/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
416 static::assertCount(1, $day['links']);
417 static::assertSame(1, $day['links'][0]['id']);
418 static::assertSame('http://domain.tld/1', $day['links'][0]['url']);
419 static::assertEquals($dates[0], $day['links'][0]['created']);
420
421 $day = $assignedVariables['days'][$dates[1]->format('Ymd')];
422
423 static::assertEquals($dates[1], $day['date']);
424 static::assertSame($dates[1]->format(\DateTime::RSS), $day['date_rss']);
425 static::assertSame(format_date($dates[1], false), $day['date_human']);
426 static::assertSame('http://shaarli/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
427 static::assertCount(2, $day['links']);
428
429 static::assertSame(2, $day['links'][0]['id']);
430 static::assertSame('http://domain.tld/2', $day['links'][0]['url']);
431 static::assertEquals($dates[1], $day['links'][0]['created']);
432 static::assertSame(3, $day['links'][1]['id']);
433 static::assertSame('http://domain.tld/3', $day['links'][1]['url']);
434 static::assertEquals($dates[1], $day['links'][1]['created']);
435 }
436
437 /**
438 * Daily RSS - trigger cache rendering
439 */
440 public function testValidRssControllerInvokeTriggerCache(): void
441 {
442 $this->createValidContainerMockSet();
443
444 $request = $this->createMock(Request::class);
445 $response = new Response();
446
447 $this->container->pageCacheManager->method('getCachePage')->willReturnCallback(function (): CachedPage {
448 $cachedPage = $this->createMock(CachedPage::class);
449 $cachedPage->method('cachedVersion')->willReturn('this is cache!');
450
451 return $cachedPage;
452 });
453
454 $this->container->bookmarkService->expects(static::never())->method('search');
455
456 $result = $this->controller->rss($request, $response);
457
458 static::assertSame(200, $result->getStatusCode());
459 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
460 static::assertSame('this is cache!', (string) $result->getBody());
461 }
462
463 /**
464 * Daily RSS - No bookmark
465 */
466 public function testValidRssControllerInvokeNoBookmark(): void
467 {
468 $this->createValidContainerMockSet();
469
470 $request = $this->createMock(Request::class);
471 $response = new Response();
472
473 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([]);
474
475 // Save RainTPL assigned variables
476 $assignedVariables = [];
477 $this->assignTemplateVars($assignedVariables);
478
479 $result = $this->controller->rss($request, $response);
480
481 static::assertSame(200, $result->getStatusCode());
482 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
483 static::assertSame('dailyrss', (string) $result->getBody());
484 static::assertSame('Shaarli', $assignedVariables['title']);
485 static::assertSame('http://shaarli', $assignedVariables['index_url']);
486 static::assertSame('http://shaarli/daily-rss', $assignedVariables['page_url']);
487 static::assertFalse($assignedVariables['hide_timestamps']);
488 static::assertCount(0, $assignedVariables['days']);
489 }
490
491 protected static function generateContent(int $length): string
492 {
493 // bin2hex(random_bytes) generates string twice as long as given parameter
494 $length = (int) ceil($length / 2);
495 return bin2hex(random_bytes($length));
496 }
497}
diff --git a/tests/front/controller/visitor/FeedControllerTest.php b/tests/front/controller/visitor/FeedControllerTest.php
new file mode 100644
index 00000000..fd4679ea
--- /dev/null
+++ b/tests/front/controller/visitor/FeedControllerTest.php
@@ -0,0 +1,151 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Feed\FeedBuilder;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class FeedControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var FeedController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->container->feedBuilder = $this->createMock(FeedBuilder::class);
24
25 $this->controller = new FeedController($this->container);
26 }
27
28 /**
29 * Feed Controller - RSS default behaviour
30 */
31 public function testDefaultRssController(): void
32 {
33 $this->createValidContainerMockSet();
34
35 $request = $this->createMock(Request::class);
36 $response = new Response();
37
38 $this->container->feedBuilder->expects(static::once())->method('setLocale');
39 $this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false);
40 $this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true);
41
42 // Save RainTPL assigned variables
43 $assignedVariables = [];
44 $this->assignTemplateVars($assignedVariables);
45
46 $this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']);
47
48 // Make sure that PluginManager hook is triggered
49 $this->container->pluginManager
50 ->expects(static::at(0))
51 ->method('executeHooks')
52 ->willReturnCallback(function (string $hook, array $data, array $param): void {
53 static::assertSame('render_feed', $hook);
54 static::assertSame('data', $data['content']);
55
56 static::assertArrayHasKey('loggedin', $param);
57 static::assertSame('rss', $param['target']);
58 })
59 ;
60
61 $result = $this->controller->rss($request, $response);
62
63 static::assertSame(200, $result->getStatusCode());
64 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
65 static::assertSame('feed.rss', (string) $result->getBody());
66 static::assertSame('data', $assignedVariables['content']);
67 }
68
69 /**
70 * Feed Controller - ATOM default behaviour
71 */
72 public function testDefaultAtomController(): void
73 {
74 $this->createValidContainerMockSet();
75
76 $request = $this->createMock(Request::class);
77 $response = new Response();
78
79 $this->container->feedBuilder->expects(static::once())->method('setLocale');
80 $this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false);
81 $this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true);
82
83 // Save RainTPL assigned variables
84 $assignedVariables = [];
85 $this->assignTemplateVars($assignedVariables);
86
87 $this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']);
88
89 // Make sure that PluginManager hook is triggered
90 $this->container->pluginManager
91 ->expects(static::at(0))
92 ->method('executeHooks')
93 ->willReturnCallback(function (string $hook, array $data, array $param): void {
94 static::assertSame('render_feed', $hook);
95 static::assertSame('data', $data['content']);
96
97 static::assertArrayHasKey('loggedin', $param);
98 static::assertSame('atom', $param['target']);
99 })
100 ;
101
102 $result = $this->controller->atom($request, $response);
103
104 static::assertSame(200, $result->getStatusCode());
105 static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]);
106 static::assertSame('feed.atom', (string) $result->getBody());
107 static::assertSame('data', $assignedVariables['content']);
108 }
109
110 /**
111 * Feed Controller - ATOM with parameters
112 */
113 public function testAtomControllerWithParameters(): void
114 {
115 $this->createValidContainerMockSet();
116
117 $request = $this->createMock(Request::class);
118 $request->method('getParams')->willReturn(['parameter' => 'value']);
119 $response = new Response();
120
121 // Save RainTPL assigned variables
122 $assignedVariables = [];
123 $this->assignTemplateVars($assignedVariables);
124
125 $this->container->feedBuilder
126 ->method('buildData')
127 ->with('atom', ['parameter' => 'value'])
128 ->willReturn(['content' => 'data'])
129 ;
130
131 // Make sure that PluginManager hook is triggered
132 $this->container->pluginManager
133 ->expects(static::at(0))
134 ->method('executeHooks')
135 ->willReturnCallback(function (string $hook, array $data, array $param): void {
136 static::assertSame('render_feed', $hook);
137 static::assertSame('data', $data['content']);
138
139 static::assertArrayHasKey('loggedin', $param);
140 static::assertSame('atom', $param['target']);
141 })
142 ;
143
144 $result = $this->controller->atom($request, $response);
145
146 static::assertSame(200, $result->getStatusCode());
147 static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]);
148 static::assertSame('feed.atom', (string) $result->getBody());
149 static::assertSame('data', $assignedVariables['content']);
150 }
151}
diff --git a/tests/front/controller/visitor/FrontControllerMockHelper.php b/tests/front/controller/visitor/FrontControllerMockHelper.php
new file mode 100644
index 00000000..bc3266b5
--- /dev/null
+++ b/tests/front/controller/visitor/FrontControllerMockHelper.php
@@ -0,0 +1,114 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\MockObject\MockObject;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Container\ShaarliTestContainer;
11use Shaarli\Formatter\BookmarkFormatter;
12use Shaarli\Formatter\BookmarkRawFormatter;
13use Shaarli\Formatter\FormatterFactory;
14use Shaarli\Plugin\PluginManager;
15use Shaarli\Render\PageBuilder;
16use Shaarli\Render\PageCacheManager;
17use Shaarli\Security\LoginManager;
18use Shaarli\Security\SessionManager;
19
20/**
21 * Trait FrontControllerMockHelper
22 *
23 * Helper trait used to initialize the ShaarliContainer and mock its services for controller tests.
24 *
25 * @property ShaarliTestContainer $container
26 * @package Shaarli\Front\Controller
27 */
28trait FrontControllerMockHelper
29{
30 /** @var ShaarliTestContainer */
31 protected $container;
32
33 /**
34 * Mock the container instance
35 */
36 protected function createContainer(): void
37 {
38 $this->container = $this->createMock(ShaarliTestContainer::class);
39 }
40
41 /**
42 * Initialize container's services used by tests
43 */
44 protected function createValidContainerMockSet(): void
45 {
46 $this->container->loginManager = $this->createMock(LoginManager::class);
47
48 // Config
49 $this->container->conf = $this->createMock(ConfigManager::class);
50 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
51 return $default;
52 });
53
54 // PageBuilder
55 $this->container->pageBuilder = $this->createMock(PageBuilder::class);
56 $this->container->pageBuilder
57 ->method('render')
58 ->willReturnCallback(function (string $template): string {
59 return $template;
60 })
61 ;
62
63 // Plugin Manager
64 $this->container->pluginManager = $this->createMock(PluginManager::class);
65
66 // BookmarkService
67 $this->container->bookmarkService = $this->createMock(BookmarkServiceInterface::class);
68
69 // Formatter
70 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
71 $this->container->formatterFactory
72 ->method('getFormatter')
73 ->willReturnCallback(function (): BookmarkFormatter {
74 return new BookmarkRawFormatter($this->container->conf, true);
75 })
76 ;
77
78 // CacheManager
79 $this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
80
81 // SessionManager
82 $this->container->sessionManager = $this->createMock(SessionManager::class);
83
84 // $_SERVER
85 $this->container->environment = [
86 'SERVER_NAME' => 'shaarli',
87 'SERVER_PORT' => '80',
88 'REQUEST_URI' => '/daily-rss',
89 ];
90 }
91
92 /**
93 * Pass a reference of an array which will be populated by `pageBuilder->assign` calls during execution.
94 *
95 * @param mixed $variables Array reference to populate.
96 */
97 protected function assignTemplateVars(array &$variables): void
98 {
99 $this->container->pageBuilder
100 ->expects(static::atLeastOnce())
101 ->method('assign')
102 ->willReturnCallback(function ($key, $value) use (&$variables) {
103 $variables[$key] = $value;
104
105 return $this;
106 })
107 ;
108 }
109
110 /**
111 * Force to be used in PHPUnit context.
112 */
113 protected abstract function createMock($originalClassName): MockObject;
114}
diff --git a/tests/front/controller/visitor/LoginControllerTest.php b/tests/front/controller/visitor/LoginControllerTest.php
new file mode 100644
index 00000000..9d223316
--- /dev/null
+++ b/tests/front/controller/visitor/LoginControllerTest.php
@@ -0,0 +1,144 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Front\Exception\LoginBannedException;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13class LoginControllerTest extends TestCase
14{
15 use FrontControllerMockHelper;
16
17 /** @var LoginController */
18 protected $controller;
19
20 public function setUp(): void
21 {
22 $this->createContainer();
23
24 $this->controller = new LoginController($this->container);
25 }
26
27 public function testValidControllerInvoke(): void
28 {
29 $this->createValidContainerMockSet();
30
31 $request = $this->createMock(Request::class);
32 $request->expects(static::once())->method('getServerParam')->willReturn('> referer');
33 $response = new Response();
34
35 $assignedVariables = [];
36 $this->container->pageBuilder
37 ->method('assign')
38 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
39 $assignedVariables[$key] = $value;
40
41 return $this;
42 })
43 ;
44
45 $this->container->loginManager->method('canLogin')->willReturn(true);
46
47 $result = $this->controller->index($request, $response);
48
49 static::assertInstanceOf(Response::class, $result);
50 static::assertSame(200, $result->getStatusCode());
51 static::assertSame('loginform', (string) $result->getBody());
52
53 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
54 static::assertSame(true, $assignedVariables['remember_user_default']);
55 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
56 }
57
58 public function testValidControllerInvokeWithUserName(): void
59 {
60 $this->createValidContainerMockSet();
61
62 $request = $this->createMock(Request::class);
63 $request->expects(static::once())->method('getServerParam')->willReturn('> referer');
64 $request->expects(static::exactly(2))->method('getParam')->willReturn('myUser>');
65 $response = new Response();
66
67 $assignedVariables = [];
68 $this->container->pageBuilder
69 ->method('assign')
70 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
71 $assignedVariables[$key] = $value;
72
73 return $this;
74 })
75 ;
76
77 $this->container->loginManager->expects(static::once())->method('canLogin')->willReturn(true);
78
79 $result = $this->controller->index($request, $response);
80
81 static::assertInstanceOf(Response::class, $result);
82 static::assertSame(200, $result->getStatusCode());
83 static::assertSame('loginform', (string) $result->getBody());
84
85 static::assertSame('myUser&gt;', $assignedVariables['username']);
86 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
87 static::assertSame(true, $assignedVariables['remember_user_default']);
88 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
89 }
90
91 public function testLoginControllerWhileLoggedIn(): void
92 {
93 $this->createValidContainerMockSet();
94
95 $request = $this->createMock(Request::class);
96 $response = new Response();
97
98 $this->container->loginManager->expects(static::once())->method('isLoggedIn')->willReturn(true);
99
100 $result = $this->controller->index($request, $response);
101
102 static::assertInstanceOf(Response::class, $result);
103 static::assertSame(302, $result->getStatusCode());
104 static::assertSame(['./'], $result->getHeader('Location'));
105 }
106
107 public function testLoginControllerOpenShaarli(): void
108 {
109 $this->createValidContainerMockSet();
110
111 $request = $this->createMock(Request::class);
112 $response = new Response();
113
114 $conf = $this->createMock(ConfigManager::class);
115 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
116 if ($parameter === 'security.open_shaarli') {
117 return true;
118 }
119 return $default;
120 });
121 $this->container->conf = $conf;
122
123 $result = $this->controller->index($request, $response);
124
125 static::assertInstanceOf(Response::class, $result);
126 static::assertSame(302, $result->getStatusCode());
127 static::assertSame(['./'], $result->getHeader('Location'));
128 }
129
130 public function testLoginControllerWhileBanned(): void
131 {
132 $this->createValidContainerMockSet();
133
134 $request = $this->createMock(Request::class);
135 $response = new Response();
136
137 $this->container->loginManager->method('isLoggedIn')->willReturn(false);
138 $this->container->loginManager->method('canLogin')->willReturn(false);
139
140 $this->expectException(LoginBannedException::class);
141
142 $this->controller->index($request, $response);
143 }
144}
diff --git a/tests/front/controller/visitor/OpenSearchControllerTest.php b/tests/front/controller/visitor/OpenSearchControllerTest.php
new file mode 100644
index 00000000..52475318
--- /dev/null
+++ b/tests/front/controller/visitor/OpenSearchControllerTest.php
@@ -0,0 +1,46 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11class OpenSearchControllerTest extends TestCase
12{
13 use FrontControllerMockHelper;
14
15 /** @var OpenSearchController */
16 protected $controller;
17
18 public function setUp(): void
19 {
20 $this->createContainer();
21
22 $this->controller = new OpenSearchController($this->container);
23 }
24
25 public function testOpenSearchController(): void
26 {
27 $this->createValidContainerMockSet();
28
29 $request = $this->createMock(Request::class);
30 $response = new Response();
31
32 // Save RainTPL assigned variables
33 $assignedVariables = [];
34 $this->assignTemplateVars($assignedVariables);
35
36 $result = $this->controller->index($request, $response);
37
38 static::assertSame(200, $result->getStatusCode());
39 static::assertStringContainsString(
40 'application/opensearchdescription+xml',
41 $result->getHeader('Content-Type')[0]
42 );
43 static::assertSame('opensearch', (string) $result->getBody());
44 static::assertSame('http://shaarli', $assignedVariables['serverurl']);
45 }
46}
diff --git a/tests/front/controller/visitor/PictureWallControllerTest.php b/tests/front/controller/visitor/PictureWallControllerTest.php
new file mode 100644
index 00000000..7ac842cb
--- /dev/null
+++ b/tests/front/controller/visitor/PictureWallControllerTest.php
@@ -0,0 +1,125 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\Bookmark;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Front\Exception\ThumbnailsDisabledException;
11use Shaarli\Thumbnailer;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class PictureWallControllerTest extends TestCase
16{
17 use FrontControllerMockHelper;
18
19 /** @var PictureWallController */
20 protected $controller;
21
22 public function setUp(): void
23 {
24 $this->createContainer();
25
26 $this->controller = new PictureWallController($this->container);
27 }
28
29 public function testValidControllerInvokeDefault(): void
30 {
31 $this->createValidContainerMockSet();
32
33 $request = $this->createMock(Request::class);
34 $request->expects(static::once())->method('getQueryParams')->willReturn([]);
35 $response = new Response();
36
37 // ConfigManager: thumbnails are enabled
38 $this->container->conf = $this->createMock(ConfigManager::class);
39 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
40 if ($parameter === 'thumbnails.mode') {
41 return Thumbnailer::MODE_COMMON;
42 }
43
44 return $default;
45 });
46
47 // Save RainTPL assigned variables
48 $assignedVariables = [];
49 $this->assignTemplateVars($assignedVariables);
50
51 // Links dataset: 2 links with thumbnails
52 $this->container->bookmarkService
53 ->expects(static::once())
54 ->method('search')
55 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
56 // Visibility is set through the container, not the call
57 static::assertNull($visibility);
58
59 // No query parameters
60 if (count($parameters) === 0) {
61 return [
62 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setThumbnail('thumb1'),
63 (new Bookmark())->setId(2)->setUrl('http://url2.tld'),
64 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setThumbnail('thumb2'),
65 ];
66 }
67 })
68 ;
69
70 // Make sure that PluginManager hook is triggered
71 $this->container->pluginManager
72 ->expects(static::at(0))
73 ->method('executeHooks')
74 ->willReturnCallback(function (string $hook, array $data, array $param): array {
75 static::assertSame('render_picwall', $hook);
76 static::assertArrayHasKey('linksToDisplay', $data);
77 static::assertCount(2, $data['linksToDisplay']);
78 static::assertSame(1, $data['linksToDisplay'][0]['id']);
79 static::assertSame(3, $data['linksToDisplay'][1]['id']);
80 static::assertArrayHasKey('loggedin', $param);
81
82 return $data;
83 });
84
85 $result = $this->controller->index($request, $response);
86
87 static::assertSame(200, $result->getStatusCode());
88 static::assertSame('picwall', (string) $result->getBody());
89 static::assertSame('Picture wall - Shaarli', $assignedVariables['pagetitle']);
90 static::assertCount(2, $assignedVariables['linksToDisplay']);
91
92 $link = $assignedVariables['linksToDisplay'][0];
93
94 static::assertSame(1, $link['id']);
95 static::assertSame('http://url.tld', $link['url']);
96 static::assertSame('thumb1', $link['thumbnail']);
97
98 $link = $assignedVariables['linksToDisplay'][1];
99
100 static::assertSame(3, $link['id']);
101 static::assertSame('http://url3.tld', $link['url']);
102 static::assertSame('thumb2', $link['thumbnail']);
103 }
104
105 public function testControllerWithThumbnailsDisabled(): void
106 {
107 $this->expectException(ThumbnailsDisabledException::class);
108
109 $this->createValidContainerMockSet();
110
111 $request = $this->createMock(Request::class);
112 $response = new Response();
113
114 // ConfigManager: thumbnails are disabled
115 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
116 if ($parameter === 'thumbnails.mode') {
117 return Thumbnailer::MODE_NONE;
118 }
119
120 return $default;
121 });
122
123 $this->controller->index($request, $response);
124 }
125}
diff --git a/tests/front/controller/visitor/ShaarliPublicControllerTest.php b/tests/front/controller/visitor/ShaarliPublicControllerTest.php
new file mode 100644
index 00000000..e2e88da3
--- /dev/null
+++ b/tests/front/controller/visitor/ShaarliPublicControllerTest.php
@@ -0,0 +1,239 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkFilter;
9use Slim\Http\Request;
10use Slim\Http\Response;
11use Slim\Http\Uri;
12
13/**
14 * Class ShaarliControllerTest
15 *
16 * This class is used to test default behavior of ShaarliController abstract class.
17 * It uses a dummy non abstract controller.
18 */
19class ShaarliControllerTest extends TestCase
20{
21 use FrontControllerMockHelper;
22
23 /** @var LoginController */
24 protected $controller;
25
26 /** @var mixed[] List of variable assigned to the template */
27 protected $assignedValues;
28
29 /** @var Request */
30 protected $request;
31
32 public function setUp(): void
33 {
34 $this->createContainer();
35
36 $this->controller = new class($this->container) extends ShaarliVisitorController
37 {
38 public function assignView(string $key, $value): ShaarliVisitorController
39 {
40 return parent::assignView($key, $value);
41 }
42
43 public function render(string $template): string
44 {
45 return parent::render($template);
46 }
47
48 public function redirectFromReferer(
49 Request $request,
50 Response $response,
51 array $loopTerms = [],
52 array $clearParams = []
53 ): Response {
54 return parent::redirectFromReferer($request, $response, $loopTerms, $clearParams);
55 }
56 };
57 $this->assignedValues = [];
58
59 $this->request = $this->createMock(Request::class);
60 $this->request->method('getUri')->willReturnCallback(function (): Uri {
61 $uri = $this->createMock(Uri::class);
62 $uri->method('getBasePath')->willReturn('/subfolder');
63
64 return $uri;
65 });
66 }
67
68 public function testAssignView(): void
69 {
70 $this->createValidContainerMockSet();
71
72 $this->assignTemplateVars($this->assignedValues);
73
74 $self = $this->controller->assignView('variableName', 'variableValue');
75
76 static::assertInstanceOf(ShaarliVisitorController::class, $self);
77 static::assertSame('variableValue', $this->assignedValues['variableName']);
78 }
79
80 public function testRender(): void
81 {
82 $this->createValidContainerMockSet();
83
84 $this->assignTemplateVars($this->assignedValues);
85
86 $this->container->bookmarkService
87 ->method('count')
88 ->willReturnCallback(function (string $visibility): int {
89 return $visibility === BookmarkFilter::$PRIVATE ? 5 : 10;
90 })
91 ;
92
93 $this->container->pluginManager
94 ->method('executeHooks')
95 ->willReturnCallback(function (string $hook, array &$data, array $params): array {
96 return $data[$hook] = $params;
97 });
98 $this->container->pluginManager->method('getErrors')->willReturn(['error']);
99
100 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
101
102 $render = $this->controller->render('templateName');
103
104 static::assertSame('templateName', $render);
105
106 static::assertSame(10, $this->assignedValues['linkcount']);
107 static::assertSame(5, $this->assignedValues['privateLinkcount']);
108 static::assertSame(['error'], $this->assignedValues['plugin_errors']);
109
110 static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']);
111 static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']);
112 static::assertSame('templateName', $this->assignedValues['plugins_header']['render_header']['target']);
113 static::assertTrue($this->assignedValues['plugins_header']['render_header']['loggedin']);
114 static::assertSame('templateName', $this->assignedValues['plugins_footer']['render_footer']['target']);
115 static::assertTrue($this->assignedValues['plugins_footer']['render_footer']['loggedin']);
116 }
117
118 /**
119 * Test redirectFromReferer() - Default behaviour
120 */
121 public function testRedirectFromRefererDefault(): void
122 {
123 $this->createValidContainerMockSet();
124
125 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
126
127 $response = new Response();
128
129 $result = $this->controller->redirectFromReferer($this->request, $response);
130
131 static::assertSame(302, $result->getStatusCode());
132 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
133 }
134
135 /**
136 * Test redirectFromReferer() - With a loop term not matched in the referer
137 */
138 public function testRedirectFromRefererWithUnmatchedLoopTerm(): void
139 {
140 $this->createValidContainerMockSet();
141
142 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
143
144 $response = new Response();
145
146 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope']);
147
148 static::assertSame(302, $result->getStatusCode());
149 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
150 }
151
152 /**
153 * Test redirectFromReferer() - With a loop term matching the referer in its path -> redirect to default
154 */
155 public function testRedirectFromRefererWithMatchingLoopTermInPath(): void
156 {
157 $this->createValidContainerMockSet();
158
159 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
160
161 $response = new Response();
162
163 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'controller']);
164
165 static::assertSame(302, $result->getStatusCode());
166 static::assertSame(['/subfolder'], $result->getHeader('location'));
167 }
168
169 /**
170 * Test redirectFromReferer() - With a loop term matching the referer in its query parameters -> redirect to default
171 */
172 public function testRedirectFromRefererWithMatchingLoopTermInQueryParam(): void
173 {
174 $this->createValidContainerMockSet();
175
176 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
177
178 $response = new Response();
179
180 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'other']);
181
182 static::assertSame(302, $result->getStatusCode());
183 static::assertSame(['/subfolder'], $result->getHeader('location'));
184 }
185
186 /**
187 * Test redirectFromReferer() - With a loop term matching the referer in its query value
188 * -> we do not block redirection for query parameter values.
189 */
190 public function testRedirectFromRefererWithMatchingLoopTermInQueryValue(): void
191 {
192 $this->createValidContainerMockSet();
193
194 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
195
196 $response = new Response();
197
198 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'param']);
199
200 static::assertSame(302, $result->getStatusCode());
201 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
202 }
203
204 /**
205 * Test redirectFromReferer() - With a loop term matching the referer in its domain name
206 * -> we do not block redirection for shaarli's hosts
207 */
208 public function testRedirectFromRefererWithLoopTermInDomain(): void
209 {
210 $this->createValidContainerMockSet();
211
212 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
213
214 $response = new Response();
215
216 $result = $this->controller->redirectFromReferer($this->request, $response, ['shaarli']);
217
218 static::assertSame(302, $result->getStatusCode());
219 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
220 }
221
222 /**
223 * Test redirectFromReferer() - With a loop term matching a query parameter AND clear this query param
224 * -> the param should be cleared before checking if it matches the redir loop terms
225 */
226 public function testRedirectFromRefererWithMatchingClearedParam(): void
227 {
228 $this->createValidContainerMockSet();
229
230 $this->container->environment['HTTP_REFERER'] = 'http://shaarli.tld/subfolder/controller?query=param&other=2';
231
232 $response = new Response();
233
234 $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
235
236 static::assertSame(302, $result->getStatusCode());
237 static::assertSame(['/subfolder/controller?other=2'], $result->getHeader('location'));
238 }
239}
diff --git a/tests/front/controller/visitor/TagCloudControllerTest.php b/tests/front/controller/visitor/TagCloudControllerTest.php
new file mode 100644
index 00000000..e636d496
--- /dev/null
+++ b/tests/front/controller/visitor/TagCloudControllerTest.php
@@ -0,0 +1,381 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkFilter;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class TagCloudControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var TagCloudController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new TagCloudController($this->container);
24 }
25
26 /**
27 * Tag Cloud - default parameters
28 */
29 public function testValidCloudControllerInvokeDefault(): void
30 {
31 $this->createValidContainerMockSet();
32
33 $allTags = [
34 'ghi' => 1,
35 'abc' => 3,
36 'def' => 12,
37 ];
38 $expectedOrder = ['abc', 'def', 'ghi'];
39
40 $request = $this->createMock(Request::class);
41 $response = new Response();
42
43 // Save RainTPL assigned variables
44 $assignedVariables = [];
45 $this->assignTemplateVars($assignedVariables);
46
47 $this->container->bookmarkService
48 ->expects(static::once())
49 ->method('bookmarksCountPerTag')
50 ->with([], null)
51 ->willReturnCallback(function () use ($allTags): array {
52 return $allTags;
53 })
54 ;
55
56 // Make sure that PluginManager hook is triggered
57 $this->container->pluginManager
58 ->expects(static::at(0))
59 ->method('executeHooks')
60 ->willReturnCallback(function (string $hook, array $data, array $param): array {
61 static::assertSame('render_tagcloud', $hook);
62 static::assertSame('', $data['search_tags']);
63 static::assertCount(3, $data['tags']);
64
65 static::assertArrayHasKey('loggedin', $param);
66
67 return $data;
68 })
69 ;
70
71 $result = $this->controller->cloud($request, $response);
72
73 static::assertSame(200, $result->getStatusCode());
74 static::assertSame('tag.cloud', (string) $result->getBody());
75 static::assertSame('Tag cloud - Shaarli', $assignedVariables['pagetitle']);
76
77 static::assertSame('', $assignedVariables['search_tags']);
78 static::assertCount(3, $assignedVariables['tags']);
79 static::assertSame($expectedOrder, array_keys($assignedVariables['tags']));
80
81 foreach ($allTags as $tag => $count) {
82 static::assertArrayHasKey($tag, $assignedVariables['tags']);
83 static::assertSame($count, $assignedVariables['tags'][$tag]['count']);
84 static::assertGreaterThan(0, $assignedVariables['tags'][$tag]['size']);
85 static::assertLessThan(5, $assignedVariables['tags'][$tag]['size']);
86 }
87 }
88
89 /**
90 * Tag Cloud - Additional parameters:
91 * - logged in
92 * - visibility private
93 * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore)
94 */
95 public function testValidCloudControllerInvokeWithParameters(): void
96 {
97 $this->createValidContainerMockSet();
98
99 $request = $this->createMock(Request::class);
100 $request
101 ->method('getQueryParam')
102 ->with()
103 ->willReturnCallback(function (string $key): ?string {
104 if ('searchtags' === $key) {
105 return 'ghi def';
106 }
107
108 return null;
109 })
110 ;
111 $response = new Response();
112
113 // Save RainTPL assigned variables
114 $assignedVariables = [];
115 $this->assignTemplateVars($assignedVariables);
116
117 $this->container->loginManager->method('isLoggedin')->willReturn(true);
118 $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private');
119
120 $this->container->bookmarkService
121 ->expects(static::once())
122 ->method('bookmarksCountPerTag')
123 ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE)
124 ->willReturnCallback(function (): array {
125 return ['abc' => 3];
126 })
127 ;
128
129 // Make sure that PluginManager hook is triggered
130 $this->container->pluginManager
131 ->expects(static::at(0))
132 ->method('executeHooks')
133 ->willReturnCallback(function (string $hook, array $data, array $param): array {
134 static::assertSame('render_tagcloud', $hook);
135 static::assertSame('ghi def', $data['search_tags']);
136 static::assertCount(1, $data['tags']);
137
138 static::assertArrayHasKey('loggedin', $param);
139
140 return $data;
141 })
142 ;
143
144 $result = $this->controller->cloud($request, $response);
145
146 static::assertSame(200, $result->getStatusCode());
147 static::assertSame('tag.cloud', (string) $result->getBody());
148 static::assertSame('ghi def - Tag cloud - Shaarli', $assignedVariables['pagetitle']);
149
150 static::assertSame('ghi def', $assignedVariables['search_tags']);
151 static::assertCount(1, $assignedVariables['tags']);
152
153 static::assertArrayHasKey('abc', $assignedVariables['tags']);
154 static::assertSame(3, $assignedVariables['tags']['abc']['count']);
155 static::assertGreaterThan(0, $assignedVariables['tags']['abc']['size']);
156 static::assertLessThan(5, $assignedVariables['tags']['abc']['size']);
157 }
158
159 /**
160 * Tag Cloud - empty
161 */
162 public function testEmptyCloud(): void
163 {
164 $this->createValidContainerMockSet();
165
166 $request = $this->createMock(Request::class);
167 $response = new Response();
168
169 // Save RainTPL assigned variables
170 $assignedVariables = [];
171 $this->assignTemplateVars($assignedVariables);
172
173 $this->container->bookmarkService
174 ->expects(static::once())
175 ->method('bookmarksCountPerTag')
176 ->with([], null)
177 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
178 return [];
179 })
180 ;
181
182 // Make sure that PluginManager hook is triggered
183 $this->container->pluginManager
184 ->expects(static::at(0))
185 ->method('executeHooks')
186 ->willReturnCallback(function (string $hook, array $data, array $param): array {
187 static::assertSame('render_tagcloud', $hook);
188 static::assertSame('', $data['search_tags']);
189 static::assertCount(0, $data['tags']);
190
191 static::assertArrayHasKey('loggedin', $param);
192
193 return $data;
194 })
195 ;
196
197 $result = $this->controller->cloud($request, $response);
198
199 static::assertSame(200, $result->getStatusCode());
200 static::assertSame('tag.cloud', (string) $result->getBody());
201 static::assertSame('Tag cloud - Shaarli', $assignedVariables['pagetitle']);
202
203 static::assertSame('', $assignedVariables['search_tags']);
204 static::assertCount(0, $assignedVariables['tags']);
205 }
206
207 /**
208 * Tag List - Default sort is by usage DESC
209 */
210 public function testValidListControllerInvokeDefault(): void
211 {
212 $this->createValidContainerMockSet();
213
214 $allTags = [
215 'def' => 12,
216 'abc' => 3,
217 'ghi' => 1,
218 ];
219
220 $request = $this->createMock(Request::class);
221 $response = new Response();
222
223 // Save RainTPL assigned variables
224 $assignedVariables = [];
225 $this->assignTemplateVars($assignedVariables);
226
227 $this->container->bookmarkService
228 ->expects(static::once())
229 ->method('bookmarksCountPerTag')
230 ->with([], null)
231 ->willReturnCallback(function () use ($allTags): array {
232 return $allTags;
233 })
234 ;
235
236 // Make sure that PluginManager hook is triggered
237 $this->container->pluginManager
238 ->expects(static::at(0))
239 ->method('executeHooks')
240 ->willReturnCallback(function (string $hook, array $data, array $param): array {
241 static::assertSame('render_taglist', $hook);
242 static::assertSame('', $data['search_tags']);
243 static::assertCount(3, $data['tags']);
244
245 static::assertArrayHasKey('loggedin', $param);
246
247 return $data;
248 })
249 ;
250
251 $result = $this->controller->list($request, $response);
252
253 static::assertSame(200, $result->getStatusCode());
254 static::assertSame('tag.list', (string) $result->getBody());
255 static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']);
256
257 static::assertSame('', $assignedVariables['search_tags']);
258 static::assertCount(3, $assignedVariables['tags']);
259
260 foreach ($allTags as $tag => $count) {
261 static::assertSame($count, $assignedVariables['tags'][$tag]);
262 }
263 }
264
265 /**
266 * Tag List - Additional parameters:
267 * - logged in
268 * - visibility private
269 * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore)
270 * - sort alphabetically
271 */
272 public function testValidListControllerInvokeWithParameters(): void
273 {
274 $this->createValidContainerMockSet();
275
276 $request = $this->createMock(Request::class);
277 $request
278 ->method('getQueryParam')
279 ->with()
280 ->willReturnCallback(function (string $key): ?string {
281 if ('searchtags' === $key) {
282 return 'ghi def';
283 } elseif ('sort' === $key) {
284 return 'alpha';
285 }
286
287 return null;
288 })
289 ;
290 $response = new Response();
291
292 // Save RainTPL assigned variables
293 $assignedVariables = [];
294 $this->assignTemplateVars($assignedVariables);
295
296 $this->container->loginManager->method('isLoggedin')->willReturn(true);
297 $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private');
298
299 $this->container->bookmarkService
300 ->expects(static::once())
301 ->method('bookmarksCountPerTag')
302 ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE)
303 ->willReturnCallback(function (): array {
304 return ['abc' => 3];
305 })
306 ;
307
308 // Make sure that PluginManager hook is triggered
309 $this->container->pluginManager
310 ->expects(static::at(0))
311 ->method('executeHooks')
312 ->willReturnCallback(function (string $hook, array $data, array $param): array {
313 static::assertSame('render_taglist', $hook);
314 static::assertSame('ghi def', $data['search_tags']);
315 static::assertCount(1, $data['tags']);
316
317 static::assertArrayHasKey('loggedin', $param);
318
319 return $data;
320 })
321 ;
322
323 $result = $this->controller->list($request, $response);
324
325 static::assertSame(200, $result->getStatusCode());
326 static::assertSame('tag.list', (string) $result->getBody());
327 static::assertSame('ghi def - Tag list - Shaarli', $assignedVariables['pagetitle']);
328
329 static::assertSame('ghi def', $assignedVariables['search_tags']);
330 static::assertCount(1, $assignedVariables['tags']);
331 static::assertSame(3, $assignedVariables['tags']['abc']);
332 }
333
334 /**
335 * Tag List - empty
336 */
337 public function testEmptyList(): void
338 {
339 $this->createValidContainerMockSet();
340
341 $request = $this->createMock(Request::class);
342 $response = new Response();
343
344 // Save RainTPL assigned variables
345 $assignedVariables = [];
346 $this->assignTemplateVars($assignedVariables);
347
348 $this->container->bookmarkService
349 ->expects(static::once())
350 ->method('bookmarksCountPerTag')
351 ->with([], null)
352 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
353 return [];
354 })
355 ;
356
357 // Make sure that PluginManager hook is triggered
358 $this->container->pluginManager
359 ->expects(static::at(0))
360 ->method('executeHooks')
361 ->willReturnCallback(function (string $hook, array $data, array $param): array {
362 static::assertSame('render_taglist', $hook);
363 static::assertSame('', $data['search_tags']);
364 static::assertCount(0, $data['tags']);
365
366 static::assertArrayHasKey('loggedin', $param);
367
368 return $data;
369 })
370 ;
371
372 $result = $this->controller->list($request, $response);
373
374 static::assertSame(200, $result->getStatusCode());
375 static::assertSame('tag.list', (string) $result->getBody());
376 static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']);
377
378 static::assertSame('', $assignedVariables['search_tags']);
379 static::assertCount(0, $assignedVariables['tags']);
380 }
381}
diff --git a/tests/front/controller/visitor/TagControllerTest.php b/tests/front/controller/visitor/TagControllerTest.php
new file mode 100644
index 00000000..9a2b1f71
--- /dev/null
+++ b/tests/front/controller/visitor/TagControllerTest.php
@@ -0,0 +1,241 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use PHPUnit\Framework\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11class TagControllerTest extends TestCase
12{
13 use FrontControllerMockHelper;
14
15 /** @var TagController */ protected $controller;
16
17 public function setUp(): void
18 {
19 $this->createContainer();
20
21 $this->controller = new TagController($this->container);
22 }
23
24 public function testAddTagWithReferer(): void
25 {
26 $this->createValidContainerMockSet();
27
28 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
29
30 $request = $this->createMock(Request::class);
31 $response = new Response();
32
33 $tags = ['newTag' => 'abc'];
34
35 $result = $this->controller->addTag($request, $response, $tags);
36
37 static::assertInstanceOf(Response::class, $result);
38 static::assertSame(302, $result->getStatusCode());
39 static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
40 }
41
42 public function testAddTagWithRefererAndExistingSearch(): void
43 {
44 $this->createValidContainerMockSet();
45
46 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
47
48 $request = $this->createMock(Request::class);
49 $response = new Response();
50
51 $tags = ['newTag' => 'abc'];
52
53 $result = $this->controller->addTag($request, $response, $tags);
54
55 static::assertInstanceOf(Response::class, $result);
56 static::assertSame(302, $result->getStatusCode());
57 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
58 }
59
60 public function testAddTagWithoutRefererAndExistingSearch(): void
61 {
62 $this->createValidContainerMockSet();
63
64 $request = $this->createMock(Request::class);
65 $response = new Response();
66
67 $tags = ['newTag' => 'abc'];
68
69 $result = $this->controller->addTag($request, $response, $tags);
70
71 static::assertInstanceOf(Response::class, $result);
72 static::assertSame(302, $result->getStatusCode());
73 static::assertSame(['./?searchtags=abc'], $result->getHeader('location'));
74 }
75
76 public function testAddTagRemoveLegacyQueryParam(): void
77 {
78 $this->createValidContainerMockSet();
79
80 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc'];
81
82 $request = $this->createMock(Request::class);
83 $response = new Response();
84
85 $tags = ['newTag' => 'abc'];
86
87 $result = $this->controller->addTag($request, $response, $tags);
88
89 static::assertInstanceOf(Response::class, $result);
90 static::assertSame(302, $result->getStatusCode());
91 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
92 }
93
94 public function testAddTagResetPagination(): void
95 {
96 $this->createValidContainerMockSet();
97
98 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12'];
99
100 $request = $this->createMock(Request::class);
101 $response = new Response();
102
103 $tags = ['newTag' => 'abc'];
104
105 $result = $this->controller->addTag($request, $response, $tags);
106
107 static::assertInstanceOf(Response::class, $result);
108 static::assertSame(302, $result->getStatusCode());
109 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
110 }
111
112 public function testAddTagWithRefererAndEmptySearch(): void
113 {
114 $this->createValidContainerMockSet();
115
116 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags='];
117
118 $request = $this->createMock(Request::class);
119 $response = new Response();
120
121 $tags = ['newTag' => 'abc'];
122
123 $result = $this->controller->addTag($request, $response, $tags);
124
125 static::assertInstanceOf(Response::class, $result);
126 static::assertSame(302, $result->getStatusCode());
127 static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
128 }
129
130 public function testAddTagWithoutNewTagWithReferer(): void
131 {
132 $this->createValidContainerMockSet();
133
134 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
135
136 $request = $this->createMock(Request::class);
137 $response = new Response();
138
139 $result = $this->controller->addTag($request, $response, []);
140
141 static::assertInstanceOf(Response::class, $result);
142 static::assertSame(302, $result->getStatusCode());
143 static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
144 }
145
146 public function testAddTagWithoutNewTagWithoutReferer(): void
147 {
148 $this->createValidContainerMockSet();
149
150 $request = $this->createMock(Request::class);
151 $response = new Response();
152
153 $result = $this->controller->addTag($request, $response, []);
154
155 static::assertInstanceOf(Response::class, $result);
156 static::assertSame(302, $result->getStatusCode());
157 static::assertSame(['./'], $result->getHeader('location'));
158 }
159
160 public function testRemoveTagWithoutMatchingTag(): void
161 {
162 $this->createValidContainerMockSet();
163
164 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
165
166 $request = $this->createMock(Request::class);
167 $response = new Response();
168
169 $tags = ['tag' => 'abc'];
170
171 $result = $this->controller->removeTag($request, $response, $tags);
172
173 static::assertInstanceOf(Response::class, $result);
174 static::assertSame(302, $result->getStatusCode());
175 static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
176 }
177
178 public function testRemoveTagWithoutTagsearch(): void
179 {
180 $this->createValidContainerMockSet();
181
182 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
183
184 $request = $this->createMock(Request::class);
185 $response = new Response();
186
187 $tags = ['tag' => 'abc'];
188
189 $result = $this->controller->removeTag($request, $response, $tags);
190
191 static::assertInstanceOf(Response::class, $result);
192 static::assertSame(302, $result->getStatusCode());
193 static::assertSame(['/controller/'], $result->getHeader('location'));
194 }
195
196 public function testRemoveTagWithoutReferer(): void
197 {
198 $this->createValidContainerMockSet();
199
200 $request = $this->createMock(Request::class);
201 $response = new Response();
202
203 $tags = ['tag' => 'abc'];
204
205 $result = $this->controller->removeTag($request, $response, $tags);
206
207 static::assertInstanceOf(Response::class, $result);
208 static::assertSame(302, $result->getStatusCode());
209 static::assertSame(['./'], $result->getHeader('location'));
210 }
211
212 public function testRemoveTagWithoutTag(): void
213 {
214 $this->createValidContainerMockSet();
215
216 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtag=abc'];
217
218 $request = $this->createMock(Request::class);
219 $response = new Response();
220
221 $result = $this->controller->removeTag($request, $response, []);
222
223 static::assertInstanceOf(Response::class, $result);
224 static::assertSame(302, $result->getStatusCode());
225 static::assertSame(['/controller/?searchtag=abc'], $result->getHeader('location'));
226 }
227
228 public function testRemoveTagWithoutTagWithoutReferer(): void
229 {
230 $this->createValidContainerMockSet();
231
232 $request = $this->createMock(Request::class);
233 $response = new Response();
234
235 $result = $this->controller->removeTag($request, $response, []);
236
237 static::assertInstanceOf(Response::class, $result);
238 static::assertSame(302, $result->getStatusCode());
239 static::assertSame(['./'], $result->getHeader('location'));
240 }
241}