]> BookStack Code Mirror - bookstack/blob - tests/Entity/PageTest.php
WYSIWYG: Updated TinyMCE from 6.5.1 to 6.7.2
[bookstack] / tests / Entity / PageTest.php
1 <?php
2
3 namespace Tests\Entity;
4
5 use BookStack\Entities\Models\Book;
6 use BookStack\Entities\Models\Page;
7 use Carbon\Carbon;
8 use Tests\TestCase;
9
10 class PageTest extends TestCase
11 {
12     public function test_create()
13     {
14         $chapter = $this->entities->chapter();
15         $page = Page::factory()->make([
16             'name' => 'My First Page',
17         ]);
18
19         $resp = $this->asEditor()->get($chapter->getUrl());
20         $this->withHtml($resp)->assertElementContains('a[href="' . $chapter->getUrl('/create-page') . '"]', 'New Page');
21
22         $resp = $this->get($chapter->getUrl('/create-page'));
23         /** @var Page $draftPage */
24         $draftPage = Page::query()
25             ->where('draft', '=', true)
26             ->orderBy('created_at', 'desc')
27             ->first();
28         $resp->assertRedirect($draftPage->getUrl());
29
30         $resp = $this->get($draftPage->getUrl());
31         $this->withHtml($resp)->assertElementContains('form[action="' . $draftPage->getUrl() . '"][method="POST"]', 'Save Page');
32
33         $resp = $this->post($draftPage->getUrl(), $draftPage->only('name', 'html'));
34         $draftPage->refresh();
35         $resp->assertRedirect($draftPage->getUrl());
36     }
37
38     public function test_page_view_when_creator_is_deleted_but_owner_exists()
39     {
40         $page = $this->entities->page();
41         $user = $this->users->viewer();
42         $owner = $this->users->editor();
43         $page->created_by = $user->id;
44         $page->owned_by = $owner->id;
45         $page->save();
46         $user->delete();
47
48         $resp = $this->asAdmin()->get($page->getUrl());
49         $resp->assertStatus(200);
50         $resp->assertSeeText('Owned by ' . $owner->name);
51     }
52
53     public function test_page_show_includes_pointer_section_select_mode_button()
54     {
55         $page = $this->entities->page();
56         $resp = $this->asEditor()->get($page->getUrl());
57         $this->withHtml($resp)->assertElementContains('.content-wrap button.screen-reader-only', 'Enter section select mode');
58     }
59
60     public function test_page_creation_with_markdown_content()
61     {
62         $this->setSettings(['app-editor' => 'markdown']);
63         $book = $this->entities->book();
64
65         $this->asEditor()->get($book->getUrl('/create-page'));
66         $draft = Page::query()->where('book_id', '=', $book->id)
67             ->where('draft', '=', true)->first();
68
69         $details = [
70             'markdown' => '# a title',
71             'html'     => '<h1>a title</h1>',
72             'name'     => 'my page',
73         ];
74         $resp = $this->post($book->getUrl("/draft/{$draft->id}"), $details);
75         $resp->assertRedirect();
76
77         $this->assertDatabaseHas('pages', [
78             'markdown' => $details['markdown'],
79             'name'     => $details['name'],
80             'id'       => $draft->id,
81             'draft'    => false,
82         ]);
83
84         $draft->refresh();
85         $resp = $this->get($draft->getUrl('/edit'));
86         $resp->assertSee('# a title');
87     }
88
89     public function test_page_delete()
90     {
91         $page = $this->entities->page();
92         $this->assertNull($page->deleted_at);
93
94         $deleteViewReq = $this->asEditor()->get($page->getUrl('/delete'));
95         $deleteViewReq->assertSeeText('Are you sure you want to delete this page?');
96
97         $deleteReq = $this->delete($page->getUrl());
98         $deleteReq->assertRedirect($page->getParent()->getUrl());
99         $this->assertActivityExists('page_delete', $page);
100
101         $page->refresh();
102         $this->assertNotNull($page->deleted_at);
103         $this->assertTrue($page->deletions()->count() === 1);
104
105         $redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
106         $this->assertNotificationContains($redirectReq, 'Page Successfully Deleted');
107     }
108
109     public function test_page_full_delete_removes_all_revisions()
110     {
111         $page = $this->entities->page();
112         $page->revisions()->create([
113             'html' => '<p>ducks</p>',
114             'name' => 'my page revision',
115             'type' => 'draft',
116         ]);
117         $page->revisions()->create([
118             'html' => '<p>ducks</p>',
119             'name' => 'my page revision',
120             'type' => 'revision',
121         ]);
122
123         $this->assertDatabaseHas('page_revisions', [
124             'page_id' => $page->id,
125         ]);
126
127         $this->asEditor()->delete($page->getUrl());
128         $this->asAdmin()->post('/settings/recycle-bin/empty');
129
130         $this->assertDatabaseMissing('page_revisions', [
131             'page_id' => $page->id,
132         ]);
133     }
134
135     public function test_page_copy()
136     {
137         $page = $this->entities->page();
138         $page->html = '<p>This is some test content</p>';
139         $page->save();
140
141         $currentBook = $page->book;
142         $newBook = Book::where('id', '!=', $currentBook->id)->first();
143
144         $resp = $this->asEditor()->get($page->getUrl('/copy'));
145         $resp->assertSee('Copy Page');
146
147         $movePageResp = $this->post($page->getUrl('/copy'), [
148             'entity_selection' => 'book:' . $newBook->id,
149             'name'             => 'My copied test page',
150         ]);
151         $pageCopy = Page::where('name', '=', 'My copied test page')->first();
152
153         $movePageResp->assertRedirect($pageCopy->getUrl());
154         $this->assertTrue($pageCopy->book->id == $newBook->id, 'Page was copied to correct book');
155         $this->assertStringContainsString('This is some test content', $pageCopy->html);
156     }
157
158     public function test_page_copy_with_markdown_has_both_html_and_markdown()
159     {
160         $page = $this->entities->page();
161         $page->html = '<h1>This is some test content</h1>';
162         $page->markdown = '# This is some test content';
163         $page->save();
164         $newBook = Book::where('id', '!=', $page->book->id)->first();
165
166         $this->asEditor()->post($page->getUrl('/copy'), [
167             'entity_selection' => 'book:' . $newBook->id,
168             'name'             => 'My copied test page',
169         ]);
170         $pageCopy = Page::where('name', '=', 'My copied test page')->first();
171
172         $this->assertStringContainsString('This is some test content', $pageCopy->html);
173         $this->assertEquals('# This is some test content', $pageCopy->markdown);
174     }
175
176     public function test_page_copy_with_no_destination()
177     {
178         $page = $this->entities->page();
179         $currentBook = $page->book;
180
181         $resp = $this->asEditor()->get($page->getUrl('/copy'));
182         $resp->assertSee('Copy Page');
183
184         $movePageResp = $this->post($page->getUrl('/copy'), [
185             'name' => 'My copied test page',
186         ]);
187
188         $pageCopy = Page::where('name', '=', 'My copied test page')->first();
189
190         $movePageResp->assertRedirect($pageCopy->getUrl());
191         $this->assertTrue($pageCopy->book->id == $currentBook->id, 'Page was copied to correct book');
192         $this->assertTrue($pageCopy->id !== $page->id, 'Page copy is not the same instance');
193     }
194
195     public function test_page_can_be_copied_without_edit_permission()
196     {
197         $page = $this->entities->page();
198         $currentBook = $page->book;
199         $newBook = Book::where('id', '!=', $currentBook->id)->first();
200         $viewer = $this->users->viewer();
201
202         $resp = $this->actingAs($viewer)->get($page->getUrl());
203         $resp->assertDontSee($page->getUrl('/copy'));
204
205         $newBook->owned_by = $viewer->id;
206         $newBook->save();
207         $this->permissions->grantUserRolePermissions($viewer, ['page-create-own']);
208         $this->permissions->regenerateForEntity($newBook);
209
210         $resp = $this->actingAs($viewer)->get($page->getUrl());
211         $resp->assertSee($page->getUrl('/copy'));
212
213         $movePageResp = $this->post($page->getUrl('/copy'), [
214             'entity_selection' => 'book:' . $newBook->id,
215             'name'             => 'My copied test page',
216         ]);
217         $movePageResp->assertRedirect();
218
219         $this->assertDatabaseHas('pages', [
220             'name'       => 'My copied test page',
221             'created_by' => $viewer->id,
222             'book_id'    => $newBook->id,
223         ]);
224     }
225
226     public function test_old_page_slugs_redirect_to_new_pages()
227     {
228         $page = $this->entities->page();
229
230         // Need to save twice since revisions are not generated in seeder.
231         $this->asAdmin()->put($page->getUrl(), [
232             'name' => 'super test',
233             'html' => '<p></p>',
234         ]);
235
236         $page->refresh();
237         $pageUrl = $page->getUrl();
238
239         $this->put($pageUrl, [
240             'name' => 'super test page',
241             'html' => '<p></p>',
242         ]);
243
244         $this->get($pageUrl)
245             ->assertRedirect("/books/{$page->book->slug}/page/super-test-page");
246     }
247
248     public function test_page_within_chapter_deletion_returns_to_chapter()
249     {
250         $chapter = $this->entities->chapter();
251         $page = $chapter->pages()->first();
252
253         $this->asEditor()->delete($page->getUrl())
254             ->assertRedirect($chapter->getUrl());
255     }
256
257     public function test_recently_updated_pages_view()
258     {
259         $user = $this->users->editor();
260         $content = $this->entities->createChainBelongingToUser($user);
261
262         $resp = $this->asAdmin()->get('/pages/recently-updated');
263         $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', $content['page']->name);
264     }
265
266     public function test_recently_updated_pages_view_shows_updated_by_details()
267     {
268         $user = $this->users->editor();
269         $page = $this->entities->page();
270
271         $this->actingAs($user)->put($page->getUrl(), [
272             'name' => 'Updated title',
273             'html' => '<p>Updated content</p>',
274         ]);
275
276         $resp = $this->asAdmin()->get('/pages/recently-updated');
277         $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', 'Updated 1 second ago by ' . $user->name);
278     }
279
280     public function test_recently_updated_pages_view_shows_parent_chain()
281     {
282         $user = $this->users->editor();
283         $page = $this->entities->pageWithinChapter();
284
285         $this->actingAs($user)->put($page->getUrl(), [
286             'name' => 'Updated title',
287             'html' => '<p>Updated content</p>',
288         ]);
289
290         $resp = $this->asAdmin()->get('/pages/recently-updated');
291         $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', $page->chapter->getShortName(42));
292         $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', $page->book->getShortName(42));
293     }
294
295     public function test_recently_updated_pages_view_does_not_show_parent_if_not_visible()
296     {
297         $user = $this->users->editor();
298         $page = $this->entities->pageWithinChapter();
299
300         $this->actingAs($user)->put($page->getUrl(), [
301             'name' => 'Updated title',
302             'html' => '<p>Updated content</p>',
303         ]);
304
305         $this->permissions->setEntityPermissions($page->book);
306         $this->permissions->setEntityPermissions($page, ['view'], [$user->roles->first()]);
307
308         $resp = $this->get('/pages/recently-updated');
309         $resp->assertDontSee($page->book->getShortName(42));
310         $resp->assertDontSee($page->chapter->getShortName(42));
311         $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', 'Updated title');
312     }
313
314     public function test_recently_updated_pages_on_home()
315     {
316         /** @var Page $page */
317         $page = Page::query()->orderBy('updated_at', 'asc')->first();
318         Page::query()->where('id', '!=', $page->id)->update([
319             'updated_at' => Carbon::now()->subSecond(1),
320         ]);
321
322         $resp = $this->asAdmin()->get('/');
323         $this->withHtml($resp)->assertElementNotContains('#recently-updated-pages', $page->name);
324
325         $this->put($page->getUrl(), [
326             'name' => $page->name,
327             'html' => $page->html,
328         ]);
329
330         $resp = $this->get('/');
331         $this->withHtml($resp)->assertElementContains('#recently-updated-pages', $page->name);
332     }
333 }