X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/1cd19c76ba91141c8d1877b8a7c1c58d008d9fb3..refs/pull/5676/head:/tests/Activity/WatchTest.php diff --git a/tests/Activity/WatchTest.php b/tests/Activity/WatchTest.php index 72e6b37a5..c405b07ae 100644 --- a/tests/Activity/WatchTest.php +++ b/tests/Activity/WatchTest.php @@ -13,6 +13,8 @@ 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; @@ -63,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' ]); @@ -80,7 +81,7 @@ class WatchTest extends TestCase ]); $resp = $this->put('/watching/update', [ - 'type' => get_class($book), + 'type' => $book->getMorphClass(), 'id' => $book->id, 'level' => 'default' ]); @@ -100,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' ]); @@ -197,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); } @@ -209,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); } @@ -252,7 +259,7 @@ 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) { @@ -261,6 +268,7 @@ class WatchTest extends TestCase 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'); }); @@ -278,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()), 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'); }); @@ -307,12 +316,13 @@ 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()), 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); }); } @@ -329,7 +339,10 @@ class WatchTest extends TestCase $activities = [ ActivityType::PAGE_CREATE => $entities['page'], ActivityType::PAGE_UPDATE => $entities['page'], - ActivityType::COMMENT_CREATE => (new Comment([]))->forceFill(['entity_id' => $entities['page']->id, 'entity_type' => $entities['page']->getMorphClass()]), + ActivityType::COMMENT_CREATE => Comment::factory()->make([ + 'entity_id' => $entities['page']->id, + 'entity_type' => $entities['page']->getMorphClass(), + ]), ]; $notifications = Notification::fake(); @@ -354,6 +367,29 @@ class WatchTest extends TestCase } } + 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(); @@ -365,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); + } }