X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/391478465ab5970ad19649d41a3f8c7816efb294..refs/pull/5676/head:/tests/Activity/WatchTest.php diff --git a/tests/Activity/WatchTest.php b/tests/Activity/WatchTest.php index fd86029d3..c405b07ae 100644 --- a/tests/Activity/WatchTest.php +++ b/tests/Activity/WatchTest.php @@ -2,13 +2,19 @@ namespace Tests\Activity; +use BookStack\Activity\ActivityType; +use BookStack\Activity\Models\Comment; +use BookStack\Activity\Notifications\Messages\BaseActivityNotification; use BookStack\Activity\Notifications\Messages\CommentCreationNotification; use BookStack\Activity\Notifications\Messages\PageCreationNotification; use BookStack\Activity\Notifications\Messages\PageUpdateNotification; +use BookStack\Activity\Tools\ActivityLogger; use BookStack\Activity\Tools\UserEntityWatchOptions; use BookStack\Activity\WatchLevels; use BookStack\Entities\Models\Entity; use BookStack\Settings\UserNotificationPreferences; +use Illuminate\Contracts\Notifications\Dispatcher; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Tests\TestCase; @@ -59,9 +65,8 @@ class WatchTest extends TestCase $editor = $this->users->editor(); $book = $this->entities->book(); - $this->actingAs($editor)->get($book->getUrl()); - $resp = $this->put('/watching/update', [ - 'type' => get_class($book), + $resp = $this->actingAs($editor)->put('/watching/update', [ + 'type' => $book->getMorphClass(), 'id' => $book->id, 'level' => 'comments' ]); @@ -76,7 +81,7 @@ class WatchTest extends TestCase ]); $resp = $this->put('/watching/update', [ - 'type' => get_class($book), + 'type' => $book->getMorphClass(), 'id' => $book->id, 'level' => 'default' ]); @@ -96,7 +101,7 @@ class WatchTest extends TestCase $book = $this->entities->book(); $resp = $this->put('/watching/update', [ - 'type' => get_class($book), + 'type' => $book->getMorphClass(), 'id' => $book->id, 'level' => 'comments' ]); @@ -193,7 +198,7 @@ class WatchTest extends TestCase $notifications = Notification::fake(); $this->asAdmin()->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment' + 'html' => '

My new comment

' ]); $notifications->assertSentTo($editor, CommentCreationNotification::class); } @@ -205,16 +210,22 @@ class WatchTest extends TestCase $prefs = new UserNotificationPreferences($editor); $prefs->updateFromSettingsArray(['comment-replies' => 'true']); + // Create some existing comments to pad IDs to help potentially error + // on mis-identification of parent via ids used. + Comment::factory()->count(5) + ->for($entities['page'], 'entity') + ->create(['created_by' => $this->users->admin()->id]); + $notifications = Notification::fake(); $this->actingAs($editor)->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment' + 'html' => '

My new comment

' ]); - $comment = $entities['page']->comments()->first(); + $comment = $entities['page']->comments()->orderBy('id', 'desc')->first(); $this->asAdmin()->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment response', - 'parent_id' => $comment->id, + 'html' => '

My new comment response

', + 'parent_id' => $comment->local_id, ]); $notifications->assertSentTo($editor, CommentCreationNotification::class); } @@ -248,15 +259,16 @@ class WatchTest extends TestCase // Comment post $this->actingAs($admin)->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment response', + 'html' => '

My new comment response

