]> BookStack Code Mirror - bookstack/blob - tests/Entity/EntityTest.php
Merge branch 'master' into master
[bookstack] / tests / Entity / EntityTest.php
1 <?php namespace Tests\Entity;
2
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;
9 use Carbon\Carbon;
10 use Tests\BrowserKitTest;
11
12 class EntityTest extends BrowserKitTest
13 {
14
15     public function test_entity_creation()
16     {
17         // Test Creation
18         $book = $this->bookCreation();
19         $chapter = $this->chapterCreation($book);
20         $this->pageCreation($chapter);
21
22         // Test Updating
23         $this->bookUpdate($book);
24     }
25
26     public function bookUpdate(Book $book)
27     {
28         $newName = $book->name . ' Updated';
29         $this->asAdmin()
30             // Go to edit screen
31             ->visit($book->getUrl() . '/edit')
32             ->see($book->name)
33             // Submit new name
34             ->type($newName, '#name')
35             ->press('Save Book')
36             // Check page url and text
37             ->seePageIs($book->getUrl() . '-updated')
38             ->see($newName);
39
40         return Book::find($book->id);
41     }
42
43     public function test_book_sort_page_shows()
44     {
45         $books =  Book::all();
46         $bookToSort = $books[0];
47         $this->asAdmin()
48             ->visit($bookToSort->getUrl())
49             ->click('Sort')
50             ->seePageIs($bookToSort->getUrl() . '/sort')
51             ->seeStatusCode(200)
52             ->see($bookToSort->name);
53     }
54
55     public function test_book_sort_item_returns_book_content()
56     {
57         $books =  Book::all();
58         $bookToSort = $books[0];
59         $firstPage = $bookToSort->pages[0];
60         $firstChapter = $bookToSort->chapters[0];
61         $this->asAdmin()
62             ->visit($bookToSort->getUrl() . '/sort-item')
63             // Ensure book details are returned
64             ->see($bookToSort->name)
65             ->see($firstPage->name)
66             ->see($firstChapter->name);
67     }
68
69     public function test_toggle_book_view()
70     {
71         $editor = $this->getEditor();
72         setting()->putUser($editor, 'books_view_type', 'grid');
73
74         $this->actingAs($editor)
75             ->visit('/books')
76             ->pageHasElement('.featured-image-container')
77             ->submitForm('List View')
78             // Check redirection.
79             ->seePageIs('/books')
80             ->pageNotHasElement('.featured-image-container');
81
82         $this->actingAs($editor)
83             ->visit('/books')
84             ->submitForm('Grid View')
85             ->seePageIs('/books')
86             ->pageHasElement('.featured-image-container');
87
88     }
89
90     public function pageCreation($chapter)
91     {
92         $page = factory(Page::class)->make([
93             'name' => 'My First Page'
94         ]);
95
96         $this->asAdmin()
97             // Navigate to page create form
98             ->visit($chapter->getUrl())
99             ->click('New Page');
100
101         $draftPage = Page::where('draft', '=', true)->orderBy('created_at', 'desc')->first();
102
103         $this->seePageIs($draftPage->getUrl())
104             // Fill out form
105             ->type($page->name, '#name')
106             ->type($page->html, '#html')
107             ->press('Save Page')
108             // Check redirect and page
109             ->seePageIs($chapter->book->getUrl() . '/page/my-first-page')
110             ->see($page->name);
111
112         $page = Page::where('slug', '=', 'my-first-page')->where('chapter_id', '=', $chapter->id)->first();
113         return $page;
114     }
115
116     public function chapterCreation(Book $book)
117     {
118         $chapter = factory(Chapter::class)->make([
119             'name' => 'My First Chapter'
120         ]);
121
122         $this->asAdmin()
123             // Navigate to chapter create page
124             ->visit($book->getUrl())
125             ->click('New Chapter')
126             ->seePageIs($book->getUrl() . '/create-chapter')
127             // Fill out form
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);
134
135         $chapter = Chapter::where('slug', '=', 'my-first-chapter')->where('book_id', '=', $book->id)->first();
136         return $chapter;
137     }
138
139     public function bookCreation()
140     {
141         $book = factory(Book::class)->make([
142             'name' => 'My First Book'
143         ]);
144         $this->asAdmin()
145             ->visit('/books')
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')
152             ->press('Save Book')
153             // Check it redirects correctly
154             ->seePageIs('/books/my-first-book')
155             ->see($book->name)->see($book->description);
156
157         // Ensure duplicate names are given different slugs
158         $this->asAdmin()
159             ->visit('/create-book')
160             ->type($book->name, '#name')
161             ->type($book->description, '#description')
162             ->press('Save Book');
163
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");
166
167         $book = Book::where('slug', '=', 'my-first-book')->first();
168         return $book;
169     }
170
171     public function test_entities_viewable_after_creator_deletion()
172     {
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>>']);
180
181         $this->checkEntitiesViewable($entities);
182     }
183
184     public function test_entities_viewable_after_updater_deletion()
185     {
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>']);
193
194         $this->checkEntitiesViewable($entities);
195     }
196
197     private function checkEntitiesViewable($entities)
198     {
199         // Check pages and books are visible.
200         $this->asAdmin();
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);
207     }
208
209     public function test_recently_updated_pages_view()
210     {
211         $user = $this->getEditor();
212         $content = $this->createEntityChainBelongingToUser($user);
213
214         $this->asAdmin()->visit('/pages/recently-updated')
215             ->seeInNthElement('.entity-list .page', 0, $content['page']->name);
216     }
217
218     public function test_old_page_slugs_redirect_to_new_pages()
219     {
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');
228
229         $page = Page::first();
230         $pageUrl = $page->getUrl();
231
232         // Second Save
233         $this->visit($pageUrl)
234             ->clickInElement('#content', 'Edit')
235             ->type('super test page', '#name')
236             ->press('Save Page')
237             // Check redirect
238             ->seePageIs($newPageUrl);
239
240         $this->visit($pageUrl)
241             ->seePageIs($newPageUrl);
242     }
243
244     public function test_recently_updated_pages_on_home()
245     {
246         $page = Page::orderBy('updated_at', 'asc')->first();
247         Page::where('id', '!=', $page->id)->update([
248             'updated_at' => Carbon::now()->subSecond(1)
249         ]);
250         $this->asAdmin()->visit('/')
251             ->dontSeeInElement('#recently-updated-pages', $page->name);
252         $this->visit($page->getUrl() . '/edit')
253             ->press('Save Page')
254             ->visit('/')
255             ->seeInElement('#recently-updated-pages', $page->name);
256     }
257
258     public function test_slug_multi_byte_url_safe()
259     {
260         $book = $this->newBook([
261             'name' => 'информация'
262         ]);
263
264         $this->assertEquals('informatsiya', $book->slug);
265
266         $book = $this->newBook([
267             'name' => '¿Qué?'
268         ]);
269
270         $this->assertEquals('que', $book->slug);
271     }
272
273     public function test_slug_format()
274     {
275         $book = $this->newBook([
276             'name' => 'PartA / PartB / PartC'
277         ]);
278
279         $this->assertEquals('parta-partb-partc', $book->slug);
280     }
281
282     public function test_shelf_cancel_creation_returns_to_correct_place()
283     {
284         $shelf = Bookshelf::first();
285
286         // Cancel button from shelf goes back to shelf
287         $this->asEditor()->visit($shelf->getUrl('/create-book'))
288             ->see('Cancel')
289             ->click('Cancel')
290             ->seePageIs($shelf->getUrl());
291
292         // Cancel button from books goes back to books
293         $this->asEditor()->visit('/create-book')
294             ->see('Cancel')
295             ->click('Cancel')
296             ->seePageIs('/books');
297
298         // Cancel button from book edit goes back to book
299         $book = Book::first();
300
301         $this->asEditor()->visit($book->getUrl('/edit'))
302             ->see('Cancel')
303             ->click('Cancel')
304             ->seePageIs($book->getUrl());
305     }
306
307     public function test_page_within_chapter_deletion_returns_to_chapter()
308     {
309         $chapter = Chapter::query()->first();
310         $page = $chapter->pages()->first();
311
312         $this->asEditor()->visit($page->getUrl('/delete'))
313             ->submitForm('Confirm')
314             ->seePageIs($chapter->getUrl());
315     }
316
317 }