]> BookStack Code Mirror - bookstack/blob - tests/Entity/PageRevisionTest.php
Applied permissions to revision action visibility
[bookstack] / tests / Entity / PageRevisionTest.php
1 <?php
2
3 namespace Tests\Entity;
4
5 use BookStack\Actions\ActivityType;
6 use BookStack\Entities\Models\Page;
7 use Tests\TestCase;
8
9 class PageRevisionTest extends TestCase
10 {
11     public function test_revision_links_visible_to_viewer()
12     {
13         /** @var Page $page */
14         $page = Page::query()->first();
15
16         $html = $this->withHtml($this->asViewer()->get($page->getUrl()));
17         $html->assertLinkExists($page->getUrl('/revisions'));
18         $html->assertElementContains('a', 'Revisions');
19         $html->assertElementContains('a', 'Revision #1');
20     }
21
22     public function test_page_revision_views_viewable()
23     {
24         $this->asEditor();
25         $page = Page::first();
26         $this->createRevisions($page, 1, ['name' => 'updated page', 'html' => '<p>new content</p>']);
27         $pageRevision = $page->revisions->last();
28
29         $resp = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
30         $resp->assertStatus(200);
31         $resp->assertSee('new content');
32
33         $resp = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id . '/changes');
34         $resp->assertStatus(200);
35         $resp->assertSee('new content');
36     }
37
38     public function test_page_revision_preview_shows_content_of_revision()
39     {
40         $this->asEditor();
41         $page = Page::first();
42         $this->createRevisions($page, 1, ['name' => 'updated page', 'html' => '<p>new revision content</p>']);
43         $pageRevision = $page->revisions->last();
44         $this->createRevisions($page, 1, ['name' => 'updated page', 'html' => '<p>Updated content</p>']);
45
46         $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
47         $revisionView->assertStatus(200);
48         $revisionView->assertSee('new revision content');
49     }
50
51     public function test_page_revision_restore_updates_content()
52     {
53         $this->asEditor();
54         $page = Page::first();
55         $this->createRevisions($page, 1, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>']);
56         $this->createRevisions($page, 1, ['name' => 'updated page again', 'html' => '<p>new content</p>']);
57         $page = Page::find($page->id);
58
59         $pageView = $this->get($page->getUrl());
60         $pageView->assertDontSee('abc123');
61         $pageView->assertDontSee('def456');
62
63         $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
64         $restoreReq = $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
65         $page = Page::find($page->id);
66
67         $restoreReq->assertStatus(302);
68         $restoreReq->assertRedirect($page->getUrl());
69
70         $pageView = $this->get($page->getUrl());
71         $pageView->assertSee('abc123');
72         $pageView->assertSee('def456');
73     }
74
75     public function test_page_revision_restore_with_markdown_retains_markdown_content()
76     {
77         $this->asEditor();
78         $page = Page::first();
79         $this->createRevisions($page, 1, ['name' => 'updated page abc123', 'markdown' => '## New Content def456']);
80         $this->createRevisions($page, 1, ['name' => 'updated page again', 'markdown' => '## New Content Updated']);
81         $page = Page::find($page->id);
82
83         $pageView = $this->get($page->getUrl());
84         $pageView->assertDontSee('abc123');
85         $pageView->assertDontSee('def456');
86
87         $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
88         $restoreReq = $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
89         $page = Page::find($page->id);
90
91         $restoreReq->assertStatus(302);
92         $restoreReq->assertRedirect($page->getUrl());
93
94         $pageView = $this->get($page->getUrl());
95         $this->assertDatabaseHas('pages', [
96             'id'       => $page->id,
97             'markdown' => '## New Content def456',
98         ]);
99         $pageView->assertSee('abc123');
100         $pageView->assertSee('def456');
101     }
102
103     public function test_page_revision_restore_sets_new_revision_with_summary()
104     {
105         $this->asEditor();
106         $page = Page::first();
107         $this->createRevisions($page, 1, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'My first update']);
108         $this->createRevisions($page, 1, ['html' => '<p>new content</p>']);
109         $page->refresh();
110
111         $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
112         $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
113         $page->refresh();
114
115         $this->assertDatabaseHas('page_revisions', [
116             'page_id' => $page->id,
117             'text'    => 'new contente def456',
118             'type'    => 'version',
119             'summary' => "Restored from #{$revToRestore->id}; My first update",
120         ]);
121
122         $detail = "Revision #{$revToRestore->revision_number} (ID: {$revToRestore->id}) for page ID {$revToRestore->page_id}";
123         $this->assertActivityExists(ActivityType::REVISION_RESTORE, null, $detail);
124     }
125
126     public function test_page_revision_count_increments_on_update()
127     {
128         $page = Page::first();
129         $startCount = $page->revision_count;
130         $this->createRevisions($page, 1);
131
132         $this->assertTrue(Page::find($page->id)->revision_count === $startCount + 1);
133     }
134
135     public function test_revision_count_shown_in_page_meta()
136     {
137         $page = Page::first();
138         $this->createRevisions($page, 2);
139
140         $pageView = $this->get($page->getUrl());
141         $pageView->assertSee('Revision #' . $page->revision_count);
142     }
143
144     public function test_revision_deletion()
145     {
146         /** @var Page $page */
147         $page = Page::query()->first();
148         $this->createRevisions($page, 2);
149         $beforeRevisionCount = $page->revisions->count();
150
151         // Delete the first revision
152         $revision = $page->revisions->get(1);
153         $resp = $this->asEditor()->delete($revision->getUrl('/delete/'));
154         $resp->assertRedirect($page->getUrl('/revisions'));
155
156         $page->refresh();
157         $afterRevisionCount = $page->revisions->count();
158
159         $this->assertTrue($beforeRevisionCount === ($afterRevisionCount + 1));
160
161         $detail = "Revision #{$revision->revision_number} (ID: {$revision->id}) for page ID {$revision->page_id}";
162         $this->assertActivityExists(ActivityType::REVISION_DELETE, null, $detail);
163
164         // Try to delete the latest revision
165         $beforeRevisionCount = $page->revisions->count();
166         $resp = $this->asEditor()->delete($page->currentRevision->getUrl('/delete/'));
167         $resp->assertRedirect($page->getUrl('/revisions'));
168
169         $page->refresh();
170         $afterRevisionCount = $page->revisions->count();
171         $this->assertTrue($beforeRevisionCount === $afterRevisionCount);
172     }
173
174     public function test_revision_limit_enforced()
175     {
176         config()->set('app.revision_limit', 2);
177         $page = Page::first();
178         $this->createRevisions($page, 12);
179
180         $revisionCount = $page->revisions()->count();
181         $this->assertEquals(2, $revisionCount);
182     }
183
184     public function test_false_revision_limit_allows_many_revisions()
185     {
186         config()->set('app.revision_limit', false);
187         $page = Page::first();
188         $this->createRevisions($page, 12);
189
190         $revisionCount = $page->revisions()->count();
191         $this->assertEquals(12, $revisionCount);
192     }
193
194     public function test_revision_list_shows_editor_type()
195     {
196         /** @var Page $page */
197         $page = Page::first();
198         $this->createRevisions($page, 1, ['html' => 'new page html']);
199
200         $resp = $this->asAdmin()->get($page->refresh()->getUrl('/revisions'));
201         $this->withHtml($resp)->assertElementContains('td', '(WYSIWYG)');
202         $this->withHtml($resp)->assertElementNotContains('td', '(Markdown)');
203
204         $this->createRevisions($page, 1, ['markdown' => '# Some markdown content']);
205         $resp = $this->get($page->refresh()->getUrl('/revisions'));
206         $this->withHtml($resp)->assertElementContains('td', '(Markdown)');
207     }
208
209     public function test_revision_restore_action_only_visible_with_permission()
210     {
211         /** @var Page $page */
212         $page = Page::query()->first();
213         $this->createRevisions($page, 2);
214
215         $viewer = $this->getViewer();
216         $this->actingAs($viewer);
217         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
218         $respHtml->assertElementNotContains('.actions a', 'Restore');
219         $respHtml->assertElementNotExists('form[action$="/restore"]');
220
221         $this->giveUserPermissions($viewer, ['page-update-all']);
222
223         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
224         $respHtml->assertElementContains('.actions a', 'Restore');
225         $respHtml->assertElementExists('form[action$="/restore"]');
226     }
227
228     public function test_revision_delete_action_only_visible_with_permission()
229     {
230         /** @var Page $page */
231         $page = Page::query()->first();
232         $this->createRevisions($page, 2);
233
234         $viewer = $this->getViewer();
235         $this->actingAs($viewer);
236         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
237         $respHtml->assertElementNotContains('.actions a', 'Delete');
238         $respHtml->assertElementNotExists('form[action$="/delete"]');
239
240         $this->giveUserPermissions($viewer, ['page-delete-all']);
241
242         $respHtml = $this->withHtml($this->get($page->getUrl('/revisions')));
243         $respHtml->assertElementContains('.actions a', 'Delete');
244         $respHtml->assertElementExists('form[action$="/delete"]');
245     }
246
247     protected function createRevisions(Page $page, int $times, array $attrs = [])
248     {
249         $user = user();
250
251         for ($i = 0; $i < $times; $i++) {
252             $data = ['name' => 'Page update' . $i, 'summary' => 'Update entry' . $i];
253             if (!isset($attrs['markdown'])) {
254                 $data['html'] = '<p>My update page</p>';
255             }
256             $this->asAdmin()->put($page->getUrl(), array_merge($data, $attrs));
257             $page->refresh();
258         }
259
260         $this->actingAs($user);
261     }
262 }