3 namespace Tests\Permissions;
5 use BookStack\Entities\Models\Book;
6 use BookStack\Entities\Models\Bookshelf;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Entity;
9 use BookStack\Entities\Models\Page;
10 use BookStack\Permissions\Permission;
11 use BookStack\Users\Models\Role;
12 use BookStack\Users\Models\User;
14 use Illuminate\Support\Str;
17 class EntityPermissionsTest extends TestCase
20 protected User $viewer;
22 protected function setUp(): void
25 $this->user = $this->users->editor();
26 $this->viewer = $this->users->viewer();
29 protected function setRestrictionsForTestRoles(Entity $entity, array $actions = []): void
32 $this->user->roles->first(),
33 $this->viewer->roles->first(),
35 $this->permissions->setEntityPermissions($entity, $actions, $roles);
38 public function test_bookshelf_view_restriction()
40 $shelf = $this->entities->shelf();
42 $this->actingAs($this->user)
43 ->get($shelf->getUrl())
46 $this->setRestrictionsForTestRoles($shelf, []);
48 $this->followingRedirects()->get($shelf->getUrl())
49 ->assertSee('Shelf not found');
51 $this->setRestrictionsForTestRoles($shelf, ['view']);
53 $this->get($shelf->getUrl())
54 ->assertSee($shelf->name);
57 public function test_bookshelf_update_restriction()
59 $shelf = $this->entities->shelf();
61 $this->actingAs($this->user)
62 ->get($shelf->getUrl('/edit'))
63 ->assertSee('Edit Shelf');
65 $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
67 $resp = $this->get($shelf->getUrl('/edit'))
68 ->assertRedirect('/');
69 $this->followRedirects($resp)->assertSee('You do not have permission');
71 $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
73 $this->get($shelf->getUrl('/edit'))
77 public function test_bookshelf_delete_restriction()
79 $shelf = $this->entities->shelf();
81 $this->actingAs($this->user)
82 ->get($shelf->getUrl('/delete'))
83 ->assertSee('Delete Shelf');
85 $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
87 $this->get($shelf->getUrl('/delete'))->assertRedirect('/');
88 $this->get('/')->assertSee('You do not have permission');
90 $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
92 $this->get($shelf->getUrl('/delete'))
94 ->assertSee('Delete Shelf');
97 public function test_book_view_restriction()
99 $book = $this->entities->book();
100 $bookPage = $book->pages->first();
101 $bookChapter = $book->chapters->first();
103 $bookUrl = $book->getUrl();
104 $this->actingAs($this->user)
108 $this->setRestrictionsForTestRoles($book, []);
110 $this->followingRedirects()->get($bookUrl)
111 ->assertSee('Book not found');
112 $this->followingRedirects()->get($bookPage->getUrl())
113 ->assertSee('Page not found');
114 $this->followingRedirects()->get($bookChapter->getUrl())
115 ->assertSee('Chapter not found');
117 $this->setRestrictionsForTestRoles($book, ['view']);
120 ->assertSee($book->name);
121 $this->get($bookPage->getUrl())
122 ->assertSee($bookPage->name);
123 $this->get($bookChapter->getUrl())
124 ->assertSee($bookChapter->name);
127 public function test_book_create_restriction()
129 $book = $this->entities->book();
131 $bookUrl = $book->getUrl();
132 $resp = $this->actingAs($this->viewer)->get($bookUrl);
133 $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
134 ->assertElementNotContains('.actions', 'New Chapter');
135 $resp = $this->actingAs($this->user)->get($bookUrl);
136 $this->withHtml($resp)->assertElementContains('.actions', 'New Page')
137 ->assertElementContains('.actions', 'New Chapter');
139 $this->setRestrictionsForTestRoles($book, ['view', 'delete', 'update']);
141 $this->get($bookUrl . '/create-chapter')->assertRedirect('/');
142 $this->get('/')->assertSee('You do not have permission');
144 $this->get($bookUrl . '/create-page')->assertRedirect('/');
145 $this->get('/')->assertSee('You do not have permission');
147 $resp = $this->get($bookUrl);
148 $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
149 ->assertElementNotContains('.actions', 'New Chapter');
151 $this->setRestrictionsForTestRoles($book, ['view', 'create']);
153 $resp = $this->post($book->getUrl('/create-chapter'), [
154 'name' => 'test chapter',
155 'description' => 'desc',
157 $resp->assertRedirect($book->getUrl('/chapter/test-chapter'));
159 $this->get($book->getUrl('/create-page'));
160 /** @var Page $page */
161 $page = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first();
162 $resp = $this->post($page->getUrl(), [
163 'name' => 'test page',
164 'html' => 'test content',
166 $resp->assertRedirect($book->getUrl('/page/test-page'));
168 $resp = $this->get($bookUrl);
169 $this->withHtml($resp)->assertElementContains('.actions', 'New Page')
170 ->assertElementContains('.actions', 'New Chapter');
173 public function test_book_update_restriction()
175 $book = $this->entities->book();
176 $bookPage = $book->pages->first();
177 $bookChapter = $book->chapters->first();
179 $bookUrl = $book->getUrl();
180 $this->actingAs($this->user)
181 ->get($bookUrl . '/edit')
182 ->assertSee('Edit Book');
184 $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
186 $this->get($bookUrl . '/edit')->assertRedirect('/');
187 $this->get('/')->assertSee('You do not have permission');
188 $this->get($bookPage->getUrl() . '/edit')->assertRedirect($bookPage->getUrl());
189 $this->get('/')->assertSee('You do not have permission');
190 $this->get($bookChapter->getUrl() . '/edit')->assertRedirect('/');
191 $this->get('/')->assertSee('You do not have permission');
193 $this->setRestrictionsForTestRoles($book, ['view', 'update']);
195 $this->get($bookUrl . '/edit')->assertOk();
196 $this->get($bookPage->getUrl() . '/edit')->assertOk();
197 $this->get($bookChapter->getUrl() . '/edit')->assertSee('Edit Chapter');
200 public function test_book_delete_restriction()
202 $book = $this->entities->book();
203 $bookPage = $book->pages->first();
204 $bookChapter = $book->chapters->first();
206 $bookUrl = $book->getUrl();
207 $this->actingAs($this->user)->get($bookUrl . '/delete')
208 ->assertSee('Delete Book');
210 $this->setRestrictionsForTestRoles($book, ['view', 'update']);
212 $this->get($bookUrl . '/delete')->assertRedirect('/');
213 $this->get('/')->assertSee('You do not have permission');
214 $this->get($bookPage->getUrl() . '/delete')->assertRedirect('/');
215 $this->get('/')->assertSee('You do not have permission');
216 $this->get($bookChapter->getUrl() . '/delete')->assertRedirect('/');
217 $this->get('/')->assertSee('You do not have permission');
219 $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
221 $this->get($bookUrl . '/delete')->assertOk()->assertSee('Delete Book');
222 $this->get($bookPage->getUrl('/delete'))->assertOk()->assertSee('Delete Page');
223 $this->get($bookChapter->getUrl('/delete'))->assertSee('Delete Chapter');
226 public function test_chapter_view_restriction()
228 $chapter = $this->entities->chapter();
229 $chapterPage = $chapter->pages->first();
231 $chapterUrl = $chapter->getUrl();
232 $this->actingAs($this->user)->get($chapterUrl)->assertOk();
234 $this->setRestrictionsForTestRoles($chapter, []);
236 $this->followingRedirects()->get($chapterUrl)->assertSee('Chapter not found');
237 $this->followingRedirects()->get($chapterPage->getUrl())->assertSee('Page not found');
239 $this->setRestrictionsForTestRoles($chapter, ['view']);
241 $this->get($chapterUrl)->assertSee($chapter->name);
242 $this->get($chapterPage->getUrl())->assertSee($chapterPage->name);
245 public function test_chapter_create_restriction()
247 $chapter = $this->entities->chapter();
249 $chapterUrl = $chapter->getUrl();
250 $resp = $this->actingAs($this->user)->get($chapterUrl);
251 $this->withHtml($resp)->assertElementContains('.actions', 'New Page');
253 $this->setRestrictionsForTestRoles($chapter, ['view', 'delete', 'update']);
255 $this->get($chapterUrl . '/create-page')->assertRedirect('/');
256 $this->get('/')->assertSee('You do not have permission');
257 $this->withHtml($this->get($chapterUrl))->assertElementNotContains('.actions', 'New Page');
259 $this->setRestrictionsForTestRoles($chapter, ['view', 'create']);
261 $this->get($chapter->getUrl('/create-page'));
262 /** @var Page $page */
263 $page = Page::query()->where('draft', '=', true)->orderBy('id', 'desc')->first();
264 $resp = $this->post($page->getUrl(), [
265 'name' => 'test page',
266 'html' => 'test content',
268 $resp->assertRedirect($chapter->book->getUrl('/page/test-page'));
270 $this->withHtml($this->get($chapterUrl))->assertElementContains('.actions', 'New Page');
273 public function test_chapter_update_restriction()
275 $chapter = $this->entities->chapter();
276 $chapterPage = $chapter->pages->first();
278 $chapterUrl = $chapter->getUrl();
279 $this->actingAs($this->user)->get($chapterUrl . '/edit')
280 ->assertSee('Edit Chapter');
282 $this->setRestrictionsForTestRoles($chapter, ['view', 'delete']);
284 $this->get($chapterUrl . '/edit')->assertRedirect('/');
285 $this->get('/')->assertSee('You do not have permission');
286 $this->get($chapterPage->getUrl() . '/edit')->assertRedirect($chapterPage->getUrl());
287 $this->get('/')->assertSee('You do not have permission');
289 $this->setRestrictionsForTestRoles($chapter, ['view', 'update']);
291 $this->get($chapterUrl . '/edit')->assertOk()->assertSee('Edit Chapter');
292 $this->get($chapterPage->getUrl() . '/edit')->assertOk();
295 public function test_chapter_delete_restriction()
297 $chapter = $this->entities->chapter();
298 $chapterPage = $chapter->pages->first();
300 $chapterUrl = $chapter->getUrl();
301 $this->actingAs($this->user)
302 ->get($chapterUrl . '/delete')
303 ->assertSee('Delete Chapter');
305 $this->setRestrictionsForTestRoles($chapter, ['view', 'update']);
307 $this->get($chapterUrl . '/delete')->assertRedirect('/');
308 $this->get('/')->assertSee('You do not have permission');
309 $this->get($chapterPage->getUrl() . '/delete')->assertRedirect('/');
310 $this->get('/')->assertSee('You do not have permission');
312 $this->setRestrictionsForTestRoles($chapter, ['view', 'delete']);
314 $this->get($chapterUrl . '/delete')->assertOk()->assertSee('Delete Chapter');
315 $this->get($chapterPage->getUrl() . '/delete')->assertOk()->assertSee('Delete Page');
318 public function test_page_view_restriction()
320 $page = $this->entities->page();
322 $pageUrl = $page->getUrl();
323 $this->actingAs($this->user)->get($pageUrl)->assertOk();
325 $this->setRestrictionsForTestRoles($page, ['update', 'delete']);
327 $this->get($pageUrl)->assertSee('Page not found');
329 $this->setRestrictionsForTestRoles($page, ['view']);
331 $this->get($pageUrl)->assertSee($page->name);
334 public function test_page_update_restriction()
336 $page = $this->entities->page();
338 $pageUrl = $page->getUrl();
339 $resp = $this->actingAs($this->user)
340 ->get($pageUrl . '/edit');
341 $this->withHtml($resp)->assertElementExists('input[name="name"][value="' . $page->name . '"]');
343 $this->setRestrictionsForTestRoles($page, ['view', 'delete']);
345 $this->get($pageUrl . '/edit')->assertRedirect($pageUrl);
346 $this->get('/')->assertSee('You do not have permission');
348 $this->setRestrictionsForTestRoles($page, ['view', 'update']);
350 $resp = $this->get($pageUrl . '/edit')
352 $this->withHtml($resp)->assertElementExists('input[name="name"][value="' . $page->name . '"]');
355 public function test_page_delete_restriction()
357 $page = $this->entities->page();
359 $pageUrl = $page->getUrl();
360 $this->actingAs($this->user)
361 ->get($pageUrl . '/delete')
362 ->assertSee('Delete Page');
364 $this->setRestrictionsForTestRoles($page, ['view', 'update']);
366 $this->get($pageUrl . '/delete')->assertRedirect('/');
367 $this->get('/')->assertSee('You do not have permission');
369 $this->setRestrictionsForTestRoles($page, ['view', 'delete']);
371 $this->get($pageUrl . '/delete')->assertOk()->assertSee('Delete Page');
374 protected function entityRestrictionFormTest(string $model, string $title, string $permission, string $roleId)
376 /** @var Entity $modelInstance */
377 $modelInstance = $model::query()->first();
378 $this->asAdmin()->get($modelInstance->getUrl('/permissions'))
381 $this->put($modelInstance->getUrl('/permissions'), [
384 $permission => 'true',
389 $this->assertDatabaseHas('entity_permissions', [
390 'entity_id' => $modelInstance->id,
391 'entity_type' => $modelInstance->getMorphClass(),
392 'role_id' => $roleId,
397 public function test_bookshelf_restriction_form()
399 $this->entityRestrictionFormTest(Bookshelf::class, 'Shelf Permissions', 'view', '2');
402 public function test_book_restriction_form()
404 $this->entityRestrictionFormTest(Book::class, 'Book Permissions', 'view', '2');
407 public function test_chapter_restriction_form()
409 $this->entityRestrictionFormTest(Chapter::class, 'Chapter Permissions', 'update', '2');
412 public function test_page_restriction_form()
414 $this->entityRestrictionFormTest(Page::class, 'Page Permissions', 'delete', '2');
417 public function test_shelf_create_permission_visible_with_notice()
419 $shelf = $this->entities->shelf();
421 $resp = $this->asAdmin()->get($shelf->getUrl('/permissions'));
422 $html = $this->withHtml($resp);
423 $html->assertElementExists('input[name$="[create]"]');
424 $resp->assertSee('Shelf create permissions are only used for copying permissions to child books using the action below.');
427 public function test_restricted_pages_not_visible_in_book_navigation_on_pages()
429 $chapter = $this->entities->chapter();
430 $page = $chapter->pages->first();
431 $page2 = $chapter->pages[2];
433 $this->setRestrictionsForTestRoles($page, []);
435 $resp = $this->actingAs($this->user)->get($page2->getUrl());
436 $this->withHtml($resp)->assertElementNotContains('.sidebar-page-list', $page->name);
439 public function test_restricted_pages_not_visible_in_book_navigation_on_chapters()
441 $chapter = $this->entities->chapter();
442 $page = $chapter->pages->first();
444 $this->setRestrictionsForTestRoles($page, []);
446 $resp = $this->actingAs($this->user)->get($chapter->getUrl());
447 $this->withHtml($resp)->assertElementNotContains('.sidebar-page-list', $page->name);
450 public function test_restricted_pages_not_visible_on_chapter_pages()
452 $chapter = $this->entities->chapter();
453 $page = $chapter->pages->first();
455 $this->setRestrictionsForTestRoles($page, []);
457 $this->actingAs($this->user)
458 ->get($chapter->getUrl())
459 ->assertDontSee($page->name);
462 public function test_restricted_chapter_pages_not_visible_on_book_page()
464 $chapter = $this->entities->chapter();
465 $this->actingAs($this->user)
466 ->get($chapter->book->getUrl())
467 ->assertSee($chapter->pages->first()->name);
469 foreach ($chapter->pages as $page) {
470 $this->setRestrictionsForTestRoles($page, []);
473 $this->actingAs($this->user)
474 ->get($chapter->book->getUrl())
475 ->assertDontSee($chapter->pages->first()->name);
478 public function test_bookshelf_update_restriction_override()
480 $shelf = $this->entities->shelf();
482 $this->actingAs($this->viewer)
483 ->get($shelf->getUrl('/edit'))
484 ->assertDontSee('Edit Book');
486 $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
488 $this->get($shelf->getUrl('/edit'))->assertRedirect('/');
489 $this->get('/')->assertSee('You do not have permission');
491 $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
493 $this->get($shelf->getUrl('/edit'))->assertOk();
496 public function test_bookshelf_delete_restriction_override()
498 $shelf = $this->entities->shelf();
500 $this->actingAs($this->viewer)
501 ->get($shelf->getUrl('/delete'))
502 ->assertDontSee('Delete Book');
504 $this->setRestrictionsForTestRoles($shelf, ['view', 'update']);
506 $this->get($shelf->getUrl('/delete'))->assertRedirect('/');
507 $this->get('/')->assertSee('You do not have permission');
509 $this->setRestrictionsForTestRoles($shelf, ['view', 'delete']);
511 $this->get($shelf->getUrl('/delete'))->assertOk()->assertSee('Delete Shelf');
514 public function test_book_create_restriction_override()
516 $book = $this->entities->book();
518 $bookUrl = $book->getUrl();
519 $resp = $this->actingAs($this->viewer)->get($bookUrl);
520 $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
521 ->assertElementNotContains('.actions', 'New Chapter');
523 $this->setRestrictionsForTestRoles($book, ['view', 'delete', 'update']);
525 $this->get($bookUrl . '/create-chapter')->assertRedirect('/');
526 $this->get('/')->assertSee('You do not have permission');
527 $this->get($bookUrl . '/create-page')->assertRedirect('/');
528 $this->get('/')->assertSee('You do not have permission');
529 $resp = $this->get($bookUrl);
530 $this->withHtml($resp)->assertElementNotContains('.actions', 'New Page')
531 ->assertElementNotContains('.actions', 'New Chapter');
533 $this->setRestrictionsForTestRoles($book, ['view', 'create']);
535 $resp = $this->post($book->getUrl('/create-chapter'), [
536 'name' => 'test chapter',
537 'description' => 'test desc',
539 $resp->assertRedirect($book->getUrl('/chapter/test-chapter'));
541 $this->get($book->getUrl('/create-page'));
542 /** @var Page $page */
543 $page = Page::query()->where('draft', '=', true)->orderByDesc('id')->first();
544 $resp = $this->post($page->getUrl(), [
545 'name' => 'test page',
546 'html' => 'test desc',
548 $resp->assertRedirect($book->getUrl('/page/test-page'));
550 $resp = $this->get($bookUrl);
551 $this->withHtml($resp)->assertElementContains('.actions', 'New Page')
552 ->assertElementContains('.actions', 'New Chapter');
555 public function test_book_update_restriction_override()
557 $book = $this->entities->book();
558 $bookPage = $book->pages->first();
559 $bookChapter = $book->chapters->first();
561 $bookUrl = $book->getUrl();
562 $this->actingAs($this->viewer)->get($bookUrl . '/edit')
563 ->assertDontSee('Edit Book');
565 $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
567 $this->get($bookUrl . '/edit')->assertRedirect('/');
568 $this->get('/')->assertSee('You do not have permission');
569 $this->get($bookPage->getUrl() . '/edit')->assertRedirect($bookPage->getUrl());
570 $this->get('/')->assertSee('You do not have permission');
571 $this->get($bookChapter->getUrl() . '/edit')->assertRedirect('/');
572 $this->get('/')->assertSee('You do not have permission');
574 $this->setRestrictionsForTestRoles($book, ['view', 'update']);
576 $this->get($bookUrl . '/edit')->assertOk();
577 $this->get($bookPage->getUrl() . '/edit')->assertOk();
578 $this->get($bookChapter->getUrl() . '/edit')->assertSee('Edit Chapter');
581 public function test_book_delete_restriction_override()
583 $book = $this->entities->book();
584 $bookPage = $book->pages->first();
585 $bookChapter = $book->chapters->first();
587 $bookUrl = $book->getUrl();
588 $this->actingAs($this->viewer)
589 ->get($bookUrl . '/delete')
590 ->assertDontSee('Delete Book');
592 $this->setRestrictionsForTestRoles($book, ['view', 'update']);
594 $this->get($bookUrl . '/delete')->assertRedirect('/');
595 $this->get('/')->assertSee('You do not have permission');
596 $this->get($bookPage->getUrl() . '/delete')->assertRedirect('/');
597 $this->get('/')->assertSee('You do not have permission');
598 $this->get($bookChapter->getUrl() . '/delete')->assertRedirect('/');
599 $this->get('/')->assertSee('You do not have permission');
601 $this->setRestrictionsForTestRoles($book, ['view', 'delete']);
603 $this->get($bookUrl . '/delete')->assertOk()->assertSee('Delete Book');
604 $this->get($bookPage->getUrl() . '/delete')->assertOk()->assertSee('Delete Page');
605 $this->get($bookChapter->getUrl() . '/delete')->assertSee('Delete Chapter');
608 public function test_page_visible_if_has_permissions_when_book_not_visible()
610 $book = $this->entities->book();
611 $bookChapter = $book->chapters->first();
612 $bookPage = $bookChapter->pages->first();
614 foreach ([$book, $bookChapter, $bookPage] as $entity) {
615 $entity->name = Str::random(24);
619 $this->setRestrictionsForTestRoles($book, []);
620 $this->setRestrictionsForTestRoles($bookPage, ['view']);
622 $this->actingAs($this->viewer);
623 $resp = $this->get($bookPage->getUrl());
625 $resp->assertSee($bookPage->name);
626 $resp->assertDontSee(substr($book->name, 0, 15));
627 $resp->assertDontSee(substr($bookChapter->name, 0, 15));
630 public function test_book_sort_view_permission()
632 $firstBook = $this->entities->book();
633 $secondBook = $this->entities->book();
635 $this->setRestrictionsForTestRoles($firstBook, ['view', 'update']);
636 $this->setRestrictionsForTestRoles($secondBook, ['view']);
638 // Test sort page visibility
639 $this->actingAs($this->user)->get($secondBook->getUrl('/sort'))->assertRedirect('/');
640 $this->get('/')->assertSee('You do not have permission');
642 // Check sort page on first book
643 $this->actingAs($this->user)->get($firstBook->getUrl('/sort'));
646 public function test_can_create_page_if_chapter_has_permissions_when_book_not_visible()
648 $book = $this->entities->book();
649 $this->setRestrictionsForTestRoles($book, []);
650 $bookChapter = $book->chapters->first();
651 $this->setRestrictionsForTestRoles($bookChapter, ['view']);
653 $this->actingAs($this->user)->get($bookChapter->getUrl())
654 ->assertDontSee('New Page');
656 $this->setRestrictionsForTestRoles($bookChapter, ['view', 'create']);
658 $this->get($bookChapter->getUrl('/create-page'));
659 /** @var Page $page */
660 $page = Page::query()->where('draft', '=', true)->orderByDesc('id')->first();
661 $resp = $this->post($page->getUrl(), [
662 'name' => 'test page',
663 'html' => 'test content',
665 $resp->assertRedirect($book->getUrl('/page/test-page'));
668 public function test_access_to_item_prevented_if_inheritance_active_but_permission_prevented_via_role()
670 $user = $this->users->viewer();
671 $viewerRole = $user->roles->first();
672 $chapter = $this->entities->chapter();
673 $book = $chapter->book;
675 $this->permissions->setEntityPermissions($book, ['update'], [$viewerRole], false);
676 $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true);
678 $this->assertFalse(userCan(Permission::ChapterUpdate, $chapter));
681 public function test_access_to_item_allowed_if_inheritance_active_and_permission_prevented_via_role_but_allowed_via_parent()
683 $user = $this->users->viewer();
684 $viewerRole = $user->roles->first();
685 $editorRole = Role::getRole('Editor');
686 $user->attachRole($editorRole);
687 $chapter = $this->entities->chapter();
688 $book = $chapter->book;
690 $this->permissions->setEntityPermissions($book, ['update'], [$editorRole], false);
691 $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true);
693 $this->actingAs($user);
694 $this->assertTrue(userCan(Permission::ChapterUpdate, $chapter));
697 public function test_book_permissions_can_be_generated_without_error_if_child_chapter_is_in_recycle_bin()
699 $book = $this->entities->bookHasChaptersAndPages();
700 /** @var Chapter $chapter */
701 $chapter = $book->chapters()->first();
703 $this->asAdmin()->delete($chapter->getUrl());
707 $this->permissions->setEntityPermissions($book, ['view'], []);
708 } catch (Exception $e) {
712 $this->assertNull($error);