From 5d8de7587d67b5c3e5d1fed8562d9b87ecde80c1 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 10 Oct 2020 17:40:26 +0200 Subject: Feature: bulk creation of bookmarks This changes creates a new form in addlink page allowing to create multiple bookmarks at once more easily. It focuses on re-using as much existing code and template component as possible. These changes includes: - a new form in addlink (hidden behind a button by default), containing a text area for URL, and tags/private status to apply to created links - this form displays a new template called editlink.batch, itself including editlink template multiple times - User interation in this new templates are handle by a new JS script (shaare-batch.js) making AJAX requests, and therefore does not need page reloading - ManageShaareController has been split into 3 distinct controllers: + ShaareAdd: displays addlink template + ShaareManage: various operation applied on existing shaares (change visibility, pin, deletion, etc.) + ShaarePublish: handles creation/edit forms and saving Shaare's form - Updated translations Fixes #137 --- .../DeleteBookmarkTest.php | 380 +++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php (limited to 'tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php') diff --git a/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php new file mode 100644 index 00000000..770a16d7 --- /dev/null +++ b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php @@ -0,0 +1,380 @@ +createContainer(); + + $this->container->httpAccess = $this->createMock(HttpAccess::class); + $this->controller = new ShaareManageController($this->container); + } + + /** + * Delete bookmark - Single bookmark with valid parameters + */ + public function testDeleteSingleBookmark(): void + { + $parameters = ['id' => '123']; + + $request = $this->createMock(Request::class); + $request + ->method('getParam') + ->willReturnCallback(function (string $key) use ($parameters): ?string { + return $parameters[$key] ?? null; + }) + ; + $response = new Response(); + + $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'); + + $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark); + $this->container->bookmarkService->expects(static::once())->method('remove')->with($bookmark, false); + $this->container->bookmarkService->expects(static::once())->method('save'); + $this->container->formatterFactory = $this->createMock(FormatterFactory::class); + $this->container->formatterFactory + ->expects(static::once()) + ->method('getFormatter') + ->with('raw') + ->willReturnCallback(function () use ($bookmark): BookmarkFormatter { + $formatter = $this->createMock(BookmarkFormatter::class); + $formatter + ->expects(static::once()) + ->method('format') + ->with($bookmark) + ->willReturn(['formatted' => $bookmark]) + ; + + return $formatter; + }) + ; + + // Make sure that PluginManager hook is triggered + $this->container->pluginManager + ->expects(static::once()) + ->method('executeHooks') + ->with('delete_link', ['formatted' => $bookmark]) + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(302, $result->getStatusCode()); + static::assertSame(['/subfolder/'], $result->getHeader('location')); + } + + /** + * Delete bookmark - Multiple bookmarks with valid parameters + */ + public function testDeleteMultipleBookmarks(): void + { + $parameters = ['id' => '123 456 789']; + + $request = $this->createMock(Request::class); + $request + ->method('getParam') + ->willReturnCallback(function (string $key) use ($parameters): ?string { + return $parameters[$key] ?? null; + }) + ; + $response = new Response(); + + $bookmarks = [ + (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'), + (new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456'), + (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'), + ]; + + $this->container->bookmarkService + ->expects(static::exactly(3)) + ->method('get') + ->withConsecutive([123], [456], [789]) + ->willReturnOnConsecutiveCalls(...$bookmarks) + ; + $this->container->bookmarkService + ->expects(static::exactly(3)) + ->method('remove') + ->withConsecutive(...array_map(function (Bookmark $bookmark): array { + return [$bookmark, false]; + }, $bookmarks)) + ; + $this->container->bookmarkService->expects(static::once())->method('save'); + $this->container->formatterFactory = $this->createMock(FormatterFactory::class); + $this->container->formatterFactory + ->expects(static::once()) + ->method('getFormatter') + ->with('raw') + ->willReturnCallback(function () use ($bookmarks): BookmarkFormatter { + $formatter = $this->createMock(BookmarkFormatter::class); + + $formatter + ->expects(static::exactly(3)) + ->method('format') + ->withConsecutive(...array_map(function (Bookmark $bookmark): array { + return [$bookmark]; + }, $bookmarks)) + ->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array { + return ['formatted' => $bookmark]; + }, $bookmarks)) + ; + + return $formatter; + }) + ; + + // Make sure that PluginManager hook is triggered + $this->container->pluginManager + ->expects(static::exactly(3)) + ->method('executeHooks') + ->with('delete_link') + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(302, $result->getStatusCode()); + static::assertSame(['/subfolder/'], $result->getHeader('location')); + } + + /** + * Delete bookmark - Single bookmark not found in the data store + */ + public function testDeleteSingleBookmarkNotFound(): void + { + $parameters = ['id' => '123']; + + $request = $this->createMock(Request::class); + $request + ->method('getParam') + ->willReturnCallback(function (string $key) use ($parameters): ?string { + return $parameters[$key] ?? null; + }) + ; + $response = new Response(); + + $this->container->bookmarkService + ->expects(static::once()) + ->method('get') + ->willThrowException(new BookmarkNotFoundException()) + ; + $this->container->bookmarkService->expects(static::never())->method('remove'); + $this->container->bookmarkService->expects(static::never())->method('save'); + $this->container->formatterFactory = $this->createMock(FormatterFactory::class); + $this->container->formatterFactory + ->expects(static::once()) + ->method('getFormatter') + ->with('raw') + ->willReturnCallback(function (): BookmarkFormatter { + $formatter = $this->createMock(BookmarkFormatter::class); + + $formatter->expects(static::never())->method('format'); + + return $formatter; + }) + ; + // Make sure that PluginManager hook is not triggered + $this->container->pluginManager + ->expects(static::never()) + ->method('executeHooks') + ->with('delete_link') + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(302, $result->getStatusCode()); + static::assertSame(['/subfolder/'], $result->getHeader('location')); + } + + /** + * Delete bookmark - Multiple bookmarks with one not found in the data store + */ + public function testDeleteMultipleBookmarksOneNotFound(): void + { + $parameters = ['id' => '123 456 789']; + + $request = $this->createMock(Request::class); + $request + ->method('getParam') + ->willReturnCallback(function (string $key) use ($parameters): ?string { + return $parameters[$key] ?? null; + }) + ; + $response = new Response(); + + $bookmarks = [ + (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'), + (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'), + ]; + + $this->container->bookmarkService + ->expects(static::exactly(3)) + ->method('get') + ->withConsecutive([123], [456], [789]) + ->willReturnCallback(function (int $id) use ($bookmarks): Bookmark { + if ($id === 123) { + return $bookmarks[0]; + } + if ($id === 789) { + return $bookmarks[1]; + } + throw new BookmarkNotFoundException(); + }) + ; + $this->container->bookmarkService + ->expects(static::exactly(2)) + ->method('remove') + ->withConsecutive(...array_map(function (Bookmark $bookmark): array { + return [$bookmark, false]; + }, $bookmarks)) + ; + $this->container->bookmarkService->expects(static::once())->method('save'); + $this->container->formatterFactory = $this->createMock(FormatterFactory::class); + $this->container->formatterFactory + ->expects(static::once()) + ->method('getFormatter') + ->with('raw') + ->willReturnCallback(function () use ($bookmarks): BookmarkFormatter { + $formatter = $this->createMock(BookmarkFormatter::class); + + $formatter + ->expects(static::exactly(2)) + ->method('format') + ->withConsecutive(...array_map(function (Bookmark $bookmark): array { + return [$bookmark]; + }, $bookmarks)) + ->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array { + return ['formatted' => $bookmark]; + }, $bookmarks)) + ; + + return $formatter; + }) + ; + + // Make sure that PluginManager hook is not triggered + $this->container->pluginManager + ->expects(static::exactly(2)) + ->method('executeHooks') + ->with('delete_link') + ; + + $this->container->sessionManager + ->expects(static::once()) + ->method('setSessionParameter') + ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.']) + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(302, $result->getStatusCode()); + static::assertSame(['/subfolder/'], $result->getHeader('location')); + } + + /** + * Delete bookmark - Invalid ID + */ + public function testDeleteInvalidId(): void + { + $parameters = ['id' => 'nope not an ID']; + + $request = $this->createMock(Request::class); + $request + ->method('getParam') + ->willReturnCallback(function (string $key) use ($parameters): ?string { + return $parameters[$key] ?? null; + }) + ; + $response = new Response(); + + $this->container->sessionManager + ->expects(static::once()) + ->method('setSessionParameter') + ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(302, $result->getStatusCode()); + static::assertSame(['/subfolder/'], $result->getHeader('location')); + } + + /** + * Delete bookmark - Empty ID + */ + public function testDeleteEmptyId(): void + { + $request = $this->createMock(Request::class); + $response = new Response(); + + $this->container->sessionManager + ->expects(static::once()) + ->method('setSessionParameter') + ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.']) + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(302, $result->getStatusCode()); + static::assertSame(['/subfolder/'], $result->getHeader('location')); + } + + /** + * Delete bookmark - from bookmarklet + */ + public function testDeleteBookmarkFromBookmarklet(): void + { + $parameters = [ + 'id' => '123', + 'source' => 'bookmarklet', + ]; + + $request = $this->createMock(Request::class); + $request + ->method('getParam') + ->willReturnCallback(function (string $key) use ($parameters): ?string { + return $parameters[$key] ?? null; + }) + ; + $response = new Response(); + + $this->container->bookmarkService->method('get')->with('123')->willReturn( + (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123') + ); + + $this->container->formatterFactory = $this->createMock(FormatterFactory::class); + $this->container->formatterFactory + ->expects(static::once()) + ->method('getFormatter') + ->willReturnCallback(function (): BookmarkFormatter { + $formatter = $this->createMock(BookmarkFormatter::class); + $formatter->method('format')->willReturn(['formatted']); + + return $formatter; + }) + ; + + $result = $this->controller->deleteBookmark($request, $response); + + static::assertSame(200, $result->getStatusCode()); + static::assertSame('', (string) $result->getBody('location')); + } +} -- cgit v1.2.3 From 330ac859fb13a3a15875f185a611bfaa6c5f5587 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Thu, 5 Nov 2020 16:14:22 +0100 Subject: Fix: redirect to referrer after bookmark deletion Except if the referer points to a permalink (which has been deleted). Fixes #1622 --- .../admin/ShaareManageControllerTest/DeleteBookmarkTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php') diff --git a/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php index 770a16d7..a276d988 100644 --- a/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php +++ b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php @@ -38,6 +38,8 @@ class DeleteBookmarkTest extends TestCase { $parameters = ['id' => '123']; + $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/shaare/abcdef'; + $request = $this->createMock(Request::class); $request ->method('getParam') @@ -90,6 +92,8 @@ class DeleteBookmarkTest extends TestCase { $parameters = ['id' => '123 456 789']; + $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/?searchtags=abcdef'; + $request = $this->createMock(Request::class); $request ->method('getParam') @@ -152,7 +156,7 @@ class DeleteBookmarkTest extends TestCase $result = $this->controller->deleteBookmark($request, $response); static::assertSame(302, $result->getStatusCode()); - static::assertSame(['/subfolder/'], $result->getHeader('location')); + static::assertSame(['/subfolder/?searchtags=abcdef'], $result->getHeader('location')); } /** -- cgit v1.2.3