3 namespace Tests\Entity;
5 use BookStack\Auth\UserRepo;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Bookshelf;
8 use BookStack\Entities\Models\Chapter;
9 use BookStack\Entities\Models\Page;
10 use BookStack\Entities\Repos\PageRepo;
12 use Tests\BrowserKitTest;
14 class EntityTest extends BrowserKitTest
16 public function test_entity_creation()
19 $book = $this->bookCreation();
20 $chapter = $this->chapterCreation($book);
21 $this->pageCreation($chapter);
24 $this->bookUpdate($book);
27 public function bookUpdate(Book $book)
29 $newName = $book->name . ' Updated';
32 ->visit($book->getUrl() . '/edit')
35 ->type($newName, '#name')
37 // Check page url and text
38 ->seePageIs($book->getUrl() . '-updated')
41 return Book::find($book->id);
44 public function test_book_sort_page_shows()
47 $bookToSort = $books[0];
49 ->visit($bookToSort->getUrl())
51 ->seePageIs($bookToSort->getUrl() . '/sort')
53 ->see($bookToSort->name);
56 public function test_book_sort_item_returns_book_content()
59 $bookToSort = $books[0];
60 $firstPage = $bookToSort->pages[0];
61 $firstChapter = $bookToSort->chapters[0];
63 ->visit($bookToSort->getUrl() . '/sort-item')
64 // Ensure book details are returned
65 ->see($bookToSort->name)
66 ->see($firstPage->name)
67 ->see($firstChapter->name);
70 public function pageCreation($chapter)
72 $page = factory(Page::class)->make([
73 'name' => 'My First Page',
77 // Navigate to page create form
78 ->visit($chapter->getUrl())
81 $draftPage = Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
83 $this->seePageIs($draftPage->getUrl())
85 ->type($page->name, '#name')
86 ->type($page->html, '#html')
88 // Check redirect and page
89 ->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
92 $page = Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
97 public function chapterCreation(Book $book)
99 $chapter = factory(Chapter::class)->make([
100 'name' => 'My First Chapter',
104 // Navigate to chapter create page
105 ->visit($book->getUrl())
106 ->click('New Chapter')
107 ->seePageIs($book->getUrl() . '/create-chapter')
109 ->type($chapter->name, '#name')
110 ->type($chapter->description, '#description')
111 ->press('Save Chapter')
112 // Check redirect and landing page
113 ->seePageIs($book->getUrl() . '/chapter/my-first-chapter')
114 ->see($chapter->name)->see($chapter->description);
116 $chapter = Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
121 public function bookCreation()
123 $book = factory(Book::class)->make([
124 'name' => 'My First Book',
128 // Choose to create a book
129 ->click('Create New Book')
130 ->seePageIs('/create-book')
131 // Fill out form & save
132 ->type($book->name, '#name')
133 ->type($book->description, '#description')
135 // Check it redirects correctly
136 ->seePageIs('/books/my-first-book')
137 ->see($book->name)->see($book->description);
139 // Ensure duplicate names are given different slugs
141 ->visit('/create-book')
142 ->type($book->name, '#name')
143 ->type($book->description, '#description')
144 ->press('Save Book');
146 $expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/';
147 $this->assertMatchesRegularExpression($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n");
149 $book = Book::where('slug', '=', 'my-first-book')->first();
154 public function test_entities_viewable_after_creator_deletion()
156 // Create required assets and revisions
157 $creator = $this->getEditor();
158 $updater = $this->getEditor();
159 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
160 $this->actingAs($creator);
161 app(UserRepo::class)->destroy($creator);
162 app(PageRepo::class)->update($entities['page'], ['html' => '<p>hello!</p>>']);
164 $this->checkEntitiesViewable($entities);
167 public function test_entities_viewable_after_updater_deletion()
169 // Create required assets and revisions
170 $creator = $this->getEditor();
171 $updater = $this->getEditor();
172 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
173 $this->actingAs($updater);
174 app(UserRepo::class)->destroy($updater);
175 app(PageRepo::class)->update($entities['page'], ['html' => '<p>Hello there!</p>']);
177 $this->checkEntitiesViewable($entities);
180 private function checkEntitiesViewable($entities)
182 // Check pages and books are visible.
184 $this->visit($entities['book']->getUrl())->seeStatusCode(200)
185 ->visit($entities['chapter']->getUrl())->seeStatusCode(200)
186 ->visit($entities['page']->getUrl())->seeStatusCode(200);
187 // Check revision listing shows no errors.
188 $this->visit($entities['page']->getUrl())
189 ->click('Revisions')->seeStatusCode(200);
192 public function test_recently_updated_pages_view()
194 $user = $this->getEditor();
195 $content = $this->createEntityChainBelongingToUser($user);
197 $this->asAdmin()->visit('/pages/recently-updated')
198 ->seeInNthElement('.entity-list .page', 0, $content['page']->name);
201 public function test_old_page_slugs_redirect_to_new_pages()
203 $page = Page::first();
204 $pageUrl = $page->getUrl();
205 $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
206 // Need to save twice since revisions are not generated in seeder.
207 $this->asAdmin()->visit($pageUrl)
208 ->clickInElement('#content', 'Edit')
209 ->type('super test', '#name')
210 ->press('Save Page');
212 $page = Page::first();
213 $pageUrl = $page->getUrl();
216 $this->visit($pageUrl)
217 ->clickInElement('#content', 'Edit')
218 ->type('super test page', '#name')
221 ->seePageIs($newPageUrl);
223 $this->visit($pageUrl)
224 ->seePageIs($newPageUrl);
227 public function test_recently_updated_pages_on_home()
229 $page = Page::orderBy('updated_at', 'asc')->first();
230 Page::where('id', '!=', $page->id)->update([
231 'updated_at' => Carbon::now()->subSecond(1),
233 $this->asAdmin()->visit('/')
234 ->dontSeeInElement('#recently-updated-pages', $page->name);
235 $this->visit($page->getUrl() . '/edit')
238 ->seeInElement('#recently-updated-pages', $page->name);
241 public function test_slug_multi_byte_url_safe()
243 $book = $this->newBook([
244 'name' => 'информация',
247 $this->assertEquals('informatsiya', $book->slug);
249 $book = $this->newBook([
253 $this->assertEquals('que', $book->slug);
256 public function test_slug_format()
258 $book = $this->newBook([
259 'name' => 'PartA / PartB / PartC',
262 $this->assertEquals('parta-partb-partc', $book->slug);
265 public function test_page_within_chapter_deletion_returns_to_chapter()
267 $chapter = Chapter::query()->first();
268 $page = $chapter->pages()->first();
270 $this->asEditor()->visit($page->getUrl('/delete'))
271 ->submitForm('Confirm')
272 ->seePageIs($chapter->getUrl());