1 <?php namespace Tests\Entity;
3 use BookStack\Entities\Models\Bookshelf;
4 use BookStack\Entities\Models\Book;
5 use BookStack\Entities\Models\Chapter;
6 use BookStack\Entities\Models\Page;
7 use BookStack\Auth\UserRepo;
8 use BookStack\Entities\Repos\PageRepo;
10 use Tests\BrowserKitTest;
12 class EntityTest extends BrowserKitTest
15 public function test_entity_creation()
18 $book = $this->bookCreation();
19 $chapter = $this->chapterCreation($book);
20 $this->pageCreation($chapter);
23 $this->bookUpdate($book);
26 public function bookUpdate(Book $book)
28 $newName = $book->name . ' Updated';
31 ->visit($book->getUrl() . '/edit')
34 ->type($newName, '#name')
36 // Check page url and text
37 ->seePageIs($book->getUrl() . '-updated')
40 return Book::find($book->id);
43 public function test_book_sort_page_shows()
46 $bookToSort = $books[0];
48 ->visit($bookToSort->getUrl())
50 ->seePageIs($bookToSort->getUrl() . '/sort')
52 ->see($bookToSort->name);
55 public function test_book_sort_item_returns_book_content()
58 $bookToSort = $books[0];
59 $firstPage = $bookToSort->pages[0];
60 $firstChapter = $bookToSort->chapters[0];
62 ->visit($bookToSort->getUrl() . '/sort-item')
63 // Ensure book details are returned
64 ->see($bookToSort->name)
65 ->see($firstPage->name)
66 ->see($firstChapter->name);
69 public function test_toggle_book_view()
71 $editor = $this->getEditor();
72 setting()->putUser($editor, 'books_view_type', 'grid');
74 $this->actingAs($editor)
76 ->pageHasElement('.featured-image-container')
77 ->submitForm('List View')
80 ->pageNotHasElement('.featured-image-container');
82 $this->actingAs($editor)
84 ->submitForm('Grid View')
86 ->pageHasElement('.featured-image-container');
90 public function pageCreation($chapter)
92 $page = factory(Page::class)->make([
93 'name' => 'My First Page'
97 // Navigate to page create form
98 ->visit($chapter->getUrl())
101 $draftPage = Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
103 $this->seePageIs($draftPage->getUrl())
105 ->type($page->name, '#name')
106 ->type($page->html, '#html')
108 // Check redirect and page
109 ->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
112 $page = Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
116 public function chapterCreation(Book $book)
118 $chapter = factory(Chapter::class)->make([
119 'name' => 'My First Chapter'
123 // Navigate to chapter create page
124 ->visit($book->getUrl())
125 ->click('New Chapter')
126 ->seePageIs($book->getUrl() . '/create-chapter')
128 ->type($chapter->name, '#name')
129 ->type($chapter->description, '#description')
130 ->press('Save Chapter')
131 // Check redirect and landing page
132 ->seePageIs($book->getUrl() . '/chapter/my-first-chapter')
133 ->see($chapter->name)->see($chapter->description);
135 $chapter = Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
139 public function bookCreation()
141 $book = factory(Book::class)->make([
142 'name' => 'My First Book'
146 // Choose to create a book
147 ->click('Create New Book')
148 ->seePageIs('/create-book')
149 // Fill out form & save
150 ->type($book->name, '#name')
151 ->type($book->description, '#description')
153 // Check it redirects correctly
154 ->seePageIs('/books/my-first-book')
155 ->see($book->name)->see($book->description);
157 // Ensure duplicate names are given different slugs
159 ->visit('/create-book')
160 ->type($book->name, '#name')
161 ->type($book->description, '#description')
162 ->press('Save Book');
164 $expectedPattern = '/\/books\/my-first-book-[0-9a-zA-Z]{3}/';
165 $this->assertRegExp($expectedPattern, $this->currentUri, "Did not land on expected page [$expectedPattern].\n");
167 $book = Book::where('slug', '=', 'my-first-book')->first();
171 public function test_entities_viewable_after_creator_deletion()
173 // Create required assets and revisions
174 $creator = $this->getEditor();
175 $updater = $this->getEditor();
176 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
177 $this->actingAs($creator);
178 app(UserRepo::class)->destroy($creator);
179 app(PageRepo::class)->update($entities['page'], ['html' => '<p>hello!</p>>']);
181 $this->checkEntitiesViewable($entities);
184 public function test_entities_viewable_after_updater_deletion()
186 // Create required assets and revisions
187 $creator = $this->getEditor();
188 $updater = $this->getEditor();
189 $entities = $this->createEntityChainBelongingToUser($creator, $updater);
190 $this->actingAs($updater);
191 app(UserRepo::class)->destroy($updater);
192 app(PageRepo::class)->update($entities['page'], ['html' => '<p>Hello there!</p>']);
194 $this->checkEntitiesViewable($entities);
197 private function checkEntitiesViewable($entities)
199 // Check pages and books are visible.
201 $this->visit($entities['book']->getUrl())->seeStatusCode(200)
202 ->visit($entities['chapter']->getUrl())->seeStatusCode(200)
203 ->visit($entities['page']->getUrl())->seeStatusCode(200);
204 // Check revision listing shows no errors.
205 $this->visit($entities['page']->getUrl())
206 ->click('Revisions')->seeStatusCode(200);
209 public function test_recently_updated_pages_view()
211 $user = $this->getEditor();
212 $content = $this->createEntityChainBelongingToUser($user);
214 $this->asAdmin()->visit('/pages/recently-updated')
215 ->seeInNthElement('.entity-list .page', 0, $content['page']->name);
218 public function test_old_page_slugs_redirect_to_new_pages()
220 $page = Page::first();
221 $pageUrl = $page->getUrl();
222 $newPageUrl = '/books/' . $page->book->slug . '/page/super-test-page';
223 // Need to save twice since revisions are not generated in seeder.
224 $this->asAdmin()->visit($pageUrl)
225 ->clickInElement('#content', 'Edit')
226 ->type('super test', '#name')
227 ->press('Save Page');
229 $page = Page::first();
230 $pageUrl = $page->getUrl();
233 $this->visit($pageUrl)
234 ->clickInElement('#content', 'Edit')
235 ->type('super test page', '#name')
238 ->seePageIs($newPageUrl);
240 $this->visit($pageUrl)
241 ->seePageIs($newPageUrl);
244 public function test_recently_updated_pages_on_home()
246 $page = Page::orderBy('updated_at', 'asc')->first();
247 Page::where('id', '!=', $page->id)->update([
248 'updated_at' => Carbon::now()->subSecond(1)
250 $this->asAdmin()->visit('/')
251 ->dontSeeInElement('#recently-updated-pages', $page->name);
252 $this->visit($page->getUrl() . '/edit')
255 ->seeInElement('#recently-updated-pages', $page->name);
258 public function test_slug_multi_byte_url_safe()
260 $book = $this->newBook([
261 'name' => 'информация'
264 $this->assertEquals('informatsiya', $book->slug);
266 $book = $this->newBook([
270 $this->assertEquals('que', $book->slug);
273 public function test_slug_format()
275 $book = $this->newBook([
276 'name' => 'PartA / PartB / PartC'
279 $this->assertEquals('parta-partb-partc', $book->slug);
282 public function test_shelf_cancel_creation_returns_to_correct_place()
284 $shelf = Bookshelf::first();
286 // Cancel button from shelf goes back to shelf
287 $this->asEditor()->visit($shelf->getUrl('/create-book'))
290 ->seePageIs($shelf->getUrl());
292 // Cancel button from books goes back to books
293 $this->asEditor()->visit('/create-book')
296 ->seePageIs('/books');
298 // Cancel button from book edit goes back to book
299 $book = Book::first();
301 $this->asEditor()->visit($book->getUrl('/edit'))
304 ->seePageIs($book->getUrl());
307 public function test_page_within_chapter_deletion_returns_to_chapter()
309 $chapter = Chapter::query()->first();
310 $page = $chapter->pages()->first();
312 $this->asEditor()->visit($page->getUrl('/delete'))
313 ->submitForm('Confirm')
314 ->seePageIs($chapter->getUrl());