From bc6e19b2a1ef424d6901cf07c179e445deede08e Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Tue, 15 Aug 2023 20:08:27 +0100 Subject: [PATCH] Notifications: Added testing to cover controls --- app/Activity/Controllers/WatchController.php | 1 + database/seeders/DummyContentSeeder.php | 2 + tests/Activity/WatchTest.php | 152 +++++++++++++++++++ tests/User/UserPreferencesTest.php | 21 ++- 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 tests/Activity/WatchTest.php diff --git a/app/Activity/Controllers/WatchController.php b/app/Activity/Controllers/WatchController.php index e0596864c..e2e27ca4a 100644 --- a/app/Activity/Controllers/WatchController.php +++ b/app/Activity/Controllers/WatchController.php @@ -15,6 +15,7 @@ class WatchController extends Controller { public function update(Request $request) { + // TODO - Require notification permission $requestData = $this->validate($request, [ 'level' => ['required', 'string'], ]); diff --git a/database/seeders/DummyContentSeeder.php b/database/seeders/DummyContentSeeder.php index 0f030d671..f0d3ffcdb 100644 --- a/database/seeders/DummyContentSeeder.php +++ b/database/seeders/DummyContentSeeder.php @@ -27,6 +27,8 @@ class DummyContentSeeder extends Seeder // Create an editor user $editorUser = User::factory()->create(); $editorRole = Role::getRole('editor'); + $additionalEditorPerms = ['receive-notifications']; + $editorRole->permissions()->syncWithoutDetaching(RolePermission::whereIn('name', $additionalEditorPerms)->pluck('id')); $editorUser->attachRole($editorRole); // Create a viewer user diff --git a/tests/Activity/WatchTest.php b/tests/Activity/WatchTest.php new file mode 100644 index 000000000..919e52608 --- /dev/null +++ b/tests/Activity/WatchTest.php @@ -0,0 +1,152 @@ +users->editor(); + $this->actingAs($editor); + + $entities = [$this->entities->book(), $this->entities->chapter(), $this->entities->page()]; + /** @var Entity $entity */ + foreach ($entities as $entity) { + $resp = $this->get($entity->getUrl()); + $this->withHtml($resp)->assertElementContains('form[action$="/watching/update"] button.icon-list-item', 'Watch'); + + $watchOptions = new UserEntityWatchOptions($editor, $entity); + $watchOptions->updateWatchLevel('comments'); + + $resp = $this->get($entity->getUrl()); + $this->withHtml($resp)->assertElementNotExists('form[action$="/watching/update"] button.icon-list-item'); + } + } + + public function test_watch_action_only_shows_with_permission() + { + $viewer = $this->users->viewer(); + $this->actingAs($viewer); + + $entities = [$this->entities->book(), $this->entities->chapter(), $this->entities->page()]; + /** @var Entity $entity */ + foreach ($entities as $entity) { + $resp = $this->get($entity->getUrl()); + $this->withHtml($resp)->assertElementNotExists('form[action$="/watching/update"] button.icon-list-item'); + } + + $this->permissions->grantUserRolePermissions($viewer, ['receive-notifications']); + + /** @var Entity $entity */ + foreach ($entities as $entity) { + $resp = $this->get($entity->getUrl()); + $this->withHtml($resp)->assertElementExists('form[action$="/watching/update"] button.icon-list-item'); + } + } + + public function test_watch_update() + { + $editor = $this->users->editor(); + $book = $this->entities->book(); + + $this->actingAs($editor)->get($book->getUrl()); + $resp = $this->put('/watching/update', [ + 'type' => get_class($book), + 'id' => $book->id, + 'level' => 'comments' + ]); + + $resp->assertRedirect($book->getUrl()); + $this->assertSessionHas('success'); + $this->assertDatabaseHas('watches', [ + 'watchable_id' => $book->id, + 'watchable_type' => $book->getMorphClass(), + 'user_id' => $editor->id, + 'level' => WatchLevels::COMMENTS, + ]); + + $resp = $this->put('/watching/update', [ + 'type' => get_class($book), + 'id' => $book->id, + 'level' => 'default' + ]); + $resp->assertRedirect($book->getUrl()); + $this->assertDatabaseMissing('watches', [ + 'watchable_id' => $book->id, + 'watchable_type' => $book->getMorphClass(), + 'user_id' => $editor->id, + ]); + } + + public function test_watch_detail_display_reflects_state() + { + $editor = $this->users->editor(); + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + $page = $chapter->pages()->first(); + + (new UserEntityWatchOptions($editor, $book))->updateWatchLevel('updates'); + + $this->actingAs($editor)->get($book->getUrl())->assertSee('Watching new pages and updates'); + $this->get($chapter->getUrl())->assertSee('Watching via parent book'); + $this->get($page->getUrl())->assertSee('Watching via parent book'); + + (new UserEntityWatchOptions($editor, $chapter))->updateWatchLevel('comments'); + $this->get($chapter->getUrl())->assertSee('Watching new pages, updates & comments'); + $this->get($page->getUrl())->assertSee('Watching via parent chapter'); + + (new UserEntityWatchOptions($editor, $page))->updateWatchLevel('updates'); + $this->get($page->getUrl())->assertSee('Watching new pages and updates'); + } + + public function test_watch_detail_ignore_indicator_cascades() + { + $editor = $this->users->editor(); + $book = $this->entities->bookHasChaptersAndPages(); + (new UserEntityWatchOptions($editor, $book))->updateWatchLevel('ignore'); + + $this->actingAs($editor)->get($book->getUrl())->assertSee('Ignoring notifications'); + $this->get($book->chapters()->first()->getUrl())->assertSee('Ignoring via parent book'); + $this->get($book->pages()->first()->getUrl())->assertSee('Ignoring via parent book'); + } + + public function test_watch_option_menu_shows_current_active_state() + { + $editor = $this->users->editor(); + $book = $this->entities->book(); + $options = new UserEntityWatchOptions($editor, $book); + + $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl())); + $respHtml->assertElementNotExists('form[action$="/watching/update"] svg[data-icon="check-circle"]'); + + $options->updateWatchLevel('comments'); + $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl())); + $respHtml->assertElementExists('form[action$="/watching/update"] button[value="comments"] svg[data-icon="check-circle"]'); + + $options->updateWatchLevel('ignore'); + $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl())); + $respHtml->assertElementExists('form[action$="/watching/update"] button[value="ignore"] svg[data-icon="check-circle"]'); + } + + public function test_watch_option_menu_limits_options_for_pages() + { + $editor = $this->users->editor(); + $book = $this->entities->bookHasChaptersAndPages(); + (new UserEntityWatchOptions($editor, $book))->updateWatchLevel('ignore'); + + $respHtml = $this->withHtml($this->actingAs($editor)->get($book->getUrl())); + $respHtml->assertElementExists('form[action$="/watching/update"] button[name="level"][value="new"]'); + + $respHtml = $this->withHtml($this->get($book->pages()->first()->getUrl())); + $respHtml->assertElementExists('form[action$="/watching/update"] button[name="level"][value="updates"]'); + $respHtml->assertElementNotExists('form[action$="/watching/update"] button[name="level"][value="new"]'); + } + + // TODO - Guest user cannot see/set notifications + // TODO - Actual notification testing +} diff --git a/tests/User/UserPreferencesTest.php b/tests/User/UserPreferencesTest.php index a30484bd2..a0e7e063f 100644 --- a/tests/User/UserPreferencesTest.php +++ b/tests/User/UserPreferencesTest.php @@ -2,6 +2,7 @@ namespace Tests\User; +use BookStack\Activity\Tools\UserEntityWatchOptions; use Tests\TestCase; class UserPreferencesTest extends TestCase @@ -79,7 +80,6 @@ class UserPreferencesTest extends TestCase public function test_notification_preferences_updating() { $editor = $this->users->editor(); - $this->permissions->grantUserRolePermissions($editor, ['receive-notifications']); // View preferences with defaults $resp = $this->actingAs($editor)->get('/preferences/notifications'); @@ -102,6 +102,25 @@ class UserPreferencesTest extends TestCase $html->assertFieldHasValue('preferences[comment-replies]', 'true'); } + public function test_notification_preferences_show_watches() + { + $editor = $this->users->editor(); + $book = $this->entities->book(); + + $options = new UserEntityWatchOptions($editor, $book); + $options->updateWatchLevel('comments'); + + $resp = $this->actingAs($editor)->get('/preferences/notifications'); + $resp->assertSee($book->name); + $resp->assertSee('All Page Updates & Comments'); + + $options->updateWatchLevel('default'); + + $resp = $this->actingAs($editor)->get('/preferences/notifications'); + $resp->assertDontSee($book->name); + $resp->assertDontSee('All Page Updates & Comments'); + } + public function test_update_sort_preference() { $editor = $this->users->editor();