namespace BookStack\Api;
-use BookStack\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\JsonResponse;
use BookStack\Actions\ActivityLogger;
use BookStack\Actions\ActivityType;
use BookStack\Auth\UserRepo;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Entities\Tools\TrashCan;
use Carbon\Carbon;
class AuditLogTest extends TestCase
{
- /** @var ActivityLogger */
- protected $activityService;
+ protected ActivityLogger $activityService;
protected function setUp(): void
{
{
$admin = $this->getAdmin();
$this->actingAs($admin);
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->activityService->add(ActivityType::PAGE_CREATE, $page);
$activity = Activity::query()->orderBy('id', 'desc')->first();
public function test_shows_name_for_deleted_items()
{
$this->actingAs($this->getAdmin());
- $page = Page::query()->first();
+ $page = $this->entities->page();
$pageName = $page->name;
$this->activityService->add(ActivityType::PAGE_CREATE, $page);
{
$viewer = $this->getViewer();
$this->actingAs($viewer);
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->activityService->add(ActivityType::PAGE_CREATE, $page);
$this->actingAs($this->getAdmin());
public function test_filters_by_key()
{
$this->actingAs($this->getAdmin());
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->activityService->add(ActivityType::PAGE_CREATE, $page);
$resp = $this->get('settings/audit');
public function test_date_filters()
{
$this->actingAs($this->getAdmin());
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->activityService->add(ActivityType::PAGE_CREATE, $page);
$yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
$admin = $this->getAdmin();
$editor = $this->getEditor();
$this->actingAs($admin);
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->activityService->add(ActivityType::PAGE_CREATE, $page);
$this->actingAs($editor);
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$this->activityService->add(ActivityType::CHAPTER_UPDATE, $chapter);
$resp = $this->actingAs($admin)->get('settings/audit?user=' . $admin->id);
{
config()->set('app.proxies', '*');
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($editor)->put($page->getUrl(), [
'name' => 'Updated page',
{
config()->set('app.proxies', '*');
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($editor)->put($page->getUrl(), [
'name' => 'Updated page',
config()->set('app.proxies', '*');
config()->set('app.env', 'demo');
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($editor)->put($page->getUrl(), [
'name' => 'Updated page',
config()->set('app.proxies', '*');
config()->set('app.ip_address_precision', 2);
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($editor)->put($page->getUrl(), [
'name' => 'Updated page',
use BookStack\Actions\DispatchWebhookJob;
use BookStack\Actions\Webhook;
use BookStack\Auth\User;
-use BookStack\Entities\Models\Page;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Http;
'*' => Http::response('', 200),
]);
$webhook = $this->newWebhook(['active' => true, 'endpoint' => 'https://wh.example.com'], ['all']);
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$this->runEvent(ActivityType::PAGE_UPDATE, $page, $editor);
use BookStack\Actions\ActivityType;
use BookStack\Actions\Webhook;
use BookStack\Actions\WebhookFormatter;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use Illuminate\Support\Arr;
use Tests\TestCase;
public function test_entity_events_show_related_user_info()
{
$events = [
- ActivityType::BOOK_UPDATE => Book::query()->first(),
- ActivityType::CHAPTER_CREATE => Chapter::query()->first(),
- ActivityType::PAGE_MOVE => Page::query()->first(),
+ ActivityType::BOOK_UPDATE => $this->entities->book(),
+ ActivityType::CHAPTER_CREATE => $this->entities->chapter(),
+ ActivityType::PAGE_MOVE => $this->entities->page(),
];
foreach ($events as $event => $entity) {
public function test_page_create_and_update_events_show_revision_info()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor()->put($page->getUrl(), ['name' => 'Updated page', 'html' => 'new page html', 'summary' => 'Update a']);
$data = $this->getWebhookData(ActivityType::PAGE_UPDATE, $page);
public function test_index_endpoint_returns_expected_book()
{
$this->actingAsApiEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$attachment = $this->createAttachmentForPage($page, [
'name' => 'My test attachment',
'external' => true,
public function test_attachments_listing_based_upon_page_visibility()
{
$this->actingAsApiEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$attachment = $this->createAttachmentForPage($page, [
'name' => 'My test attachment',
'external' => true,
$page->restricted = true;
$page->save();
- $this->regenEntityPermissions($page);
+ $this->entities->regenPermissions($page);
$resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
$resp->assertJsonMissing(['data' => [
public function test_create_endpoint_for_link_attachment()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$details = [
'name' => 'My attachment',
public function test_create_endpoint_for_upload_attachment()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$file = $this->getTestFile('textfile.txt');
$details = [
public function test_upload_limit_restricts_attachment_uploads()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
config()->set('app.upload_limit', 1);
public function test_name_needed_to_create()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$details = [
'uploaded_to' => $page->id,
public function test_link_or_file_needed_to_create()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$details = [
'name' => 'my attachment',
public function test_message_shown_if_file_is_not_a_valid_file()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$details = [
'name' => 'my attachment',
public function test_read_endpoint_for_link_attachment()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$attachment = $this->createAttachmentForPage($page, [
'name' => 'my attachment',
public function test_read_endpoint_for_file_attachment()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$file = $this->getTestFile('textfile.txt');
$details = [
$this->actingAsApiAdmin();
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->draft = true;
$page->owned_by = $editor->id;
$page->save();
- $this->regenEntityPermissions($page);
+ $this->entities->regenPermissions($page);
$attachment = $this->createAttachmentForPage($page, [
'name' => 'my attachment',
public function test_update_endpoint()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$attachment = $this->createAttachmentForPage($page);
$details = [
public function test_update_link_attachment_to_file()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$attachment = $this->createAttachmentForPage($page);
$file = $this->getTestFile('textfile.txt');
public function test_update_file_attachment_to_link()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$file = $this->getTestFile('textfile.txt');
$this->call('POST', $this->baseEndpoint, ['name' => 'My file attachment', 'uploaded_to' => $page->id], [], ['file' => $file]);
/** @var Attachment $attachment */
public function test_delete_endpoint()
{
$this->actingAsApiAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$attachment = $this->createAttachmentForPage($page);
$resp = $this->deleteJson("{$this->baseEndpoint}/{$attachment->id}");
public function test_read_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$resp = $this->getJson($this->baseEndpoint . "/{$book->id}");
public function test_read_endpoint_includes_chapter_and_page_contents()
{
$this->actingAsApiEditor();
- /** @var Book $book */
- $book = Book::visible()->has('chapters')->has('pages')->first();
+ $book = $this->entities->bookHasChaptersAndPages();
$chapter = $book->chapters()->first();
$chapterPage = $chapter->pages()->first();
public function test_update_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$details = [
'name' => 'My updated API book',
'description' => 'A book created via the API',
public function test_update_increments_updated_date_if_only_tags_are_sent()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
DB::table('books')->where('id', '=', $book->id)->update(['updated_at' => Carbon::now()->subWeek()]);
$details = [
{
$this->actingAsApiEditor();
/** @var Book $book */
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$this->assertNull($book->cover);
$file = $this->getTestImage('image.png');
public function test_delete_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$resp = $this->deleteJson($this->baseEndpoint . "/{$book->id}");
$resp->assertStatus(204);
public function test_export_html_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$resp = $this->get($this->baseEndpoint . "/{$book->id}/export/html");
$resp->assertStatus(200);
public function test_export_plain_text_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$resp = $this->get($this->baseEndpoint . "/{$book->id}/export/plaintext");
$resp->assertStatus(200);
public function test_export_pdf_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$resp = $this->get($this->baseEndpoint . "/{$book->id}/export/pdf");
$resp->assertStatus(200);
$this->actingAsApiEditor();
$this->removePermissionFromUser($this->getEditor(), 'content-export');
- $book = Book::visible()->first();
+ $book = $this->entities->book();
foreach ($types as $type) {
$resp = $this->get($this->baseEndpoint . "/{$book->id}/export/{$type}");
$this->assertPermissionError($resp);
namespace Tests\Api;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
public function test_create_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::query()->first();
+ $book = $this->entities->book();
$details = [
'name' => 'My API chapter',
'description' => 'A chapter created via the API',
public function test_chapter_name_needed_to_create()
{
$this->actingAsApiEditor();
- $book = Book::query()->first();
+ $book = $this->entities->book();
$details = [
'book_id' => $book->id,
'description' => 'A chapter created via the API',
public function test_read_endpoint()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages()->first();
$resp = $this->getJson($this->baseEndpoint . "/{$chapter->id}");
public function test_update_endpoint()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$details = [
'name' => 'My updated API chapter',
'description' => 'A chapter created via the API',
public function test_update_increments_updated_date_if_only_tags_are_sent()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
DB::table('chapters')->where('id', '=', $chapter->id)->update(['updated_at' => Carbon::now()->subWeek()]);
$details = [
public function test_delete_endpoint()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->deleteJson($this->baseEndpoint . "/{$chapter->id}");
$resp->assertStatus(204);
public function test_export_html_endpoint()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/html");
$resp->assertStatus(200);
public function test_export_plain_text_endpoint()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/plaintext");
$resp->assertStatus(200);
public function test_export_pdf_endpoint()
{
$this->actingAsApiEditor();
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/pdf");
$resp->assertStatus(200);
namespace Tests\Api;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use Carbon\Carbon;
public function test_create_endpoint()
{
$this->actingAsApiEditor();
- $book = Book::query()->first();
+ $book = $this->entities->book();
$details = [
'name' => 'My API page',
'book_id' => $book->id,
public function test_page_name_needed_to_create()
{
$this->actingAsApiEditor();
- $book = Book::query()->first();
+ $book = $this->entities->book();
$details = [
'book_id' => $book->id,
'html' => '<p>A page created via the API</p>',
'chapter_id' => ['The chapter id field is required when book id is not present.'],
]));
- $chapter = Chapter::visible()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->postJson($this->baseEndpoint, array_merge($details, ['chapter_id' => $chapter->id]));
$resp->assertStatus(200);
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$resp = $this->postJson($this->baseEndpoint, array_merge($details, ['book_id' => $book->id]));
$resp->assertStatus(200);
}
public function test_markdown_can_be_provided_for_create()
{
$this->actingAsApiEditor();
- $book = Book::visible()->first();
+ $book = $this->entities->book();
$details = [
'book_id' => $book->id,
'name' => 'My api page',
public function test_read_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$resp = $this->getJson($this->baseEndpoint . "/{$page->id}");
$resp->assertStatus(200);
public function test_read_endpoint_provides_rendered_html()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$page->html = "<p>testing</p><script>alert('danger')</script><h1>Hello</h1>";
$page->save();
public function test_update_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$details = [
'name' => 'My updated API page',
'html' => '<p>A page created via the API</p>',
public function test_providing_new_chapter_id_on_update_will_move_page()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
$details = [
'name' => 'My updated API page',
public function test_providing_move_via_update_requires_page_create_permission_on_new_parent()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$chapter = Chapter::visible()->where('book_id', '!=', $page->book_id)->first();
- $this->setEntityRestrictions($chapter, ['view'], [$this->getEditor()->roles()->first()]);
+ $this->entities->setPermissions($chapter, ['view'], [$this->getEditor()->roles()->first()]);
$details = [
'name' => 'My updated API page',
'chapter_id' => $chapter->id,
public function test_update_endpoint_does_not_wipe_content_if_no_html_or_md_provided()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$originalContent = $page->html;
$details = [
'name' => 'My updated API page',
public function test_update_increments_updated_date_if_only_tags_are_sent()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
DB::table('pages')->where('id', '=', $page->id)->update(['updated_at' => Carbon::now()->subWeek()]);
$details = [
public function test_delete_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$resp = $this->deleteJson($this->baseEndpoint . "/{$page->id}");
$resp->assertStatus(204);
public function test_export_html_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$resp = $this->get($this->baseEndpoint . "/{$page->id}/export/html");
$resp->assertStatus(200);
public function test_export_plain_text_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$resp = $this->get($this->baseEndpoint . "/{$page->id}/export/plaintext");
$resp->assertStatus(200);
public function test_export_pdf_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$resp = $this->get($this->baseEndpoint . "/{$page->id}/export/pdf");
$resp->assertStatus(200);
public function test_export_markdown_endpoint()
{
$this->actingAsApiEditor();
- $page = Page::visible()->first();
+ $page = $this->entities->page();
$resp = $this->get($this->baseEndpoint . "/{$page->id}/export/markdown");
$resp->assertStatus(200);
$this->actingAsApiEditor();
$this->removePermissionFromUser($this->getEditor(), 'content-export');
- $page = Page::visible()->first();
+ $page = $this->entities->page();
foreach ($types as $type) {
$resp = $this->get($this->baseEndpoint . "/{$page->id}/export/{$type}");
$this->assertPermissionError($resp);
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Deletion;
-use BookStack\Entities\Models\Page;
use Illuminate\Support\Collection;
use Tests\TestCase;
{
$admin = $this->getAdmin();
- $page = Page::query()->first();
- $book = Book::query()->first();
+ $page = $this->entities->page();
+ $book = $this->entities->book();
$this->actingAs($admin)->delete($page->getUrl());
$this->delete($book->getUrl());
public function test_index_endpoint_returns_parent()
{
$admin = $this->getAdmin();
- $page = Page::query()->whereHas('chapter')->with('chapter')->first();
+ $page = $this->entities->pageWithinChapter();
$this->actingAs($admin)->delete($page->getUrl());
$deletion = Deletion::query()->orderBy('id')->first();
public function test_restore_endpoint()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin()->delete($page->getUrl());
$page->refresh();
public function test_destroy_endpoint()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin()->delete($page->getUrl());
$page->refresh();
public function test_all_endpoint_returns_entity_url()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->update(['name' => 'name with superuniquevalue within']);
$page->indexForSearch();
public function test_all_endpoint_returns_items_with_preview_html()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$book->update(['name' => 'name with superuniquevalue within', 'description' => 'Description with superuniquevalue within']);
$book->indexForSearch();
$user = User::query()->where('id', '!=', $this->getAdmin()->id)
->whereNull('system_name')
->first();
- $entityChain = $this->createEntityChainBelongingToUser($user);
+ $entityChain = $this->entities->createChainBelongingToUser($user);
/** @var User $newOwner */
$newOwner = User::query()->where('id', '!=', $user->id)->first();
namespace Tests\Auth;
use BookStack\Auth\Access\Mfa\MfaSession;
-use BookStack\Entities\Models\Page;
use Illuminate\Testing\TestResponse;
use Tests\TestCase;
public function test_login_redirects_to_initially_requested_url_correctly()
{
config()->set('app.url', 'http://localhost');
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->get($page->getUrl())->assertRedirect(url('/login'));
namespace Tests\Commands;
use BookStack\Actions\ActivityType;
-use BookStack\Entities\Models\Page;
use BookStack\Facades\Activity;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
public function test_clear_activity_command()
{
$this->asEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
Activity::add(ActivityType::PAGE_UPDATE, $page);
$this->assertDatabaseHas('activities', [
public function test_copy_shelf_permissions_command_using_slug()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$child = $shelf->books()->first();
$editorRole = $this->getEditor()->roles()->first();
$this->assertFalse(boolval($child->restricted), 'Child book should not be restricted by default');
$this->assertTrue($child->permissions()->count() === 0, 'Child book should have no permissions by default');
- $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+ $this->entities->setPermissions($shelf, ['view', 'update'], [$editorRole]);
$this->artisan('bookstack:copy-shelf-permissions', [
'--slug' => $shelf->slug,
]);
public function test_copy_shelf_permissions_command_using_all()
{
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
Bookshelf::query()->where('id', '!=', $shelf->id)->delete();
$child = $shelf->books()->first();
$editorRole = $this->getEditor()->roles()->first();
$this->assertFalse(boolval($child->restricted), 'Child book should not be restricted by default');
$this->assertTrue($child->permissions()->count() === 0, 'Child book should have no permissions by default');
- $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+ $this->entities->setPermissions($shelf, ['view', 'update'], [$editorRole]);
$this->artisan('bookstack:copy-shelf-permissions --all')
->expectsQuestion('Permission settings for all shelves will be cascaded. Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. Are you sure you want to proceed?', 'y');
$child = $shelf->books()->first();
namespace Tests\Commands;
-use BookStack\Entities\Models\Page;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
{
public function test_regenerate_references_command()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = $page->book;
$page->html = '<a href="' . $book->getUrl() . '">Book Link</a>';
namespace Tests\Commands;
-use BookStack\Entities\Models\Page;
use Symfony\Component\Console\Exception\RuntimeException;
use Tests\TestCase;
{
public function test_command_updates_page_content()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<a href="https://example.com/donkeys"></a>';
$page->save();
{
$user = User::factory()->create();
$this->giveUserPermissions($user, ['image-create-all']);
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$userRole = $user->roles()->first();
$resp = $this->actingAs($user)->get('/');
$this->withHtml($resp)->assertElementNotContains('header', 'Shelves');
- $this->setEntityRestrictions($shelf, ['view'], [$userRole]);
+ $this->entities->setPermissions($shelf, ['view'], [$userRole]);
$resp = $this->get('/');
$this->withHtml($resp)->assertElementContains('header', 'Shelves');
config()->set([
'setting-defaults.user.bookshelves_view_type' => 'list',
]);
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$book = $shelf->books()->first();
$resp = $this->asEditor()->get('/shelves');
$resp->assertSee($book->name);
$resp->assertSee($book->getUrl());
- $this->setEntityRestrictions($book, []);
+ $this->entities->setPermissions($book, []);
$resp = $this->asEditor()->get('/shelves');
$resp->assertDontSee($book->name);
public function test_shelf_view()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$resp = $this->asEditor()->get($shelf->getUrl());
$resp->assertStatus(200);
$resp->assertSeeText($shelf->name);
public function test_shelf_view_shows_action_buttons()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$resp = $this->asAdmin()->get($shelf->getUrl());
$resp->assertSee($shelf->getUrl('/create-book'));
$resp->assertSee($shelf->getUrl('/edit'));
public function test_shelf_view_has_sort_control_that_defaults_to_default()
{
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$resp = $this->asAdmin()->get($shelf->getUrl());
$this->withHtml($resp)->assertElementExists('form[action$="change-sort/shelf_books"]');
$this->withHtml($resp)->assertElementContains('form[action$="change-sort/shelf_books"] [aria-haspopup="true"]', 'Default');
public function test_shelf_edit()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$resp = $this->asEditor()->get($shelf->getUrl('/edit'));
$resp->assertSeeText('Edit Shelf');
public function test_shelf_create_new_book()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$resp = $this->asEditor()->get($shelf->getUrl('/create-book'));
$resp->assertSee('Create New Book');
public function test_shelf_copy_permissions()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$resp = $this->asAdmin()->get($shelf->getUrl('/permissions'));
$resp->assertSeeText('Copy Permissions');
$resp->assertSee("action=\"{$shelf->getUrl('/copy-permissions')}\"", false);
$this->assertFalse(boolval($child->restricted), 'Child book should not be restricted by default');
$this->assertTrue($child->permissions()->count() === 0, 'Child book should have no permissions by default');
- $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+ $this->entities->setPermissions($shelf, ['view', 'update'], [$editorRole]);
$resp = $this->post($shelf->getUrl('/copy-permissions'));
$child = $shelf->books()->first();
public function test_permission_page_has_a_warning_about_no_cascading()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$resp = $this->asAdmin()->get($shelf->getUrl('/permissions'));
$resp->assertSeeText('Permissions on shelves do not automatically cascade to contained books.');
}
public function test_bookshelves_show_in_breadcrumbs_if_in_context()
{
- $shelf = Bookshelf::first();
+ $shelf = $this->entities->shelf();
$shelfBook = $shelf->books()->first();
$shelfPage = $shelfBook->pages()->first();
$this->asAdmin();
public function test_cancel_on_child_book_creation_returns_to_original_shelf()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$resp = $this->asEditor()->get($shelf->getUrl('/create-book'));
$this->withHtml($resp)->assertElementContains('form a[href="' . $shelf->getUrl() . '"]', 'Cancel');
}
public function test_update()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
// Cheeky initial update to refresh slug
$this->asEditor()->put($book->getUrl(), ['name' => $book->name . '5', 'description' => $book->description]);
$book->refresh();
public function test_update_sets_tags()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->assertEquals(0, $book->tags()->count());
public function test_cancel_on_edit_book_page_leads_back_to_book()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->asEditor()->get($book->getUrl('/edit'));
$this->withHtml($resp)->assertElementContains('form a[href="' . $book->getUrl() . '"]', 'Cancel');
}
public function test_next_previous_navigation_controls_show_within_book_content()
{
- $book = Book::query()->first();
+ $book = $this->entities->book();
$chapter = $book->chapters->first();
$resp = $this->asEditor()->get($chapter->getUrl());
public function test_slug_multi_byte_url_safe()
{
- $book = $this->newBook([
+ $book = $this->entities->newBook([
'name' => 'информация',
]);
$this->assertEquals('informaciya', $book->slug);
- $book = $this->newBook([
+ $book = $this->entities->newBook([
'name' => '¿Qué?',
]);
public function test_slug_format()
{
- $book = $this->newBook([
+ $book = $this->entities->newBook([
'name' => 'PartA / PartB / PartC',
]);
public function test_show_view_has_copy_button()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->asEditor()->get($book->getUrl());
$this->withHtml($resp)->assertElementContains("a[href=\"{$book->getUrl('/copy')}\"]", 'Copy');
public function test_copy_view()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->asEditor()->get($book->getUrl('/copy'));
$resp->assertOk();
foreach ($book->getDirectChildren() as $child) {
$child->restricted = true;
$child->save();
- $this->regenEntityPermissions($child);
+ $this->entities->regenPermissions($child);
}
$this->asEditor()->post($book->getUrl('/copy'), ['name' => 'My copy book']);
public function test_copy_clones_cover_image_if_existing()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookRepo = $this->app->make(BookRepo::class);
$coverImageFile = $this->getTestImage('cover.png');
$bookRepo->updateCoverImage($book, $coverImageFile);
/** @var Bookshelf $shelfA */
/** @var Bookshelf $shelfB */
[$shelfA, $shelfB] = Bookshelf::query()->take(2)->get();
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$shelfA->appendBook($book);
$shelfB->appendBook($book);
$viewer = $this->getViewer();
$this->giveUserPermissions($viewer, ['book-update-all', 'book-create-all', 'bookshelf-update-all']);
- $this->setEntityRestrictions($shelfB);
+ $this->entities->setPermissions($shelfB);
$this->asEditor()->post($book->getUrl('/copy'), ['name' => 'My copy book']);
{
public function test_create()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$chapter = Chapter::factory()->make([
'name' => 'My First Chapter',
public function test_show_view_has_copy_button()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->asEditor()->get($chapter->getUrl());
$this->withHtml($resp)->assertElementContains("a[href$=\"{$chapter->getUrl('/copy')}\"]", 'Copy');
public function test_copy_view()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->asEditor()->get($chapter->getUrl('/copy'));
$resp->assertOk();
public function test_copy_does_not_copy_non_visible_pages()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->whereHas('pages')->first();
+ $chapter = $this->entities->chapterHasPages();
// Hide pages to all non-admin roles
/** @var Page $page */
foreach ($chapter->pages as $page) {
$page->restricted = true;
$page->save();
- $this->regenEntityPermissions($page);
+ $this->entities->regenPermissions($page);
}
$this->asEditor()->post($chapter->getUrl('/copy'), [
public function test_copy_does_not_copy_pages_if_user_cant_page_create()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->whereHas('pages')->first();
+ $chapter = $this->entities->chapterHasPages();
$viewer = $this->getViewer();
$this->giveUserPermissions($viewer, ['chapter-create-all']);
public function test_sort_book_action_visible_if_permissions_allow()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->actingAs($this->getViewer())->get($chapter->getUrl());
$this->withHtml($resp)->assertLinkNotExists($chapter->book->getUrl('sort'));
namespace Tests\Entity;
-use BookStack\Entities\Models\Page;
use Tests\TestCase;
class CommentSettingTest extends TestCase
{
- protected $page;
-
- protected function setUp(): void
- {
- parent::setUp();
- $this->page = Page::query()->first();
- }
-
public function test_comment_disable()
{
+ $page = $this->entities->page();
$this->setSettings(['app-disable-comments' => 'true']);
$this->asAdmin();
- $resp = $this->asAdmin()->get($this->page->getUrl());
+ $resp = $this->asAdmin()->get($page->getUrl());
$this->withHtml($resp)->assertElementNotExists('.comments-list');
}
public function test_comment_enable()
{
+ $page = $this->entities->page();
$this->setSettings(['app-disable-comments' => 'false']);
$this->asAdmin();
- $resp = $this->asAdmin()->get($this->page->getUrl());
+ $resp = $this->asAdmin()->get($page->getUrl());
$this->withHtml($resp)->assertElementExists('.comments-list');
}
}
public function test_add_comment()
{
$this->asAdmin();
- $page = Page::first();
+ $page = $this->entities->page();
$comment = Comment::factory()->make(['parent_id' => 2]);
$resp = $this->postJson("/comment/$page->id", $comment->getAttributes());
public function test_comment_edit()
{
$this->asAdmin();
- $page = Page::first();
+ $page = $this->entities->page();
$comment = Comment::factory()->make();
$this->postJson("/comment/$page->id", $comment->getAttributes());
public function test_comment_delete()
{
$this->asAdmin();
- $page = Page::first();
+ $page = $this->entities->page();
$comment = Comment::factory()->make();
$this->postJson("/comment/$page->id", $comment->getAttributes());
public function test_comments_converts_markdown_input_to_html()
{
- $page = Page::first();
+ $page = $this->entities->page();
$this->asAdmin()->postJson("/comment/$page->id", [
'text' => '# My Title',
]);
public function test_html_cannot_be_injected_via_comment_content()
{
$this->asAdmin();
- $page = Page::first();
+ $page = $this->entities->page();
$script = '<script>const a = "script";</script>\n\n# sometextinthecomment';
$this->postJson("/comment/$page->id", [
{
public function test_chapter_edit_view_shows_convert_option()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->asEditor()->get($chapter->getUrl('/edit'));
$resp->assertSee('Convert to Book');
public function test_convert_chapter_to_book()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->whereHas('pages')->first();
+ $chapter = $this->entities->chapterHasPages();
$chapter->tags()->save(new Tag(['name' => 'Category', 'value' => 'Penguins']));
/** @var Page $childPage */
$childPage = $chapter->pages()->first();
public function test_convert_chapter_to_book_requires_permissions()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$user = $this->getViewer();
$permissions = ['chapter-delete-all', 'book-create-all', 'chapter-update-all'];
public function test_book_edit_view_shows_convert_option()
{
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->asEditor()->get($book->getUrl('/edit'));
$resp->assertSee('Convert to Shelf');
public function test_book_convert_to_shelf_requires_permissions()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$user = $this->getViewer();
$permissions = ['book-delete-all', 'bookshelf-create-all', 'book-update-all', 'book-create-all'];
use BookStack\Auth\UserRepo;
use BookStack\Entities\Models\Entity;
-use BookStack\Entities\Repos\PageRepo;
use Tests\TestCase;
class EntityAccessTest extends TestCase
// Create required assets and revisions
$creator = $this->getEditor();
$updater = $this->getViewer();
- $entities = $this->createEntityChainBelongingToUser($creator, $updater);
+ $entities = $this->entities->createChainBelongingToUser($creator, $updater);
app()->make(UserRepo::class)->destroy($creator);
- app()->make(PageRepo::class)->update($entities['page'], ['html' => '<p>hello!</p>>']);
+ $this->entities->updatePage($entities['page'], ['html' => '<p>hello!</p>>']);
$this->checkEntitiesViewable($entities);
}
// Create required assets and revisions
$creator = $this->getViewer();
$updater = $this->getEditor();
- $entities = $this->createEntityChainBelongingToUser($creator, $updater);
+ $entities = $this->entities->createChainBelongingToUser($creator, $updater);
app()->make(UserRepo::class)->destroy($updater);
- app()->make(PageRepo::class)->update($entities['page'], ['html' => '<p>Hello there!</p>']);
+ $this->entities->updatePage($entities['page'], ['html' => '<p>Hello there!</p>']);
$this->checkEntitiesViewable($entities);
}
use BookStack\Actions\Tag;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use Tests\TestCase;
class EntitySearchTest extends TestCase
{
public function test_page_search()
{
- $book = Book::all()->first();
+ $book = $this->entities->book();
$page = $book->pages->first();
$search = $this->asEditor()->get('/search?term=' . urlencode($page->name));
public function test_bookshelf_search()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$search = $this->asEditor()->get('/search?term=' . urlencode($shelf->name) . ' {type:bookshelf}');
$search->assertSee('Search Results');
public function test_searching_accents_and_small_terms()
{
- $page = $this->newPage(['name' => 'My new test quaffleachits', 'html' => 'some áéííúü¿¡ test content a2 orange dog']);
+ $page = $this->entities->newPage(['name' => 'My new test quaffleachits', 'html' => 'some áéííúü¿¡ test content a2 orange dog']);
$this->asEditor();
$accentSearch = $this->get('/search?term=' . urlencode('áéíí'));
public function test_chapter_search()
{
- $chapter = Chapter::has('pages')->first();
+ $chapter = $this->entities->chapterHasPages();
$page = $chapter->pages[0];
$pageTestResp = $this->asEditor()->get('/search/chapter/' . $chapter->id . '?term=' . urlencode($page->name));
]),
];
- $pageA = Page::first();
+ $pageA = $this->entities->page();
$pageA->tags()->saveMany($newTags);
- $pageB = Page::all()->last();
+ $pageB = $this->entities->page();
$pageB->tags()->create(['name' => 'animal', 'value' => 'dog']);
$this->asEditor();
public function test_exact_searches()
{
- $page = $this->newPage(['name' => 'My new test page', 'html' => 'this is a story about an orange donkey']);
+ $page = $this->entities->newPage(['name' => 'My new test page', 'html' => 'this is a story about an orange donkey']);
$exactSearchA = $this->asEditor()->get('/search?term=' . urlencode('"story about an orange"'));
$exactSearchA->assertStatus(200)->assertSee($page->name);
public function test_search_terms_with_delimiters_are_converted_to_exact_matches()
{
$this->asEditor();
- $page = $this->newPage(['name' => 'Delimiter test', 'html' => '<p>1.1 2,2 3?3 4:4 5;5 (8) <9> "10" \'11\' `12`</p>']);
+ $page = $this->entities->newPage(['name' => 'Delimiter test', 'html' => '<p>1.1 2,2 3?3 4:4 5;5 (8) <9> "10" \'11\' `12`</p>']);
$terms = explode(' ', '1.1 2,2 3?3 4:4 5;5 (8) <9> "10" \'11\' `12`');
foreach ($terms as $term) {
public function test_search_filters()
{
- $page = $this->newPage(['name' => 'My new test quaffleachits', 'html' => 'this is about an orange donkey danzorbhsing']);
+ $page = $this->entities->newPage(['name' => 'My new test quaffleachits', 'html' => 'this is about an orange donkey danzorbhsing']);
$this->asEditor();
$editorId = $this->getEditor()->id;
$editorSlug = $this->getEditor()->slug;
public function test_ajax_entity_search()
{
- $page = $this->newPage(['name' => 'my ajax search test', 'html' => 'ajax test']);
- $notVisitedPage = Page::first();
+ $page = $this->entities->newPage(['name' => 'my ajax search test', 'html' => 'ajax test']);
+ $notVisitedPage = $this->entities->page();
// Visit the page to make popular
$this->asEditor()->get($page->getUrl());
public function test_ajax_entity_search_shows_breadcrumbs()
{
- $chapter = Chapter::first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages->first();
$this->asEditor();
public function test_ajax_entity_search_reflects_items_without_permission()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$baseSelector = 'a[data-entity-type="page"][data-entity-id="' . $page->id . '"]';
$searchUrl = '/ajax/search/entities?permission=update&term=' . urlencode($page->name);
public function test_sibling_search_for_pages()
{
- $chapter = Chapter::query()->with('pages')->first();
+ $chapter = $this->entities->chapterHasPages();
$this->assertGreaterThan(2, count($chapter->pages), 'Ensure we\'re testing with at least 1 sibling');
$page = $chapter->pages->first();
public function test_sibling_search_for_pages_without_chapter()
{
- $page = Page::query()->where('chapter_id', '=', 0)->firstOrFail();
+ $page = $this->entities->pageNotWithinChapter();
$bookChildren = $page->book->getDirectChildren();
$this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
public function test_sibling_search_for_chapters()
{
- $chapter = Chapter::query()->firstOrFail();
+ $chapter = $this->entities->chapter();
$bookChildren = $chapter->book->getDirectChildren();
$this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling');
public function test_search_works_on_updated_page_content()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor();
$update = $this->put($page->getUrl(), [
public function test_search_ranks_common_words_lower()
{
- $this->newPage(['name' => 'Test page A', 'html' => '<p>dog biscuit dog dog</p>']);
- $this->newPage(['name' => 'Test page B', 'html' => '<p>cat biscuit</p>']);
+ $this->entities->newPage(['name' => 'Test page A', 'html' => '<p>dog biscuit dog dog</p>']);
+ $this->entities->newPage(['name' => 'Test page B', 'html' => '<p>cat biscuit</p>']);
$search = $this->asEditor()->get('/search?term=cat+dog+biscuit');
$this->withHtml($search)->assertElementContains('.entity-list > .page:nth-child(1)', 'Test page A');
$this->withHtml($search)->assertElementContains('.entity-list > .page:nth-child(2)', 'Test page B');
for ($i = 0; $i < 2; $i++) {
- $this->newPage(['name' => 'Test page ' . $i, 'html' => '<p>dog</p>']);
+ $this->entities->newPage(['name' => 'Test page ' . $i, 'html' => '<p>dog</p>']);
}
$search = $this->asEditor()->get('/search?term=cat+dog+biscuit');
public function test_terms_in_headers_have_an_adjusted_index_score()
{
- $page = $this->newPage(['name' => 'Test page A', 'html' => '
+ $page = $this->entities->newPage(['name' => 'Test page A', 'html' => '
<p>TermA</p>
<h1>TermB <strong>TermNested</strong></h1>
<h2>TermC</h2>
public function test_name_and_content_terms_are_merged_to_single_score()
{
- $page = $this->newPage(['name' => 'TermA', 'html' => '
+ $page = $this->entities->newPage(['name' => 'TermA', 'html' => '
<p>TermA</p>
']);
public function test_tag_names_and_values_are_indexed_for_search()
{
- $page = $this->newPage(['name' => 'PageA', 'html' => '<p>content</p>', 'tags' => [
+ $page = $this->entities->newPage(['name' => 'PageA', 'html' => '<p>content</p>', 'tags' => [
['name' => 'Animal', 'value' => 'MeowieCat'],
['name' => 'SuperImportant'],
]]);
public function test_matching_terms_in_search_results_are_highlighted()
{
- $this->newPage(['name' => 'My Meowie Cat', 'html' => '<p>A superimportant page about meowieable animals</p>', 'tags' => [
+ $this->entities->newPage(['name' => 'My Meowie Cat', 'html' => '<p>A superimportant page about meowieable animals</p>', 'tags' => [
['name' => 'Animal', 'value' => 'MeowieCat'],
['name' => 'SuperImportant'],
]]);
public function test_match_highlighting_works_with_multibyte_content()
{
- $this->newPage([
+ $this->entities->newPage([
'name' => 'Test Page',
'html' => '<p>На мен ми трябва нещо добро test</p>',
]);
public function test_html_entities_in_item_details_remains_escaped_in_search_results()
{
- $this->newPage(['name' => 'My <cool> TestPageContent', 'html' => '<p>My supercool <great> TestPageContent page</p>']);
+ $this->entities->newPage(['name' => 'My <cool> TestPageContent', 'html' => '<p>My supercool <great> TestPageContent page</p>']);
$search = $this->asEditor()->get('/search?term=TestPageContent');
$search->assertSee('My <cool> <strong>TestPageContent</strong>', false);
public function test_words_adjacent_to_lines_breaks_can_be_matched_with_normal_terms()
{
- $page = $this->newPage(['name' => 'TermA', 'html' => '
+ $page = $this->entities->newPage(['name' => 'TermA', 'html' => '
<p>TermA<br>TermB<br>TermC</p>
']);
{
public function test_page_text_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor();
$resp = $this->get($page->getUrl('/export/plaintext'));
public function test_page_pdf_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor();
$resp = $this->get($page->getUrl('/export/pdf'));
public function test_page_html_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor();
$resp = $this->get($page->getUrl('/export/html'));
public function test_book_text_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = $page->book;
$this->asEditor();
public function test_book_pdf_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = $page->book;
$this->asEditor();
public function test_book_html_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = $page->book;
$this->asEditor();
public function test_book_html_export_shows_chapter_descriptions()
{
$chapterDesc = 'My custom test chapter description ' . Str::random(12);
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$chapter->description = $chapterDesc;
$chapter->save();
public function test_chapter_text_export()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages[0];
$this->asEditor();
public function test_chapter_pdf_export()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$this->asEditor();
$resp = $this->get($chapter->getUrl('/export/pdf'));
public function test_chapter_html_export()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages[0];
$this->asEditor();
public function test_page_html_export_contains_custom_head_if_set()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$customHeadContent = '<style>p{color: red;}</style>';
$this->setSettings(['app-custom-head' => $customHeadContent]);
public function test_page_html_export_does_not_break_with_only_comments_in_custom_head()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$customHeadContent = '<!-- A comment -->';
$this->setSettings(['app-custom-head' => $customHeadContent]);
public function test_page_html_export_use_absolute_dates()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$resp = $this->asEditor()->get($page->getUrl('/export/html'));
$resp->assertSee($page->created_at->formatLocalized('%e %B %Y %H:%M:%S'));
public function test_page_export_does_not_include_user_or_revision_links()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$resp = $this->asEditor()->get($page->getUrl('/export/html'));
$resp->assertDontSee($page->getUrl('/revisions'));
public function test_page_export_sets_right_data_type_for_svg_embeds()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
Storage::disk('local')->makeDirectory('uploads/images/gallery');
Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
$page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg">';
public function test_page_image_containment_works_on_multiple_images_within_a_single_line()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
Storage::disk('local')->makeDirectory('uploads/images/gallery');
Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
Storage::disk('local')->put('uploads/images/gallery/svg_test2.svg', '<svg></svg>');
public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg"/>'
. '<img src="http://localhost/uploads/svg_test.svg"/>'
. '<img src="/uploads/svg_test.svg"/>';
$contents = file_get_contents(public_path('.htaccess'));
config()->set('filesystems.images', 'local');
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<img src="http://localhost/uploads/images/../../.htaccess"/>';
$page->save();
config()->set('filesystems.images', 'local_secure');
file_put_contents($testFilePath, 'I am a cat');
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<img src="http://localhost/uploads/images/../../logs/test.txt"/>';
$page->save();
public function test_page_export_with_deleted_creator_and_updater()
{
$user = $this->getViewer(['name' => 'ExportWizardTheFifth']);
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->created_by = $user->id;
$page->updated_by = $user->id;
$page->save();
public function test_page_pdf_export_opens_details_blocks()
{
- $page = Page::query()->first()->forceFill([
+ $page = $this->entities->page()->forceFill([
'html' => '<details><summary>Hello</summary><p>Content!</p></details>',
]);
$page->save();
public function test_page_markdown_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
$resp->assertStatus(200);
public function test_page_markdown_export_uses_existing_markdown_if_apparent()
{
- $page = Page::query()->first()->forceFill([
+ $page = $this->entities->page()->forceFill([
'markdown' => '# A header',
'html' => '<h1>Dogcat</h1>',
]);
public function test_page_markdown_export_converts_html_where_no_markdown()
{
- $page = Page::query()->first()->forceFill([
+ $page = $this->entities->page()->forceFill([
'markdown' => '',
'html' => '<h1>Dogcat</h1><p>Some <strong>bold</strong> text</p>',
]);
public function test_chapter_markdown_export()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages()->first();
$resp = $this->asEditor()->get($chapter->getUrl('/export/markdown'));
public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
config()->set('snappy.pdf.binary', '/abc123');
config()->set('app.allow_untrusted_server_fetching', false);
public function test_html_exports_contain_csp_meta_tag()
{
$entities = [
- Page::query()->first(),
- Book::query()->first(),
- Chapter::query()->first(),
+ $this->entities->page(),
+ $this->entities->book(),
+ $this->entities->chapter(),
];
foreach ($entities as $entity) {
public function test_html_exports_contain_body_classes_for_export_identification()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$resp = $this->asEditor()->get($page->getUrl('/export/html'));
$this->withHtml($resp)->assertElementExists('body.export.export-format-html.export-engine-none');
public function test_page_includes()
{
- $page = Page::query()->first();
- $secondPage = Page::query()->where('id', '!=', $page->id)->first();
+ $page = $this->entities->page();
+ $secondPage = $this->entities->page();
$secondPage->html = "<p id='section1'>Hello, This is a test</p><p id='section2'>This is a second block of content</p>";
$secondPage->save();
public function test_saving_page_with_includes()
{
- $page = Page::query()->first();
- $secondPage = Page::query()->where('id', '!=', $page->id)->first();
+ $page = $this->entities->page();
+ $secondPage = $this->entities->page();
$this->asEditor();
$includeTag = '{{@' . $secondPage->id . '}}';
public function test_page_includes_do_not_break_tables()
{
- /** @var Page $page */
- $page = Page::query()->first();
- /** @var Page $secondPage */
- $secondPage = Page::query()->where('id', '!=', $page->id)->first();
+ $page = $this->entities->page();
+ $secondPage = $this->entities->page();
$content = '<table id="table"><tbody><tr><td>test</td></tr></tbody></table>';
$secondPage->html = $content;
public function test_page_includes_do_not_break_code()
{
- /** @var Page $page */
- $page = Page::query()->first();
- /** @var Page $secondPage */
- $secondPage = Page::query()->where('id', '!=', $page->id)->first();
+ $page = $this->entities->page();
+ $secondPage = $this->entities->page();
$content = '<pre id="bkmrk-code"><code>var cat = null;</code></pre>';
$secondPage->html = $content;
public function test_page_includes_rendered_on_book_export()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$secondPage = Page::query()
->where('book_id', '!=', $page->book_id)
->first();
public function test_page_content_scripts_removed_by_default()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$script = 'abc123<script>console.log("hello-test")</script>abc123';
$page->html = "escape {$script}";
$page->save();
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
public function test_page_inline_on_attributes_removed_by_default()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$script = '<p onmouseenter="console.log(\'test\')">Hello</p>';
$page->html = "escape {$script}";
$page->save();
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
public function test_page_content_scripts_show_when_configured()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
config()->push('app.allow_content_scripts', 'true');
$script = 'abc123<script>console.log("hello-test")</script>abc123';
];
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
foreach ($checks as $check) {
$page->html = $check;
public function test_page_inline_on_attributes_show_if_configured()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
config()->push('app.allow_content_scripts', 'true');
$script = '<p onmouseenter="console.log(\'test\')">Hello</p>';
public function test_duplicate_ids_fixed_on_page_save()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '<ul id="bkmrk-test"><li>test a</li><li><ul id="bkmrk-test"><li>test b</li></ul></li></ul>';
$pageSave = $this->put($page->getUrl(), [
public function test_anchors_referencing_non_bkmrk_ids_rewritten_after_save()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '<h1 id="non-standard-id">test</h1><p><a href="#non-standard-id">link</a></p>';
$this->put($page->getUrl(), [
public function test_page_text_decodes_html_entities()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($this->getAdmin())
->put($page->getUrl(''), [
public function test_page_markdown_table_rendering()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '| Syntax | Description |
| ----------- | ----------- |
public function test_page_markdown_task_list_rendering()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '- [ ] Item a
- [x] Item b';
public function test_page_markdown_strikethrough_rendering()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '~~some crossed out text~~';
$this->put($page->getUrl(), [
public function test_page_markdown_single_html_comment_saving()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '<!-- Test Comment -->';
$this->put($page->getUrl(), [
public function test_base64_images_get_extracted_from_page_content()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
public function test_base64_images_get_extracted_when_containing_whitespace()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$base64PngWithWhitespace = "iVBORw0KGg\noAAAANSUhE\tUgAAAAEAAAA BCA YAAAAfFcSJAAA\n\t ACklEQVR4nGMAAQAABQAB";
$base64PngWithoutWhitespace = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQAB';
foreach ($extensions as $extension) {
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
public function test_base64_images_get_extracted_from_markdown_page_content()
{
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
$pcreRecursionLimit = ini_get('pcre.recursion_limit');
$this->asEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
ini_set('pcre.backtrack_limit', '500');
ini_set('pcre.recursion_limit', '500');
public function test_base64_images_within_markdown_blanked_if_not_supported_extension_for_extract()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor()->put($page->getUrl(), [
'name' => $page->name, 'summary' => '',
public function test_nested_headers_gets_assigned_an_id()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '<table><tbody><tr><td><h5>Simple Test</h5></td></tr></tbody></table>';
$this->asEditor()->put($page->getUrl(), [
public function test_non_breaking_spaces_are_preserved()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = '<p> </p>';
$this->asEditor()->put($page->getUrl(), [
namespace Tests\Entity;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Models\PageRevision;
use BookStack\Entities\Repos\PageRepo;
class PageDraftTest extends TestCase
{
- /**
- * @var Page
- */
- protected $page;
-
- /**
- * @var PageRepo
- */
- protected $pageRepo;
+ protected Page $page;
+ protected PageRepo $pageRepo;
protected function setUp(): void
{
parent::setUp();
- $this->page = Page::query()->first();
+ $this->page = $this->entities->page();
$this->pageRepo = app()->make(PageRepo::class);
}
{
$admin = $this->getAdmin();
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($editor)->put('/ajax/page/' . $page->id . '/save-draft', [
'name' => $page->name,
{
$admin = $this->getAdmin();
$editor = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($admin)->put('/ajax/page/' . $page->id . '/save-draft', [
'name' => $page->name,
public function test_draft_pages_show_on_homepage()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->asAdmin()->get('/');
$this->withHtml($resp)->assertElementNotContains('#recent-drafts', 'New Page');
public function test_draft_pages_not_visible_by_others()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$chapter = $book->chapters->first();
$newUser = $this->getEditor();
public function test_page_html_in_ajax_fetch_response()
{
$this->asAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->getJson('/ajax/page/' . $page->id)->assertJson([
'html' => $page->html,
public function test_updating_page_draft_with_markdown_retains_markdown_content()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->asEditor()->get($book->getUrl('/create-page'));
/** @var Page $draft */
$draft = Page::query()->where('draft', '=', true)->where('book_id', '=', $book->id)->firstOrFail();
public function test_slug_generated_on_draft_publish_to_page_when_no_name_change()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->asEditor()->get($book->getUrl('/create-page'));
/** @var Page $draft */
$draft = Page::query()->where('draft', '=', true)->where('book_id', '=', $book->id)->firstOrFail();
namespace Tests\Entity;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use Tests\TestCase;
class PageEditorTest extends TestCase
{
- /** @var Page */
- protected $page;
+ protected Page $page;
protected function setUp(): void
{
parent::setUp();
- $this->page = Page::query()->first();
+ $this->page = $this->entities->page();
}
public function test_default_editor_is_wysiwyg_for_new_pages()
public function test_empty_markdown_still_saves_without_error()
{
$this->setSettings(['app-editor' => 'markdown']);
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->asEditor()->get($book->getUrl('/create-page'));
$draft = Page::query()->where('book_id', '=', $book->id)
public function test_back_link_in_editor_has_correct_url()
{
- /** @var Book $book */
- $book = Book::query()->whereHas('pages')->whereHas('chapters')->firstOrFail();
+ $book = $this->entities->bookHasChaptersAndPages();
$this->asEditor()->get($book->getUrl('/create-page'));
/** @var Chapter $chapter */
$chapter = $book->chapters()->firstOrFail();
public function test_switching_from_html_to_clean_markdown_works()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<h2>A Header</h2><p>Some <strong>bold</strong> content.</p>';
$page->save();
public function test_switching_from_html_to_stable_markdown_works()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<h2>A Header</h2><p>Some <strong>bold</strong> content.</p>';
$page->save();
public function test_switching_from_markdown_to_wysiwyg_works()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '';
$page->markdown = "## A Header\n\nSome content with **bold** text!";
$page->save();
public function test_page_editor_type_switch_does_not_work_without_change_editor_permissions()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<h2>A Header</h2><p>Some <strong>bold</strong> content.</p>';
$page->save();
public function test_page_save_does_not_change_active_editor_without_change_editor_permissions()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->html = '<h2>A Header</h2><p>Some <strong>bold</strong> content.</p>';
$page->editor = 'wysiwyg';
$page->save();
{
public function test_revision_links_visible_to_viewer()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$html = $this->withHtml($this->asViewer()->get($page->getUrl()));
$html->assertLinkExists($page->getUrl('/revisions'));
public function test_page_revision_views_viewable()
{
$this->asEditor();
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 1, ['name' => 'updated page', 'html' => '<p>new content</p>']);
$pageRevision = $page->revisions->last();
public function test_page_revision_preview_shows_content_of_revision()
{
$this->asEditor();
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 1, ['name' => 'updated page', 'html' => '<p>new revision content</p>']);
$pageRevision = $page->revisions->last();
$this->createRevisions($page, 1, ['name' => 'updated page', 'html' => '<p>Updated content</p>']);
public function test_page_revision_restore_updates_content()
{
$this->asEditor();
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 1, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>']);
$this->createRevisions($page, 1, ['name' => 'updated page again', 'html' => '<p>new content</p>']);
$page = Page::find($page->id);
public function test_page_revision_restore_with_markdown_retains_markdown_content()
{
$this->asEditor();
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 1, ['name' => 'updated page abc123', 'markdown' => '## New Content def456']);
$this->createRevisions($page, 1, ['name' => 'updated page again', 'markdown' => '## New Content Updated']);
$page = Page::find($page->id);
public function test_page_revision_restore_sets_new_revision_with_summary()
{
$this->asEditor();
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 1, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'My first update']);
$this->createRevisions($page, 1, ['html' => '<p>new content</p>']);
$page->refresh();
public function test_page_revision_count_increments_on_update()
{
- $page = Page::first();
+ $page = $this->entities->page();
$startCount = $page->revision_count;
$this->createRevisions($page, 1);
public function test_revision_count_shown_in_page_meta()
{
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 2);
$pageView = $this->get($page->getUrl());
public function test_revision_deletion()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->createRevisions($page, 2);
$beforeRevisionCount = $page->revisions->count();
public function test_revision_limit_enforced()
{
config()->set('app.revision_limit', 2);
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 12);
$revisionCount = $page->revisions()->count();
public function test_false_revision_limit_allows_many_revisions()
{
config()->set('app.revision_limit', false);
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 12);
$revisionCount = $page->revisions()->count();
public function test_revision_list_shows_editor_type()
{
- /** @var Page $page */
- $page = Page::first();
+ $page = $this->entities->page();
$this->createRevisions($page, 1, ['html' => 'new page html']);
$resp = $this->asAdmin()->get($page->refresh()->getUrl('/revisions'));
public function test_revision_restore_action_only_visible_with_permission()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->createRevisions($page, 2);
$viewer = $this->getViewer();
public function test_revision_delete_action_only_visible_with_permission()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->createRevisions($page, 2);
$viewer = $this->getViewer();
{
public function test_active_templates_visible_on_page_view()
{
- $page = Page::first();
+ $page = $this->entities->page();
$this->asEditor();
$templateView = $this->get($page->getUrl());
public function test_manage_templates_permission_required_to_change_page_template_status()
{
- $page = Page::first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$this->actingAs($editor);
public function test_templates_content_should_be_fetchable_only_if_page_marked_as_template()
{
$content = '<div>my_custom_template_content</div>';
- $page = Page::first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$this->actingAs($editor);
namespace Tests\Entity;
use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Page;
use Carbon\Carbon;
use Tests\TestCase;
{
public function test_create()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = Page::factory()->make([
'name' => 'My First Page',
]);
public function test_page_view_when_creator_is_deleted_but_owner_exists()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$user = $this->getViewer();
$owner = $this->getEditor();
$page->created_by = $user->id;
public function test_page_creation_with_markdown_content()
{
$this->setSettings(['app-editor' => 'markdown']);
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->asEditor()->get($book->getUrl('/create-page'));
$draft = Page::query()->where('book_id', '=', $book->id)
public function test_page_delete()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->assertNull($page->deleted_at);
$deleteViewReq = $this->asEditor()->get($page->getUrl('/delete'));
public function test_page_full_delete_removes_all_revisions()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->revisions()->create([
'html' => '<p>ducks</p>',
'name' => 'my page revision',
public function test_page_copy()
{
- $page = Page::first();
+ $page = $this->entities->page();
$page->html = '<p>This is some test content</p>';
$page->save();
public function test_page_copy_with_markdown_has_both_html_and_markdown()
{
- $page = Page::first();
+ $page = $this->entities->page();
$page->html = '<h1>This is some test content</h1>';
$page->markdown = '# This is some test content';
$page->save();
public function test_page_copy_with_no_destination()
{
- $page = Page::first();
+ $page = $this->entities->page();
$currentBook = $page->book;
$resp = $this->asEditor()->get($page->getUrl('/copy'));
public function test_page_can_be_copied_without_edit_permission()
{
- $page = Page::first();
+ $page = $this->entities->page();
$currentBook = $page->book;
$newBook = Book::where('id', '!=', $currentBook->id)->first();
$viewer = $this->getViewer();
$newBook->owned_by = $viewer->id;
$newBook->save();
$this->giveUserPermissions($viewer, ['page-create-own']);
- $this->regenEntityPermissions($newBook);
+ $this->entities->regenPermissions($newBook);
$resp = $this->actingAs($viewer)->get($page->getUrl());
$resp->assertSee($page->getUrl('/copy'));
public function test_old_page_slugs_redirect_to_new_pages()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
// Need to save twice since revisions are not generated in seeder.
$this->asAdmin()->put($page->getUrl(), [
public function test_page_within_chapter_deletion_returns_to_chapter()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages()->first();
$this->asEditor()->delete($page->getUrl())
public function test_recently_updated_pages_view()
{
$user = $this->getEditor();
- $content = $this->createEntityChainBelongingToUser($user);
+ $content = $this->entities->createChainBelongingToUser($user);
$resp = $this->asAdmin()->get('/pages/recently-updated');
$this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', $content['page']->name);
public function test_recently_updated_pages_view_shows_updated_by_details()
{
$user = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($user)->put($page->getUrl(), [
'name' => 'Updated title',
public function test_recently_updated_pages_view_shows_parent_chain()
{
$user = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->whereNotNull('chapter_id')->first();
+ $page = $this->entities->pageWithinChapter();
$this->actingAs($user)->put($page->getUrl(), [
'name' => 'Updated title',
public function test_recently_updated_pages_view_does_not_show_parent_if_not_visible()
{
$user = $this->getEditor();
- /** @var Page $page */
- $page = Page::query()->whereNotNull('chapter_id')->first();
+ $page = $this->entities->pageWithinChapter();
$this->actingAs($user)->put($page->getUrl(), [
'name' => 'Updated title',
'html' => '<p>Updated content</p>',
]);
- $this->setEntityRestrictions($page->book);
- $this->setEntityRestrictions($page, ['view'], [$user->roles->first()]);
+ $this->entities->setPermissions($page->book);
+ $this->entities->setPermissions($page, ['view'], [$user->roles->first()]);
$resp = $this->get('/pages/recently-updated');
$resp->assertDontSee($page->book->getShortName(42));
class SortTest extends TestCase
{
- protected $book;
-
- protected function setUp(): void
- {
- parent::setUp();
- $this->book = Book::first();
- }
-
public function test_drafts_do_not_show_up()
{
$this->asAdmin();
$pageRepo = app(PageRepo::class);
- $draft = $pageRepo->getNewDraftPage($this->book);
+ $book = $this->entities->book();
+ $draft = $pageRepo->getNewDraftPage($book);
- $resp = $this->get($this->book->getUrl());
+ $resp = $this->get($book->getUrl());
$resp->assertSee($draft->name);
- $resp = $this->get($this->book->getUrl() . '/sort');
+ $resp = $this->get($book->getUrl() . '/sort');
$resp->assertDontSee($draft->name);
}
public function test_page_move_into_book()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$currentBook = $page->book;
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
$movePageResp = $this->put($page->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
- $page = Page::query()->find($page->id);
+ $page->refresh();
$movePageResp->assertRedirect($page->getUrl());
$this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book');
public function test_page_move_into_chapter()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$currentBook = $page->book;
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
$newChapter = $newBook->chapters()->first();
$movePageResp = $this->actingAs($this->getEditor())->put($page->getUrl('/move'), [
'entity_selection' => 'chapter:' . $newChapter->id,
]);
- $page = Page::query()->find($page->id);
+ $page->refresh();
$movePageResp->assertRedirect($page->getUrl());
$this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new chapter');
public function test_page_move_requires_create_permissions_on_parent()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$currentBook = $page->book;
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($newBook, ['view', 'update', 'delete'], $editor->roles->all());
+ $this->entities->setPermissions($newBook, ['view', 'update', 'delete'], $editor->roles->all());
$movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
$this->assertPermissionError($movePageResp);
- $this->setEntityRestrictions($newBook, ['view', 'update', 'delete', 'create'], $editor->roles->all());
+ $this->entities->setPermissions($newBook, ['view', 'update', 'delete', 'create'], $editor->roles->all());
$movePageResp = $this->put($page->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
- $page = Page::query()->find($page->id);
+ $page->refresh();
$movePageResp->assertRedirect($page->getUrl());
$this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book');
public function test_page_move_requires_delete_permissions()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$currentBook = $page->book;
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
- $this->setEntityRestrictions($page, ['view', 'update', 'create'], $editor->roles->all());
+ $this->entities->setPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+ $this->entities->setPermissions($page, ['view', 'update', 'create'], $editor->roles->all());
$movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
$pageView = $this->get($page->getUrl());
$pageView->assertDontSee($page->getUrl('/move'));
- $this->setEntityRestrictions($page, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+ $this->entities->setPermissions($page, ['view', 'update', 'create', 'delete'], $editor->roles->all());
$movePageResp = $this->put($page->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
- $page = Page::query()->find($page->id);
+ $page->refresh();
$movePageResp->assertRedirect($page->getUrl());
$this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book');
}
public function test_chapter_move()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$currentBook = $chapter->book;
$pageToCheck = $chapter->pages->first();
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
public function test_chapter_move_requires_delete_permissions()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$currentBook = $chapter->book;
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
- $this->setEntityRestrictions($chapter, ['view', 'update', 'create'], $editor->roles->all());
+ $this->entities->setPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+ $this->entities->setPermissions($chapter, ['view', 'update', 'create'], $editor->roles->all());
$moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
$pageView = $this->get($chapter->getUrl());
$pageView->assertDontSee($chapter->getUrl('/move'));
- $this->setEntityRestrictions($chapter, ['view', 'update', 'create', 'delete'], $editor->roles->all());
+ $this->entities->setPermissions($chapter, ['view', 'update', 'create', 'delete'], $editor->roles->all());
$moveChapterResp = $this->put($chapter->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
public function test_chapter_move_requires_create_permissions_in_new_book()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$currentBook = $chapter->book;
$newBook = Book::query()->where('id', '!=', $currentBook->id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($newBook, ['view', 'update', 'delete'], [$editor->roles->first()]);
- $this->setEntityRestrictions($chapter, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
+ $this->entities->setPermissions($newBook, ['view', 'update', 'delete'], [$editor->roles->first()]);
+ $this->entities->setPermissions($chapter, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
$moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
$this->assertPermissionError($moveChapterResp);
- $this->setEntityRestrictions($newBook, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
+ $this->entities->setPermissions($newBook, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]);
$moveChapterResp = $this->put($chapter->getUrl('/move'), [
'entity_selection' => 'book:' . $newBook->id,
]);
public function test_book_sort_page_shows()
{
- /** @var Book $bookToSort */
- $bookToSort = Book::query()->first();
+ $bookToSort = $this->entities->book();
$resp = $this->asAdmin()->get($bookToSort->getUrl());
$this->withHtml($resp)->assertElementExists('a[href="' . $bookToSort->getUrl('/sort') . '"]');
public function test_book_sort()
{
- $oldBook = Book::query()->first();
- $chapterToMove = $this->newChapter(['name' => 'chapter to move'], $oldBook);
- $newBook = $this->newBook(['name' => 'New sort book']);
+ $oldBook = $this->entities->book();
+ $chapterToMove = $this->entities->newChapter(['name' => 'chapter to move'], $oldBook);
+ $newBook = $this->entities->newBook(['name' => 'New sort book']);
$pagesToMove = Page::query()->take(5)->get();
// Create request data
public function test_book_sort_makes_no_changes_if_new_chapter_does_not_align_with_new_book()
{
- /** @var Page $page */
- $page = Page::query()->where('chapter_id', '!=', 0)->first();
+ $page = $this->entities->pageWithinChapter();
$otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
$sortData = [
public function test_book_sort_makes_no_changes_if_no_view_permissions_on_new_chapter()
{
- /** @var Page $page */
- $page = Page::query()->where('chapter_id', '!=', 0)->first();
+ $page = $this->entities->pageWithinChapter();
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
- $this->setEntityRestrictions($otherChapter);
+ $this->entities->setPermissions($otherChapter);
$sortData = [
'id' => $page->id,
public function test_book_sort_makes_no_changes_if_no_view_permissions_on_new_book()
{
- /** @var Page $page */
- $page = Page::query()->where('chapter_id', '!=', 0)->first();
+ $page = $this->entities->pageWithinChapter();
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($otherChapter->book, ['update', 'delete'], [$editor->roles()->first()]);
+ $this->entities->setPermissions($otherChapter->book, ['update', 'delete'], [$editor->roles()->first()]);
$sortData = [
'id' => $page->id,
public function test_book_sort_makes_no_changes_if_no_update_or_create_permissions_on_new_chapter()
{
- /** @var Page $page */
- $page = Page::query()->where('chapter_id', '!=', 0)->first();
+ $page = $this->entities->pageWithinChapter();
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($otherChapter, ['view', 'delete'], [$editor->roles()->first()]);
+ $this->entities->setPermissions($otherChapter, ['view', 'delete'], [$editor->roles()->first()]);
$sortData = [
'id' => $page->id,
public function test_book_sort_makes_no_changes_if_no_update_permissions_on_moved_item()
{
- /** @var Page $page */
- $page = Page::query()->where('chapter_id', '!=', 0)->first();
+ $page = $this->entities->pageWithinChapter();
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($page, ['view', 'delete'], [$editor->roles()->first()]);
+ $this->entities->setPermissions($page, ['view', 'delete'], [$editor->roles()->first()]);
$sortData = [
'id' => $page->id,
public function test_book_sort_makes_no_changes_if_no_delete_permissions_on_moved_item()
{
- /** @var Page $page */
- $page = Page::query()->where('chapter_id', '!=', 0)->first();
+ $page = $this->entities->pageWithinChapter();
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->where('book_id', '!=', $page->book_id)->first();
$editor = $this->getEditor();
- $this->setEntityRestrictions($page, ['view', 'update'], [$editor->roles()->first()]);
+ $this->entities->setPermissions($page, ['view', 'update'], [$editor->roles()->first()]);
$sortData = [
'id' => $page->id,
public function test_book_sort_item_returns_book_content()
{
- $books = Book::all();
- $bookToSort = $books[0];
+ $bookToSort = $this->entities->book();
$firstPage = $bookToSort->pages[0];
$firstChapter = $bookToSort->chapters[0];
public function test_pages_in_book_show_sorted_by_priority()
{
- /** @var Book $book */
- $book = Book::query()->whereHas('pages')->first();
+ $book = $this->entities->bookHasChaptersAndPages();
$book->chapters()->forceDelete();
/** @var Page[] $pages */
$pages = $book->pages()->where('chapter_id', '=', 0)->take(2)->get();
namespace Tests\Entity;
use BookStack\Actions\Tag;
-use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use Tests\TestCase;
public function test_tags_index_shows_tag_name_as_expected_with_right_counts()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
$page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
$html->assertElementContains('a[title="Assigned to Shelves"]', '0');
$html->assertElementContains('a[href$="/tags?name=Category"]', '2 unique values');
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$book->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
$resp = $this->asEditor()->get('/tags');
$this->withHtml($resp)->assertElementContains('a[title="Total tag usages"]', '3');
public function test_tag_index_can_be_searched()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
$resp = $this->asEditor()->get('/tags?search=cat');
public function test_tag_index_search_will_show_mulitple_values_of_a_single_tag_name()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->tags()->create(['name' => 'Animal', 'value' => 'Catfish']);
$page->tags()->create(['name' => 'Animal', 'value' => 'Catdog']);
public function test_tag_index_can_be_scoped_to_specific_tag_name()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->tags()->create(['name' => 'Category', 'value' => 'GreatTestContent']);
$page->tags()->create(['name' => 'Category', 'value' => 'OtherTestContent']);
$page->tags()->create(['name' => 'OtherTagName', 'value' => 'OtherValue']);
public function test_tags_index_adheres_to_page_permissions()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->tags()->create(['name' => 'SuperCategory', 'value' => 'GreatTestContent']);
$resp = $this->asEditor()->get('/tags');
$resp->assertSee('GreatTestContent');
$page->restricted = true;
- $this->regenEntityPermissions($page);
+ $this->entities->regenPermissions($page);
$resp = $this->asEditor()->get('/tags');
$resp->assertDontSee('SuperCategory');
{
$this->asEditor();
- foreach ($this->getEachEntityType() as $entity) {
+ foreach ($this->entities->all() as $entity) {
$entity->tags()->create(['name' => 'My Super Tag Name', 'value' => 'An-awesome-value']);
$html = $this->withHtml($this->get($entity->getUrl()));
$html->assertElementExists('body.tag-name-mysupertagname.tag-value-anawesomevalue.tag-pair-mysupertagname-anawesomevalue');
public function test_tag_classes_are_escaped()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->tags()->create(['name' => '<>']);
$resp = $this->asEditor()->get($page->getUrl());
$resp->assertDontSee('tag-name-<>', false);
namespace Tests;
-use BookStack\Entities\Models\Book;
use Illuminate\Support\Facades\Log;
class ErrorTest extends TestCase
{
$this->actingAs($this->getViewer());
$handler = $this->withTestLogger();
- $book = Book::query()->first();
+ $book = $this->entities->book();
// Ensure we're seeing errors
Log::error('cat');
use BookStack\Actions\Favourite;
use BookStack\Auth\User;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
class FavouriteTest extends TestCase
{
public function test_page_add_favourite_flow()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$resp = $this->actingAs($editor)->get($page->getUrl());
public function test_page_remove_favourite_flow()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
Favourite::query()->forceCreate([
'user_id' => $editor->id,
public function test_favourite_flow_with_own_permissions()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$user = User::factory()->create();
$book->owned_by = $user->id;
$book->save();
]);
}
- public function test_book_chapter_shelf_pages_contain_favourite_button()
+ public function test_each_entity_type_shows_favourite_button()
{
- $entities = [
- Bookshelf::query()->first(),
- Book::query()->first(),
- Chapter::query()->first(),
- ];
$this->actingAs($this->getEditor());
- foreach ($entities as $entity) {
+ foreach ($this->entities->all() as $entity) {
$resp = $this->get($entity->getUrl());
$this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/add"]');
}
$resp = $this->actingAs($editor)->get('/');
$this->withHtml($resp)->assertElementNotExists('#top-favourites');
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->favourites()->save((new Favourite())->forceFill(['user_id' => $editor->id]));
$resp = $this->get('/');
public function test_favourites_list_page_shows_favourites_and_has_working_pagination()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$resp = $this->actingAs($editor)->get('/favourites');
--- /dev/null
+<?php
+
+namespace Tests\Helpers;
+
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\BookRepo;
+use BookStack\Entities\Repos\BookshelfRepo;
+use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Entities\Repos\PageRepo;
+use Illuminate\Database\Eloquent\Builder;
+
+/**
+ * Class to provider and action entity models for common test case
+ * operations. Tracks handled models and only returns fresh models.
+ * Does not dedupe against nested/child/parent models.
+ */
+class EntityProvider
+{
+ /**
+ * @var array<string, int[]>
+ */
+ protected array $fetchCache = [
+ 'book' => [],
+ 'page' => [],
+ 'bookshelf' => [],
+ 'chapter' => [],
+ ];
+
+ /**
+ * Get an un-fetched page from the system.
+ */
+ public function page(callable $queryFilter = null): Page
+ {
+ /** @var Page $page */
+ $page = Page::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['page'])->first();
+ $this->addToCache($page);
+ return $page;
+ }
+
+ public function pageWithinChapter(): Page
+ {
+ return $this->page(fn(Builder $query) => $query->whereHas('chapter')->with('chapter'));
+ }
+
+ public function pageNotWithinChapter(): Page
+ {
+ return $this->page(fn(Builder $query) => $query->where('chapter_id', '=', 0));
+ }
+
+ /**
+ * Get an un-fetched chapter from the system.
+ */
+ public function chapter(callable $queryFilter = null): Chapter
+ {
+ /** @var Chapter $chapter */
+ $chapter = Chapter::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['chapter'])->first();
+ $this->addToCache($chapter);
+ return $chapter;
+ }
+
+ public function chapterHasPages(): Chapter
+ {
+ return $this->chapter(fn(Builder $query) => $query->whereHas('pages'));
+ }
+
+ /**
+ * Get an un-fetched book from the system.
+ */
+ public function book(callable $queryFilter = null): Book
+ {
+ /** @var Book $book */
+ $book = Book::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['book'])->first();
+ $this->addToCache($book);
+ return $book;
+ }
+
+ /**
+ * Get a book that has chapters and pages assigned.
+ */
+ public function bookHasChaptersAndPages(): Book
+ {
+ return $this->book(function (Builder $query) {
+ $query->has('chapters')->has('pages')->with(['chapters', 'pages']);
+ });
+ }
+
+ /**
+ * Get an un-fetched shelf from the system.
+ */
+ public function shelf(callable $queryFilter = null): Bookshelf
+ {
+ /** @var Bookshelf $shelf */
+ $shelf = Bookshelf::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['bookshelf'])->first();
+ $this->addToCache($shelf);
+ return $shelf;
+ }
+
+ /**
+ * Get all entity types from the system.
+ * @return array{page: Page, chapter: Chapter, book: Book, bookshelf: Bookshelf}
+ */
+ public function all(): array
+ {
+ return [
+ 'page' => $this->page(),
+ 'chapter' => $this->chapter(),
+ 'book' => $this->book(),
+ 'bookshelf' => $this->shelf(),
+ ];
+ }
+
+ public function updatePage(Page $page, array $data): Page
+ {
+ $this->addToCache($page);
+ return app()->make(PageRepo::class)->update($page, $data);
+ }
+
+ /**
+ * Create a book to page chain of entities that belong to a specific user.
+ * @return array{book: Book, chapter: Chapter, page: Page}
+ */
+ public function createChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
+ {
+ if (empty($updaterUser)) {
+ $updaterUser = $creatorUser;
+ }
+
+ $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
+ /** @var Book $book */
+ $book = Book::factory()->create($userAttrs);
+ $chapter = Chapter::factory()->create(array_merge(['book_id' => $book->id], $userAttrs));
+ $page = Page::factory()->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
+
+ $book->rebuildPermissions();
+ $this->addToCache([$page, $chapter, $book]);
+
+ return compact('book', 'chapter', 'page');
+ }
+
+ /**
+ * Create and return a new bookshelf.
+ */
+ public function newShelf(array $input = ['name' => 'test shelf', 'description' => 'My new test shelf']): Bookshelf
+ {
+ $shelf = app(BookshelfRepo::class)->create($input, []);
+ $this->addToCache($shelf);
+ return $shelf;
+ }
+
+ /**
+ * Create and return a new book.
+ */
+ public function newBook(array $input = ['name' => 'test book', 'description' => 'My new test book']): Book
+ {
+ $book = app(BookRepo::class)->create($input);
+ $this->addToCache($book);
+ return $book;
+ }
+
+ /**
+ * Create and return a new test chapter.
+ */
+ public function newChapter(array $input, Book $book): Chapter
+ {
+ $chapter = app(ChapterRepo::class)->create($input, $book);
+ $this->addToCache($chapter);
+ return $chapter;
+ }
+
+ /**
+ * Create and return a new test page.
+ */
+ public function newPage(array $input = ['name' => 'test page', 'html' => 'My new test page']): Page
+ {
+ $book = $this->book();
+ $pageRepo = app(PageRepo::class);
+ $draftPage = $pageRepo->getNewDraftPage($book);
+ $this->addToCache($draftPage);
+ return $pageRepo->publishDraft($draftPage, $input);
+ }
+
+ /**
+ * Regenerate the permission for an entity.
+ * Centralised to manage clearing of cached elements between requests.
+ */
+ public function regenPermissions(Entity $entity): void
+ {
+ $entity->rebuildPermissions();
+ $entity->load('jointPermissions');
+ }
+
+ /**
+ * Set the given entity as having restricted permissions, and apply the given
+ * permissions for the given roles.
+ * @param string[] $actions
+ * @param Role[] $roles
+ */
+ public function setPermissions(Entity $entity, array $actions = [], array $roles = []): void
+ {
+ $entity->restricted = true;
+ $entity->permissions()->delete();
+
+ $permissions = [];
+ foreach ($actions as $action) {
+ foreach ($roles as $role) {
+ $permissions[] = [
+ 'role_id' => $role->id,
+ 'action' => strtolower($action),
+ ];
+ }
+ }
+
+ $entity->permissions()->createMany($permissions);
+ $entity->save();
+ $entity->load('permissions');
+ $this->regenPermissions($entity);
+ }
+
+ /**
+ * @param Entity|Entity[] $entities
+ */
+ protected function addToCache($entities): void
+ {
+ if (!is_array($entities)) {
+ $entities = [$entities];
+ }
+
+ foreach ($entities as $entity) {
+ $this->fetchCache[$entity->getType()][] = $entity->id;
+ }
+ }
+}
<?php
-namespace Tests;
+namespace Tests\Helpers;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\ParallelTesting;
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Page;
class HomepageTest extends TestCase
{
$this->asEditor();
$name = 'My custom homepage';
$content = str_repeat('This is the body content of my custom homepage.', 20);
- $customPage = $this->newPage(['name' => $name, 'html' => $content]);
+ $customPage = $this->entities->newPage(['name' => $name, 'html' => $content]);
$this->setSettings(['app-homepage' => $customPage->id]);
$this->setSettings(['app-homepage-type' => 'page']);
$this->asEditor();
$name = 'My custom homepage';
$content = str_repeat('This is the body content of my custom homepage.', 20);
- $customPage = $this->newPage(['name' => $name, 'html' => $content]);
+ $customPage = $this->entities->newPage(['name' => $name, 'html' => $content]);
$this->setSettings([
'app-homepage' => $customPage->id,
'app-homepage-type' => 'page',
$this->asEditor();
$name = 'My custom homepage';
$content = str_repeat('This is the body content of my custom homepage.', 20);
- $customPage = $this->newPage(['name' => $name, 'html' => $content]);
+ $customPage = $this->entities->newPage(['name' => $name, 'html' => $content]);
$this->setSettings([
'app-homepage' => $customPage->id,
'app-homepage-type' => 'default',
public function test_custom_homepage_cannot_be_deleted_from_parent_deletion()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->setSettings([
'app-homepage' => $page->id,
'app-homepage-type' => 'page',
public function test_custom_homepage_renders_includes()
{
$this->asEditor();
- /** @var Page $included */
- $included = Page::query()->first();
+ $included = $this->entities->page();
$content = str_repeat('This is the body content of my custom homepage.', 20);
$included->html = $content;
$included->save();
$name = 'My custom homepage';
- $customPage = $this->newPage(['name' => $name, 'html' => '{{@' . $included->id . '}}']);
+ $customPage = $this->entities->newPage(['name' => $name, 'html' => '{{@' . $included->id . '}}']);
$this->setSettings(['app-homepage' => $customPage->id]);
$this->setSettings(['app-homepage-type' => 'page']);
{
$editor = $this->getEditor();
setting()->putUser($editor, 'bookshelves_view_type', 'grid');
- $shelf = Bookshelf::query()->firstOrFail();
+ $shelf = $this->entities->shelf();
$this->setSettings(['app-homepage-type' => 'bookshelves']);
$this->setSettings(['app-homepage-type' => 'bookshelves']);
$this->asEditor();
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$book = $shelf->books()->first();
// Ensure initially visible
$this->withHtml($homeVisit)->assertElementNotContains('.content-wrap', $book->name);
// Ensure is visible again with entity-level view permission
- $this->setEntityRestrictions($book, ['view'], [$editor->roles()->first()]);
+ $this->entities->setPermissions($book, ['view'], [$editor->roles()->first()]);
$homeVisit = $this->get('/');
$this->withHtml($homeVisit)->assertElementContains('.content-wrap', $shelf->name);
$this->withHtml($homeVisit)->assertElementContains('.content-wrap', $book->name);
namespace Tests;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\BaseRepo;
use BookStack\Entities\Repos\BookRepo;
use Illuminate\Support\Str;
public function test_page_tags()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$resp = $this->asEditor()->get($page->getUrl());
$tags = $this->getOpenGraphTags($resp);
public function test_chapter_tags()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->asEditor()->get($chapter->getUrl());
$tags = $this->getOpenGraphTags($resp);
public function test_book_tags()
{
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->asEditor()->get($book->getUrl());
$tags = $this->getOpenGraphTags($resp);
public function test_shelf_tags()
{
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$resp = $this->asEditor()->get($shelf->getUrl());
$tags = $this->getOpenGraphTags($resp);
namespace Tests\Permissions;
use BookStack\Auth\User;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use Tests\TestCase;
class EntityOwnerChangeTest extends TestCase
{
public function test_changing_page_owner()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$user = User::query()->where('id', '!=', $page->owned_by)->first();
$this->asAdmin()->put($page->getUrl('permissions'), ['owned_by' => $user->id]);
public function test_changing_chapter_owner()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$user = User::query()->where('id', '!=', $chapter->owned_by)->first();
$this->asAdmin()->put($chapter->getUrl('permissions'), ['owned_by' => $user->id]);
public function test_changing_book_owner()
{
- $book = Book::query()->first();
+ $book = $this->entities->book();
$user = User::query()->where('id', '!=', $book->owned_by)->first();
$this->asAdmin()->put($book->getUrl('permissions'), ['owned_by' => $user->id]);
public function test_changing_shelf_owner()
{
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$user = User::query()->where('id', '!=', $shelf->owned_by)->first();
$this->asAdmin()->put($shelf->getUrl('permissions'), ['owned_by' => $user->id]);
class EntityPermissionsTest extends TestCase
{
- /**
- * @var User
- */
- protected $user;
-
- /**
- * @var User
- */
- protected $viewer;
+ protected User $user;
+ protected User $viewer;
protected function setUp(): void
{
$this->user->roles->first(),
$this->viewer->roles->first(),
];
- $this->setEntityRestrictions($entity, $actions, $roles);
+ $this->entities->setPermissions($entity, $actions, $roles);
}
public function test_bookshelf_view_restriction()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$this->actingAs($this->user)
->get($shelf->getUrl())
public function test_bookshelf_update_restriction()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$this->actingAs($this->user)
->get($shelf->getUrl('/edit'))
public function test_bookshelf_delete_restriction()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$this->actingAs($this->user)
->get($shelf->getUrl('/delete'))
public function test_book_view_restriction()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookPage = $book->pages->first();
$bookChapter = $book->chapters->first();
public function test_book_create_restriction()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookUrl = $book->getUrl();
$resp = $this->actingAs($this->viewer)->get($bookUrl);
public function test_book_update_restriction()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookPage = $book->pages->first();
$bookChapter = $book->chapters->first();
public function test_book_delete_restriction()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookPage = $book->pages->first();
$bookChapter = $book->chapters->first();
public function test_chapter_view_restriction()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$chapterPage = $chapter->pages->first();
$chapterUrl = $chapter->getUrl();
public function test_chapter_create_restriction()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$chapterUrl = $chapter->getUrl();
$resp = $this->actingAs($this->user)->get($chapterUrl);
public function test_chapter_update_restriction()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$chapterPage = $chapter->pages->first();
$chapterUrl = $chapter->getUrl();
public function test_chapter_delete_restriction()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$chapterPage = $chapter->pages->first();
$chapterUrl = $chapter->getUrl();
public function test_page_view_restriction()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$pageUrl = $page->getUrl();
$this->actingAs($this->user)->get($pageUrl)->assertOk();
public function test_page_update_restriction()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$pageUrl = $page->getUrl();
$resp = $this->actingAs($this->user)
public function test_page_delete_restriction()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$pageUrl = $page->getUrl();
$this->actingAs($this->user)
public function test_restricted_pages_not_visible_in_book_navigation_on_pages()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages->first();
$page2 = $chapter->pages[2];
public function test_restricted_pages_not_visible_in_book_navigation_on_chapters()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages->first();
$this->setRestrictionsForTestRoles($page, []);
public function test_restricted_pages_not_visible_on_chapter_pages()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages->first();
$this->setRestrictionsForTestRoles($page, []);
public function test_restricted_chapter_pages_not_visible_on_book_page()
{
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$this->actingAs($this->user)
->get($chapter->book->getUrl())
->assertSee($chapter->pages->first()->name);
public function test_bookshelf_update_restriction_override()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$this->actingAs($this->viewer)
->get($shelf->getUrl('/edit'))
public function test_bookshelf_delete_restriction_override()
{
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
$this->actingAs($this->viewer)
->get($shelf->getUrl('/delete'))
public function test_book_create_restriction_override()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookUrl = $book->getUrl();
$resp = $this->actingAs($this->viewer)->get($bookUrl);
public function test_book_update_restriction_override()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookPage = $book->pages->first();
$bookChapter = $book->chapters->first();
public function test_book_delete_restriction_override()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookPage = $book->pages->first();
$bookChapter = $book->chapters->first();
public function test_page_visible_if_has_permissions_when_book_not_visible()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$bookChapter = $book->chapters->first();
$bookPage = $bookChapter->pages->first();
public function test_can_create_page_if_chapter_has_permissions_when_book_not_visible()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->setRestrictionsForTestRoles($book, []);
$bookChapter = $book->chapters->first();
$this->setRestrictionsForTestRoles($bookChapter, ['view']);
namespace Tests\Permissions;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
use Illuminate\Support\Str;
use Tests\TestCase;
{
public function test_page_content_without_view_access_hidden_on_chapter_export()
{
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$page = $chapter->pages()->firstOrFail();
$pageContent = Str::random(48);
$page->html = '<p>' . $pageContent . '</p>';
$resp->assertSee($pageContent);
}
- $this->setEntityRestrictions($page, []);
+ $this->entities->setPermissions($page, []);
foreach ($formats as $format) {
$resp = $this->get($chapter->getUrl("export/{$format}"));
public function test_page_content_without_view_access_hidden_on_book_export()
{
- $book = Book::query()->first();
+ $book = $this->entities->book();
$page = $book->pages()->firstOrFail();
$pageContent = Str::random(48);
$page->html = '<p>' . $pageContent . '</p>';
$resp->assertSee($pageContent);
}
- $this->setEntityRestrictions($page, []);
+ $this->entities->setPermissions($page, []);
foreach ($formats as $format) {
$resp = $this->get($book->getUrl("export/{$format}"));
class RolesTest extends TestCase
{
- protected $user;
+ protected User $user;
protected function setUp(): void
{
{
/** @var Page $otherUsersPage */
$otherUsersPage = Page::query()->first();
- $content = $this->createEntityChainBelongingToUser($this->user);
+ $content = $this->entities->createChainBelongingToUser($this->user);
// Set a different creator on the page we're checking to ensure
// that the owner fields are checked
{
/** @var Bookshelf $otherShelf */
$otherShelf = Bookshelf::query()->first();
- $ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
+ $ownShelf = $this->entities->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
$ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
- $this->regenEntityPermissions($ownShelf);
+ $this->entities->regenPermissions($ownShelf);
$this->checkAccessPermission('bookshelf-update-own', [
$ownShelf->getUrl('/edit'),
$this->giveUserPermissions($this->user, ['bookshelf-update-all']);
/** @var Bookshelf $otherShelf */
$otherShelf = Bookshelf::query()->first();
- $ownShelf = $this->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
+ $ownShelf = $this->entities->newShelf(['name' => 'test-shelf', 'slug' => 'test-shelf']);
$ownShelf->forceFill(['owned_by' => $this->user->id, 'updated_by' => $this->user->id])->save();
- $this->regenEntityPermissions($ownShelf);
+ $this->entities->regenPermissions($ownShelf);
$this->checkAccessPermission('bookshelf-delete-own', [
$ownShelf->getUrl('/delete'),
{
/** @var Book $otherBook */
$otherBook = Book::query()->take(1)->get()->first();
- $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
+ $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
$this->checkAccessPermission('book-update-own', [
$ownBook->getUrl() . '/edit',
], [
$this->giveUserPermissions($this->user, ['book-update-all']);
/** @var Book $otherBook */
$otherBook = Book::query()->take(1)->get()->first();
- $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
+ $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
$this->checkAccessPermission('book-delete-own', [
$ownBook->getUrl() . '/delete',
], [
{
/** @var Book $book */
$book = Book::query()->take(1)->get()->first();
- $ownBook = $this->createEntityChainBelongingToUser($this->user)['book'];
+ $ownBook = $this->entities->createChainBelongingToUser($this->user)['book'];
$this->checkAccessPermission('chapter-create-own', [
$ownBook->getUrl('/create-chapter'),
], [
public function test_chapter_create_all_permissions()
{
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->checkAccessPermission('chapter-create-all', [
$book->getUrl('/create-chapter'),
], [
{
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->first();
- $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
+ $ownChapter = $this->entities->createChainBelongingToUser($this->user)['chapter'];
$this->checkAccessPermission('chapter-update-own', [
$ownChapter->getUrl() . '/edit',
], [
$this->giveUserPermissions($this->user, ['chapter-update-all']);
/** @var Chapter $otherChapter */
$otherChapter = Chapter::query()->first();
- $ownChapter = $this->createEntityChainBelongingToUser($this->user)['chapter'];
+ $ownChapter = $this->entities->createChainBelongingToUser($this->user)['chapter'];
$this->checkAccessPermission('chapter-delete-own', [
$ownChapter->getUrl() . '/delete',
], [
public function test_page_create_own_permissions()
{
- /** @var Book $book */
- $book = Book::query()->first();
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $book = $this->entities->book();
+ $chapter = $this->entities->chapter();
- $entities = $this->createEntityChainBelongingToUser($this->user);
+ $entities = $this->entities->createChainBelongingToUser($this->user);
$ownBook = $entities['book'];
$ownChapter = $entities['chapter'];
public function test_page_create_all_permissions()
{
- /** @var Book $book */
- $book = Book::query()->first();
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $book = $this->entities->book();
+ $chapter = $this->entities->chapter();
$createUrl = $book->getUrl('/create-page');
$createUrlChapter = $chapter->getUrl('/create-page');
{
/** @var Page $otherPage */
$otherPage = Page::query()->first();
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->checkAccessPermission('page-update-own', [
$ownPage->getUrl() . '/edit',
], [
$this->giveUserPermissions($this->user, ['page-update-all']);
/** @var Page $otherPage */
$otherPage = Page::query()->first();
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->checkAccessPermission('page-delete-own', [
$ownPage->getUrl() . '/delete',
], [
public function test_image_delete_own_permission()
{
$this->giveUserPermissions($this->user, ['image-update-all']);
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$image = Image::factory()->create([
'uploaded_to' => $page->id,
'created_by' => $this->user->id,
{
$this->giveUserPermissions($this->user, ['image-update-all']);
$admin = $this->getAdmin();
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$image = Image::factory()->create(['uploaded_to' => $page->id, 'created_by' => $admin->id, 'updated_by' => $admin->id]);
$this->actingAs($this->user)->json('delete', '/images/' . $image->id)->assertStatus(403);
public function test_role_permission_removal()
{
// To cover issue fixed in f99c8ff99aee9beb8c692f36d4b84dc6e651e50a.
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$viewerRole = Role::getRole('viewer');
$viewer = $this->getViewer();
$this->actingAs($viewer)->get($page->getUrl())->assertOk();
$admin = $this->getAdmin();
// Book links
$book = Book::factory()->create(['created_by' => $admin->id, 'updated_by' => $admin->id]);
- $this->regenEntityPermissions($book);
+ $this->entities->regenPermissions($book);
$this->actingAs($this->getViewer())->get($book->getUrl())
->assertDontSee('Create a new page')
->assertDontSee('Add a chapter');
// Chapter links
$chapter = Chapter::factory()->create(['created_by' => $admin->id, 'updated_by' => $admin->id, 'book_id' => $book->id]);
- $this->regenEntityPermissions($chapter);
+ $this->entities->regenPermissions($chapter);
$this->actingAs($this->getViewer())->get($chapter->getUrl())
->assertDontSee('Create a new page')
->assertDontSee('Sort the current book');
public function test_comment_create_permission()
{
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->actingAs($this->user)
->addComment($ownPage)
public function test_comment_update_own_permission()
{
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->giveUserPermissions($this->user, ['comment-create-all']);
$this->actingAs($this->user)->addComment($ownPage);
/** @var Comment $comment */
public function test_comment_update_all_permission()
{
/** @var Page $ownPage */
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->asAdmin()->addComment($ownPage);
/** @var Comment $comment */
$comment = $ownPage->comments()->latest()->first();
public function test_comment_delete_own_permission()
{
/** @var Page $ownPage */
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->giveUserPermissions($this->user, ['comment-create-all']);
$this->actingAs($this->user)->addComment($ownPage);
public function test_comment_delete_all_permission()
{
/** @var Page $ownPage */
- $ownPage = $this->createEntityChainBelongingToUser($this->user)['page'];
+ $ownPage = $this->entities->createChainBelongingToUser($this->user)['page'];
$this->asAdmin()->addComment($ownPage);
/** @var Comment $comment */
$comment = $ownPage->comments()->latest()->first();
use BookStack\Auth\User;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
public function test_app_not_public()
{
$this->setSettings(['app-public' => 'false']);
- $book = Book::query()->first();
+ $book = $this->entities->book();
$this->get('/books')->assertRedirect('/login');
$this->get($book->getUrl())->assertRedirect('/login');
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->get($page->getUrl())->assertRedirect('/login');
}
$this->app->make(JointPermissionBuilder::class)->rebuildForRole($publicRole);
user()->clearPermissionCache();
- /** @var Chapter $chapter */
- $chapter = Chapter::query()->first();
+ $chapter = $this->entities->chapter();
$resp = $this->get($chapter->getUrl());
$resp->assertSee('New Page');
$this->withHtml($resp)->assertElementExists('a[href="' . $chapter->getUrl('/create-page') . '"]');
public function test_content_not_listed_on_404_for_public_users()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$page->fill(['name' => 'my testing random unique page name'])->save();
$this->asAdmin()->get($page->getUrl()); // Fake visit to show on recents
$resp = $this->get('/cats/dogs/hippos');
public function test_public_view_then_login_redirects_to_previous_content()
{
$this->setSettings(['app-public' => 'true']);
- /** @var Book $book */
- $book = Book::query()->first();
+ $book = $this->entities->book();
$resp = $this->get($book->getUrl());
$resp->assertSee($book->name);
public function test_access_hidden_content_then_login_redirects_to_intended_content()
{
$this->setSettings(['app-public' => 'true']);
- /** @var Book $book */
- $book = Book::query()->first();
- $this->setEntityRestrictions($book);
+ $book = $this->entities->book();
+ $this->entities->setPermissions($book);
$resp = $this->get($book->getUrl());
$resp->assertSee('Book not found');
namespace Tests\References;
-use BookStack\Entities\Models\Page;
use BookStack\References\CrossLinkParser;
use Tests\TestCase;
{
public function test_instance_with_entity_resolvers_matches_entity_links()
{
- $entities = $this->getEachEntityType();
- $otherPage = Page::query()->where('id', '!=', $entities['page']->id)->first();
+ $entities = $this->entities->all();
+ $otherPage = $this->entities->page();
$html = '
<a href="' . url('/link/' . $otherPage->id) . '#cat">Page Permalink</a>
public function test_similar_page_and_book_reference_links_dont_conflict()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = $page->book;
$html = '
namespace Tests\References;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Model;
{
public function test_references_created_on_page_update()
{
- /** @var Page $pageA */
- /** @var Page $pageB */
- $pageA = Page::query()->first();
- $pageB = Page::query()->where('id', '!=', $pageA->id)->first();
+ $pageA = $this->entities->page();
+ $pageB = $this->entities->page();
$this->assertDatabaseMissing('references', ['from_id' => $pageA->id, 'from_type' => $pageA->getMorphClass()]);
public function test_references_deleted_on_entity_delete()
{
- /** @var Page $pageA */
- /** @var Page $pageB */
- $pageA = Page::query()->first();
- $pageB = Page::query()->where('id', '!=', $pageA->id)->first();
+ $pageA = $this->entities->page();
+ $pageB = $this->entities->page();
$this->createReference($pageA, $pageB);
$this->createReference($pageB, $pageA);
public function test_references_to_count_visible_on_entity_show_view()
{
- $entities = $this->getEachEntityType();
- /** @var Page $otherPage */
- $otherPage = Page::query()->where('id', '!=', $entities['page']->id)->first();
+ $entities = $this->entities->all();
+ $otherPage = $this->entities->page();
$this->asEditor();
foreach ($entities as $entity) {
public function test_references_to_visible_on_references_page()
{
- $entities = $this->getEachEntityType();
+ $entities = $this->entities->all();
$this->asEditor();
foreach ($entities as $entity) {
$this->createReference($entities['page'], $entity);
public function test_reference_not_visible_if_view_permission_does_not_permit()
{
- /** @var Page $page */
- /** @var Page $pageB */
- $page = Page::query()->first();
- $pageB = Page::query()->where('id', '!=', $page->id)->first();
+ $page = $this->entities->page();
+ $pageB = $this->entities->page();
$this->createReference($pageB, $page);
- $this->setEntityRestrictions($pageB);
+ $this->entities->setPermissions($pageB);
$this->asEditor()->get($page->getUrl('/references'))->assertDontSee($pageB->name);
$this->asAdmin()->get($page->getUrl('/references'))->assertSee($pageB->name);
public function test_reference_page_shows_empty_state_with_no_references()
{
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asEditor()
->get($page->getUrl('/references'))
public function test_pages_leading_to_entity_updated_on_url_change()
{
- /** @var Page $pageA */
- /** @var Page $pageB */
- /** @var Book $book */
- $pageA = Page::query()->first();
- $pageB = Page::query()->where('id', '!=', $pageA->id)->first();
- $book = Book::query()->first();
+ $pageA = $this->entities->page();
+ $pageB = $this->entities->page();
+ $book = $this->entities->book();
foreach ([$pageA, $pageB] as $page) {
$page->html = '<a href="' . $book->getUrl() . '">Link</a>';
public function test_pages_linking_to_other_page_updated_on_parent_book_url_change()
{
- /** @var Page $bookPage */
- /** @var Page $otherPage */
- /** @var Book $book */
- $bookPage = Page::query()->first();
- $otherPage = Page::query()->where('id', '!=', $bookPage->id)->first();
+ $bookPage = $this->entities->page();
+ $otherPage = $this->entities->page();
$book = $bookPage->book;
$otherPage->html = '<a href="' . $bookPage->getUrl() . '">Link</a>';
public function test_pages_linking_to_chapter_updated_on_parent_book_url_change()
{
- /** @var Chapter $bookChapter */
- /** @var Page $otherPage */
- /** @var Book $book */
- $bookChapter = Chapter::query()->first();
- $otherPage = Page::query()->first();
+ $bookChapter = $this->entities->chapter();
+ $otherPage = $this->entities->page();
$book = $bookChapter->book;
$otherPage->html = '<a href="' . $bookChapter->getUrl() . '">Link</a>';
public function test_markdown_links_leading_to_entity_updated_on_url_change()
{
- /** @var Page $page */
- /** @var Book $book */
- $page = Page::query()->first();
- $book = Book::query()->first();
+ $page = $this->entities->page();
+ $book = $this->entities->book();
$bookUrl = $book->getUrl();
$markdown = '
namespace Tests\Settings;
use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Deletion;
-use BookStack\Entities\Models\Entity;
use BookStack\Entities\Models\Page;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
{
public function test_recycle_bin_routes_permissions()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$this->actingAs($editor)->delete($page->getUrl());
$deletion = Deletion::query()->firstOrFail();
public function test_recycle_bin_view()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = Book::query()->whereHas('pages')->whereHas('chapters')->withCount(['pages', 'chapters'])->first();
$editor = $this->getEditor();
$this->actingAs($editor)->delete($page->getUrl());
public function test_recycle_bin_empty()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$book = Book::query()->where('id', '!=', $page->book_id)->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
$editor = $this->getEditor();
$this->actingAs($editor)->delete($page->getUrl());
public function test_entity_restore()
{
- $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+ $book = $this->entities->bookHasChaptersAndPages();
$this->asEditor()->delete($book->getUrl());
$deletion = Deletion::query()->firstOrFail();
public function test_permanent_delete()
{
- $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+ $book = $this->entities->bookHasChaptersAndPages();
$this->asEditor()->delete($book->getUrl());
$deletion = Deletion::query()->firstOrFail();
public function test_permanent_delete_for_each_type()
{
- /** @var Entity $entity */
- foreach ([new Bookshelf(), new Book(), new Chapter(), new Page()] as $entity) {
- $entity = $entity->newQuery()->first();
+ foreach ($this->entities->all() as $type => $entity) {
$this->asEditor()->delete($entity->getUrl());
$deletion = Deletion::query()->orderBy('id', 'desc')->firstOrFail();
public function test_permanent_entity_delete_updates_existing_activity_with_entity_name()
{
- $page = Page::query()->firstOrFail();
+ $page = $this->entities->page();
$this->asEditor()->delete($page->getUrl());
$deletion = $page->deletions()->firstOrFail();
public function test_auto_clear_functionality_works()
{
config()->set('app.recycle_bin_lifetime', 5);
- $page = Page::query()->firstOrFail();
- $otherPage = Page::query()->where('id', '!=', $page->id)->firstOrFail();
+ $page = $this->entities->page();
+ $otherPage = $this->entities->page();
$this->asEditor()->delete($page->getUrl());
$this->assertDatabaseHas('pages', ['id' => $page->id]);
public function test_auto_clear_functionality_with_negative_time_keeps_forever()
{
config()->set('app.recycle_bin_lifetime', -1);
- $page = Page::query()->firstOrFail();
- $otherPage = Page::query()->where('id', '!=', $page->id)->firstOrFail();
+ $page = $this->entities->page();
+ $otherPage = $this->entities->page();
$this->asEditor()->delete($page->getUrl());
$this->assertEquals(1, Deletion::query()->count());
public function test_auto_clear_functionality_with_zero_time_deletes_instantly()
{
config()->set('app.recycle_bin_lifetime', 0);
- $page = Page::query()->firstOrFail();
+ $page = $this->entities->page();
$this->asEditor()->delete($page->getUrl());
$this->assertDatabaseMissing('pages', ['id' => $page->id]);
public function test_restore_page_shows_link_to_parent_restore_if_parent_also_deleted()
{
- /** @var Book $book */
- $book = Book::query()->whereHas('pages')->whereHas('chapters')->with(['pages', 'chapters'])->firstOrFail();
+ $book = $this->entities->bookHasChaptersAndPages();
$chapter = $book->chapters->first();
/** @var Page $page */
$page = $chapter->pages->first();
use BookStack\Auth\Permissions\RolePermission;
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
use BookStack\Entities\Models\Entity;
-use BookStack\Entities\Models\Page;
-use BookStack\Entities\Repos\BookRepo;
-use BookStack\Entities\Repos\BookshelfRepo;
-use BookStack\Entities\Repos\ChapterRepo;
-use BookStack\Entities\Repos\PageRepo;
use BookStack\Settings\SettingService;
use BookStack\Uploads\HttpFetcher;
use GuzzleHttp\Client;
use Monolog\Logger;
use Psr\Http\Client\ClientInterface;
use Ssddanbrown\AssertHtml\TestsHtml;
+use Tests\Helpers\EntityProvider;
+use Tests\Helpers\TestServiceProvider;
abstract class TestCase extends BaseTestCase
{
protected ?User $admin = null;
protected ?User $editor = null;
+ protected EntityProvider $entities;
+
+ protected function setUp(): void
+ {
+ $this->entities = new EntityProvider();
+ parent::setUp();
+ }
/**
* The base URL to use while testing the application.
return User::query()->where('system_name', '=', null)->get()->last();
}
- /**
- * Regenerate the permission for an entity.
- */
- protected function regenEntityPermissions(Entity $entity): void
- {
- $entity->rebuildPermissions();
- $entity->load('jointPermissions');
- }
-
- /**
- * Create and return a new bookshelf.
- */
- public function newShelf(array $input = ['name' => 'test shelf', 'description' => 'My new test shelf']): Bookshelf
- {
- return app(BookshelfRepo::class)->create($input, []);
- }
-
- /**
- * Create and return a new book.
- */
- public function newBook(array $input = ['name' => 'test book', 'description' => 'My new test book']): Book
- {
- return app(BookRepo::class)->create($input);
- }
-
- /**
- * Create and return a new test chapter.
- */
- public function newChapter(array $input, Book $book): Chapter
- {
- return app(ChapterRepo::class)->create($input, $book);
- }
-
- /**
- * Create and return a new test page.
- */
- public function newPage(array $input = ['name' => 'test page', 'html' => 'My new test page']): Page
- {
- $book = Book::query()->first();
- $pageRepo = app(PageRepo::class);
- $draftPage = $pageRepo->getNewDraftPage($book);
-
- return $pageRepo->publishDraft($draftPage, $input);
- }
-
/**
* Quickly sets an array of settings.
*/
}
}
- /**
- * Manually set some permissions on an entity.
- */
- protected function setEntityRestrictions(Entity $entity, array $actions = [], array $roles = []): void
- {
- $entity->restricted = true;
- $entity->permissions()->delete();
-
- $permissions = [];
- foreach ($actions as $action) {
- foreach ($roles as $role) {
- $permissions[] = [
- 'role_id' => $role->id,
- 'action' => strtolower($action),
- ];
- }
- }
- $entity->permissions()->createMany($permissions);
-
- $entity->save();
- $entity->load('permissions');
- $this->app->make(JointPermissionBuilder::class)->rebuildForEntity($entity);
- $entity->load('jointPermissions');
- }
-
/**
* Give the given user some permissions.
*/
return $permissionRepo->saveNewRole($roleData);
}
- /**
- * Create a group of entities that belong to a specific user.
- *
- * @return array{book: Book, chapter: Chapter, page: Page}
- */
- protected function createEntityChainBelongingToUser(User $creatorUser, ?User $updaterUser = null): array
- {
- if (empty($updaterUser)) {
- $updaterUser = $creatorUser;
- }
-
- $userAttrs = ['created_by' => $creatorUser->id, 'owned_by' => $creatorUser->id, 'updated_by' => $updaterUser->id];
- $book = Book::factory()->create($userAttrs);
- $chapter = Chapter::factory()->create(array_merge(['book_id' => $book->id], $userAttrs));
- $page = Page::factory()->create(array_merge(['book_id' => $book->id, 'chapter_id' => $chapter->id], $userAttrs));
-
- $this->app->make(JointPermissionBuilder::class)->rebuildForEntity($book);
-
- return compact('book', 'chapter', 'page');
- }
-
/**
* Mock the HttpFetcher service and return the given data on fetch.
*/
$this->assertDatabaseHas('activities', $detailsToCheck);
}
-
- /**
- * @return array{page: Page, chapter: Chapter, book: Book, bookshelf: Bookshelf}
- */
- protected function getEachEntityType(): array
- {
- return [
- 'page' => Page::query()->first(),
- 'chapter' => Chapter::query()->first(),
- 'book' => Book::query()->first(),
- 'bookshelf' => Bookshelf::query()->first(),
- ];
- }
}
};
Theme::listen(ThemeEvents::COMMONMARK_ENVIRONMENT_CONFIGURE, $callback);
- $page = Page::query()->first();
+ $page = $this->entities->page();
$content = new PageContent($page);
$content->setNewMarkdown('# test');
public function test_event_activity_logged()
{
- $book = Book::query()->first();
+ $book = $this->entities->book();
$args = [];
$callback = function (...$eventArgs) use (&$args) {
$args = $eventArgs;
{
/** @var Page $page */
/** @var Page $otherPage */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$otherPage = Page::query()->where('id', '!=', $page->id)->first();
$otherPage->html = '<p id="bkmrk-cool">This is a really cool section</p>';
$page->html = "<p>{{@{$otherPage->id}#bkmrk-cool}}</p>";
{
$bodyStartStr = 'garry-fought-against-the-panther';
$bodyEndStr = 'garry-lost-his-fight-with-grace';
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->usingThemeFolder(function (string $folder) use ($bodyStartStr, $bodyEndStr, $page) {
$viewDir = theme_path('layouts/parts');
public function test_file_upload()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$admin = $this->getAdmin();
$fileName = 'upload_test_file.txt';
public function test_file_upload_does_not_use_filename()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$fileName = 'upload_test_file.txt';
$upload = $this->asAdmin()->uploadFile($fileName, $page->id);
public function test_file_display_and_access()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$fileName = 'upload_test_file.txt';
public function test_attaching_link_to_page()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->asAdmin();
public function test_attachment_updating()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$attachment = $this->createAttachment($page);
public function test_file_deletion()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$fileName = 'deletion_test.txt';
$this->uploadFile($fileName, $page->id);
public function test_attachment_deletion_on_page_deletion()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$fileName = 'deletion_test.txt';
$this->uploadFile($fileName, $page->id);
{
$admin = $this->getAdmin();
$viewer = $this->getViewer();
- $page = Page::query()->first(); /** @var Page $page */
+ $page = $this->entities->page(); /** @var Page $page */
$this->actingAs($admin);
$fileName = 'permission_test.txt';
$this->uploadFile($fileName, $page->id);
public function test_data_and_js_links_cannot_be_attached_to_a_page()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$badLinks = [
public function test_file_access_with_open_query_param_provides_inline_response_with_correct_content_type()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$fileName = 'upload_test_file.txt';
public function test_html_file_access_with_open_forces_plain_content_type()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$attachment = $this->createUploadAttachment($page, 'test_file.html', '<html></html><p>testing</p>', 'text/html');
{
config()->set('filesystems.attachments', 'local_secure_restricted');
- $page = Page::query()->first();
+ $page = $this->entities->page();
$fileName = 'upload_test_file.txt';
$upload = $this->asAdmin()->uploadFile($fileName, $page->id);
public function test_image_upload()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
public function test_image_display_thumbnail_generation_does_not_increase_image_size()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
public function test_image_display_thumbnail_generation_for_apng_images_uses_original_file()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
public function test_image_usage()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$editor = $this->getEditor();
$this->actingAs($editor);
public function test_php_files_cannot_be_uploaded()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
public function test_php_like_files_cannot_be_uploaded()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
public function test_files_with_double_extensions_will_get_sanitized()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
];
foreach ($badNames as $name) {
$galleryFile = $this->getTestImage($name);
- $page = Page::query()->first();
+ $page = $this->entities->page();
$badPath = $this->getTestImagePath('gallery', $name);
$this->deleteImage($badPath);
config()->set('filesystems.images', 'local_secure');
$this->asEditor();
$galleryFile = $this->getTestImage('my-secure-test-upload.png');
- $page = Page::query()->first();
+ $page = $this->entities->page();
$expectedPath = storage_path('uploads/images/gallery/' . date('Y-m') . '/my-secure-test-upload.png');
$upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
config()->set('filesystems.images', 'local_secure');
$this->asEditor();
$galleryFile = $this->getTestImage('my-secure-test-upload.png');
- $page = Page::query()->first();
+ $page = $this->entities->page();
$expectedPath = storage_path('uploads/images/gallery/' . date('Y-m') . '/my-secure-test-upload.png');
$upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
config()->set('filesystems.images', 'local_secure_restricted');
$this->asEditor();
$galleryFile = $this->getTestImage('my-secure-restricted-test-upload.png');
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
$upload->assertStatus(200);
$this->get($expectedUrl)->assertOk();
- $this->setEntityRestrictions($page, [], []);
+ $this->entities->setPermissions($page, [], []);
$resp = $this->get($expectedUrl);
$resp->assertNotFound();
config()->set('filesystems.images', 'local_secure_restricted');
$this->asEditor();
$galleryFile = $this->getTestImage('my-secure-restricted-thumb-test-test.png');
- /** @var Page $page */
- $page = Page::query()->first();
+ $page = $this->entities->page();
$upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
$upload->assertStatus(200);
$this->get($expectedUrl)->assertOk();
- $this->setEntityRestrictions($page, [], []);
+ $this->entities->setPermissions($page, [], []);
$resp = $this->get($expectedUrl);
$resp->assertNotFound();
$export = $this->get($pageB->getUrl('/export/html'));
$this->assertStringContainsString($encodedImageContent, $export->getContent());
- $this->setEntityRestrictions($pageA, [], []);
+ $this->entities->setPermissions($pageA, [], []);
$export = $this->get($pageB->getUrl('/export/html'));
$this->assertStringNotContainsString($encodedImageContent, $export->getContent());
public function test_image_delete()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$imageName = 'first-image.png';
$relPath = $this->getTestImagePath('gallery', $imageName);
public function test_image_delete_does_not_delete_similar_images()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$imageName = 'first-image.png';
public function test_image_manager_delete_button_only_shows_with_permission()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->asAdmin();
$imageName = 'first-image.png';
$relPath = $this->getTestImagePath('gallery', $imageName);
public function test_deleted_unused_images()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$admin = $this->getAdmin();
$this->actingAs($admin);
protected function uploadGalleryImage(Page $page = null, ?string $testDataFileName = null)
{
if ($page === null) {
- $page = Page::query()->first();
+ $page = $this->entities->page();
}
$imageName = $testDataFileName ?? 'first-image.png';
use BookStack\Http\Request;
-use function request;
-use function url;
-
class UrlTest extends TestCase
{
public function test_url_helper_takes_custom_url_into_account()
use BookStack\Auth\Access\UserInviteService;
use BookStack\Auth\Role;
use BookStack\Auth\User;
-use BookStack\Entities\Models\Page;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Mockery\MockInterface;
public function test_delete_with_new_owner_id_changes_ownership()
{
- $page = Page::query()->first();
+ $page = $this->entities->page();
$owner = $page->ownedBy;
$newOwner = User::query()->where('id', '!=', $owner->id)->first();
namespace Tests\User;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Page;
use Tests\TestCase;
class UserPreferencesTest extends TestCase
public function test_shelf_view_type_change()
{
$editor = $this->getEditor();
- /** @var Bookshelf $shelf */
- $shelf = Bookshelf::query()->first();
+ $shelf = $this->entities->shelf();
setting()->putUser($editor, 'bookshelf_view_type', 'list');
$resp = $this->actingAs($editor)->get($shelf->getUrl())->assertSee('Grid View');
public function test_update_code_language_favourite()
{
$editor = $this->getEditor();
- $page = Page::query()->first();
+ $page = $this->entities->page();
$this->actingAs($editor);
$this->patch('/settings/users/update-code-language-favourite', ['language' => 'php', 'active' => true]);
public function test_profile_page_shows_recent_entities()
{
- $content = $this->createEntityChainBelongingToUser($this->user, $this->user);
+ $content = $this->entities->createChainBelongingToUser($this->user, $this->user);
$resp = $this->asAdmin()->get('/user/' . $this->user->slug);
// Check the recently created page is shown
->assertElementContains('#content-counts', '0 Chapters')
->assertElementContains('#content-counts', '0 Pages');
- $this->createEntityChainBelongingToUser($newUser, $newUser);
+ $this->entities->createChainBelongingToUser($newUser, $newUser);
$resp = $this->asAdmin()->get('/user/' . $newUser->slug)
->assertSee($newUser->name);
{
$newUser = User::factory()->create();
$this->actingAs($newUser);
- $entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
+ $entities = $this->entities->createChainBelongingToUser($newUser, $newUser);
Activity::add(ActivityType::BOOK_UPDATE, $entities['book']);
Activity::add(ActivityType::PAGE_CREATE, $entities['page']);
{
$newUser = User::factory()->create();
$this->actingAs($newUser);
- $entities = $this->createEntityChainBelongingToUser($newUser, $newUser);
+ $entities = $this->entities->createChainBelongingToUser($newUser, $newUser);
Activity::add(ActivityType::BOOK_UPDATE, $entities['book']);
Activity::add(ActivityType::PAGE_CREATE, $entities['page']);