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