', ]); $notifications->assertSentTo($editor, function (CommentCreationNotification $notification) use ($editor, $admin, $entities) { $mail = $notification->toMail($editor); - $mailContent = html_entity_decode(strip_tags($mail->render())); + $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES); return $mail->subject === 'New comment on page: ' . $entities['page']->getShortName() && str_contains($mailContent, 'View Comment') && str_contains($mailContent, 'Page Name: ' . $entities['page']->name) + && str_contains($mailContent, 'Page Path: ' . $entities['book']->getShortName(24) . ' > ' . $entities['chapter']->getShortName(24)) && str_contains($mailContent, 'Commenter: ' . $admin->name) && str_contains($mailContent, 'Comment: My new comment response'); }); @@ -274,12 +286,13 @@ class WatchTest extends TestCase $this->actingAs($admin); $this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']); - $notifications->assertSentTo($editor, function (PageUpdateNotification $notification) use ($editor, $admin) { + $notifications->assertSentTo($editor, function (PageUpdateNotification $notification) use ($editor, $admin, $entities) { $mail = $notification->toMail($editor); - $mailContent = html_entity_decode(strip_tags($mail->render())); + $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES); return $mail->subject === 'Updated page: Updated page' && str_contains($mailContent, 'View Page') && str_contains($mailContent, 'Page Name: Updated page') + && str_contains($mailContent, 'Page Path: ' . $entities['book']->getShortName(24) . ' > ' . $entities['chapter']->getShortName(24)) && str_contains($mailContent, 'Updated By: ' . $admin->name) && str_contains($mailContent, 'you won\'t be sent notifications for further edits to this page by the same editor'); }); @@ -303,16 +316,80 @@ class WatchTest extends TestCase $page = $entities['chapter']->pages()->where('draft', '=', true)->first(); $this->post($page->getUrl(), ['name' => 'My new page', 'html' => 'My new page content']); - $notifications->assertSentTo($editor, function (PageCreationNotification $notification) use ($editor, $admin) { + $notifications->assertSentTo($editor, function (PageCreationNotification $notification) use ($editor, $admin, $entities) { $mail = $notification->toMail($editor); - $mailContent = html_entity_decode(strip_tags($mail->render())); + $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES); return $mail->subject === 'New page: My new page' && str_contains($mailContent, 'View Page') && str_contains($mailContent, 'Page Name: My new page') + && str_contains($mailContent, 'Page Path: ' . $entities['book']->getShortName(24) . ' > ' . $entities['chapter']->getShortName(24)) && str_contains($mailContent, 'Created By: ' . $admin->name); }); } + public function test_notifications_sent_in_right_language() + { + $editor = $this->users->editor(); + $admin = $this->users->admin(); + setting()->putUser($editor, 'language', 'de'); + $entities = $this->entities->createChainBelongingToUser($editor); + $watches = new UserEntityWatchOptions($editor, $entities['book']); + $watches->updateLevelByValue(WatchLevels::COMMENTS); + + $activities = [ + ActivityType::PAGE_CREATE => $entities['page'], + ActivityType::PAGE_UPDATE => $entities['page'], + ActivityType::COMMENT_CREATE => Comment::factory()->make([ + 'entity_id' => $entities['page']->id, + 'entity_type' => $entities['page']->getMorphClass(), + ]), + ]; + + $notifications = Notification::fake(); + $logger = app()->make(ActivityLogger::class); + $this->actingAs($admin); + + foreach ($activities as $activityType => $detail) { + $logger->add($activityType, $detail); + } + + $sent = $notifications->sentNotifications()[get_class($editor)][$editor->id]; + $this->assertCount(3, $sent); + + foreach ($sent as $notificationInfo) { + $notification = $notificationInfo[0]['notification']; + $this->assertInstanceOf(BaseActivityNotification::class, $notification); + $mail = $notification->toMail($editor); + $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES); + $this->assertStringContainsString('Name der Seite:', $mailContent); + $this->assertStringContainsString('Diese Benachrichtigung wurde', $mailContent); + $this->assertStringContainsString('Sollte es beim Anklicken der Schaltfläche', $mailContent); + } + } + + public function test_failed_notifications_dont_block_and_log_errors() + { + $logger = $this->withTestLogger(); + $editor = $this->users->editor(); + $admin = $this->users->admin(); + $page = $this->entities->page(); + $book = $page->book; + $activityLogger = app()->make(ActivityLogger::class); + + $watches = new UserEntityWatchOptions($editor, $book); + $watches->updateLevelByValue(WatchLevels::UPDATES); + + $mockDispatcher = $this->mock(Dispatcher::class); + $mockDispatcher->shouldReceive('send')->once() + ->andThrow(\Exception::class, 'Failed to connect to mail server'); + + $this->actingAs($admin); + + $activityLogger->add(ActivityType::PAGE_UPDATE, $page); + + $this->assertTrue($logger->hasErrorThatContains("Failed to send email notification to user [id:{$editor->id}] with error: Failed to connect to mail server")); + } + public function test_notifications_not_sent_if_lacking_view_permission_for_related_item() { $notifications = Notification::fake(); @@ -324,9 +401,65 @@ class WatchTest extends TestCase $this->permissions->disableEntityInheritedPermissions($page); $this->asAdmin()->post("/comment/{$page->id}", [ - 'text' => 'My new comment response', + 'html' => '

My new comment response

', ])->assertOk(); $notifications->assertNothingSentTo($editor); } + + public function test_watches_deleted_on_user_delete() + { + $editor = $this->users->editor(); + $page = $this->entities->page(); + + $watches = new UserEntityWatchOptions($editor, $page); + $watches->updateLevelByValue(WatchLevels::COMMENTS); + $this->assertDatabaseHas('watches', ['user_id' => $editor->id]); + + $this->asAdmin()->delete($editor->getEditUrl()); + + $this->assertDatabaseMissing('watches', ['user_id' => $editor->id]); + } + + public function test_watches_deleted_on_item_delete() + { + $editor = $this->users->editor(); + $page = $this->entities->page(); + + $watches = new UserEntityWatchOptions($editor, $page); + $watches->updateLevelByValue(WatchLevels::COMMENTS); + $this->assertDatabaseHas('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]); + + $this->entities->destroy($page); + + $this->assertDatabaseMissing('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]); + } + + public function test_page_path_in_notifications_limited_by_permissions() + { + $chapter = $this->entities->chapterHasPages(); + $page = $chapter->pages()->first(); + $book = $chapter->book; + $notification = new PageCreationNotification($page, $this->users->editor()); + + $viewer = $this->users->viewer(); + $viewerRole = $viewer->roles()->first(); + + $content = html_entity_decode(strip_tags($notification->toMail($viewer)->render()), ENT_QUOTES); + $this->assertStringContainsString('Page Path: ' . $book->getShortName(24) . ' > ' . $chapter->getShortName(24), $content); + + $this->permissions->setEntityPermissions($page, ['view'], [$viewerRole]); + $this->permissions->setEntityPermissions($chapter, [], [$viewerRole]); + + $content = html_entity_decode(strip_tags($notification->toMail($viewer)->render()), ENT_QUOTES); + $this->assertStringContainsString('Page Path: ' . $book->getShortName(24), $content); + $this->assertStringNotContainsString(' > ' . $chapter->getShortName(24), $content); + + $this->permissions->setEntityPermissions($book, [], [$viewerRole]); + + $content = html_entity_decode(strip_tags($notification->toMail($viewer)->render()), ENT_QUOTES); + $this->assertStringNotContainsString('Page Path:', $content); + $this->assertStringNotContainsString($book->getShortName(24), $content); + $this->assertStringNotContainsString($chapter->getShortName(24), $content); + } }