3 namespace Tests\Entity;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Activity\Models\Comment;
7 use BookStack\Entities\Models\Page;
10 class CommentStoreTest extends TestCase
12 public function test_add_comment()
15 $page = $this->entities->page();
17 $comment = Comment::factory()->make(['parent_id' => 2]);
18 $resp = $this->postJson("/comment/$page->id", $comment->getAttributes());
20 $resp->assertStatus(200);
21 $resp->assertSee($comment->html, false);
23 $pageResp = $this->get($page->getUrl());
24 $pageResp->assertSee($comment->html, false);
26 $this->assertDatabaseHas('comments', [
28 'entity_id' => $page->id,
29 'entity_type' => Page::newModelInstance()->getMorphClass(),
33 $this->assertActivityExists(ActivityType::COMMENT_CREATE);
35 public function test_add_comment_stores_content_reference_only_if_format_valid()
38 'bkmrk-my-title:4589284922:4-3' => true,
39 'bkmrk-my-title:4589284922:' => true,
40 'bkmrk-my-title:4589284922:abc' => false,
41 'my-title:4589284922:' => false,
42 'bkmrk-my-title-4589284922:' => false,
45 $page = $this->entities->page();
47 foreach ($validityByRefs as $ref => $valid) {
48 $this->asAdmin()->postJson("/comment/$page->id", [
49 'html' => '<p>My comment</p>',
51 'content_ref' => $ref,
55 $this->assertDatabaseHas('comments', ['entity_id' => $page->id, 'content_ref' => $ref]);
57 $this->assertDatabaseMissing('comments', ['entity_id' => $page->id, 'content_ref' => $ref]);
62 public function test_comment_edit()
65 $page = $this->entities->page();
67 $comment = Comment::factory()->make();
68 $this->postJson("/comment/$page->id", $comment->getAttributes());
70 $comment = $page->comments()->first();
71 $newHtml = '<p>updated text content</p>';
72 $resp = $this->putJson("/comment/$comment->id", [
76 $resp->assertStatus(200);
77 $resp->assertSee($newHtml, false);
78 $resp->assertDontSee($comment->html, false);
80 $this->assertDatabaseHas('comments', [
82 'entity_id' => $page->id,
85 $this->assertActivityExists(ActivityType::COMMENT_UPDATE);
88 public function test_comment_delete()
91 $page = $this->entities->page();
93 $comment = Comment::factory()->make();
94 $this->postJson("/comment/$page->id", $comment->getAttributes());
96 $comment = $page->comments()->first();
98 $resp = $this->delete("/comment/$comment->id");
99 $resp->assertStatus(200);
101 $this->assertDatabaseMissing('comments', [
102 'id' => $comment->id,
105 $this->assertActivityExists(ActivityType::COMMENT_DELETE);
108 public function test_comment_archive_and_unarchive()
111 $page = $this->entities->page();
113 $comment = Comment::factory()->make();
114 $page->comments()->save($comment);
117 $this->put("/comment/$comment->id/archive");
119 $this->assertDatabaseHas('comments', [
120 'id' => $comment->id,
124 $this->assertActivityExists(ActivityType::COMMENT_UPDATE);
126 $this->put("/comment/$comment->id/unarchive");
128 $this->assertDatabaseHas('comments', [
129 'id' => $comment->id,
133 $this->assertActivityExists(ActivityType::COMMENT_UPDATE);
136 public function test_archive_endpoints_require_delete_or_edit_permissions()
138 $viewer = $this->users->viewer();
139 $page = $this->entities->page();
141 $comment = Comment::factory()->make();
142 $page->comments()->save($comment);
145 $endpoints = ["/comment/$comment->id/archive", "/comment/$comment->id/unarchive"];
147 foreach ($endpoints as $endpoint) {
148 $resp = $this->actingAs($viewer)->put($endpoint);
149 $this->assertPermissionError($resp);
152 $this->permissions->grantUserRolePermissions($viewer, ['comment-delete-all']);
154 foreach ($endpoints as $endpoint) {
155 $resp = $this->actingAs($viewer)->put($endpoint);
159 $this->permissions->removeUserRolePermissions($viewer, ['comment-delete-all']);
160 $this->permissions->grantUserRolePermissions($viewer, ['comment-update-all']);
162 foreach ($endpoints as $endpoint) {
163 $resp = $this->actingAs($viewer)->put($endpoint);
168 public function test_non_top_level_comments_cant_be_archived_or_unarchived()
171 $page = $this->entities->page();
173 $comment = Comment::factory()->make();
174 $page->comments()->save($comment);
175 $subComment = Comment::factory()->make(['parent_id' => $comment->id]);
176 $page->comments()->save($subComment);
177 $subComment->refresh();
179 $resp = $this->putJson("/comment/$subComment->id/archive");
180 $resp->assertStatus(400);
182 $this->assertDatabaseHas('comments', [
183 'id' => $subComment->id,
187 $resp = $this->putJson("/comment/$subComment->id/unarchive");
188 $resp->assertStatus(400);
191 public function test_scripts_cannot_be_injected_via_comment_html()
193 $page = $this->entities->page();
195 $script = '<script>const a = "script";</script><script>const b = "sneakyscript";</script><p onclick="1">My lovely comment</p>';
196 $this->asAdmin()->postJson("/comment/$page->id", [
200 $pageView = $this->get($page->getUrl());
201 $pageView->assertDontSee($script, false);
202 $pageView->assertDontSee('sneakyscript', false);
203 $pageView->assertSee('<p>My lovely comment</p>', false);
205 $comment = $page->comments()->first();
206 $this->putJson("/comment/$comment->id", [
207 'html' => $script . '<p>updated</p>',
210 $pageView = $this->get($page->getUrl());
211 $pageView->assertDontSee($script, false);
212 $pageView->assertDontSee('sneakyscript', false);
213 $pageView->assertSee('<p>My lovely comment</p><p>updated</p>');
216 public function test_scripts_are_removed_even_if_already_in_db()
218 $page = $this->entities->page();
219 Comment::factory()->create([
220 'html' => '<script>superbadscript</script><script>superbadscript</script><p onclick="superbadonclick">scriptincommentest</p>',
221 'entity_type' => 'page', 'entity_id' => $page
224 $resp = $this->asAdmin()->get($page->getUrl());
225 $resp->assertSee('scriptincommentest', false);
226 $resp->assertDontSee('superbadscript', false);
227 $resp->assertDontSee('superbadonclick', false);
230 public function test_comment_html_is_limited()
232 $page = $this->entities->page();
233 $input = '<h1>Test</h1><p id="abc" href="beans">Content<a href="#cat" data-a="b">a</a><section>Hello</section><section>there</section></p>';
234 $expected = '<p>Content<a href="#cat">a</a></p>';
236 $resp = $this->asAdmin()->post("/comment/{$page->id}", ['html' => $input]);
238 $this->assertDatabaseHas('comments', [
239 'entity_type' => 'page',
240 'entity_id' => $page->id,
244 $comment = $page->comments()->first();
245 $resp = $this->put("/comment/{$comment->id}", ['html' => $input]);
247 $this->assertDatabaseHas('comments', [
248 'id' => $comment->id,
253 public function test_comment_html_spans_are_cleaned()
255 $page = $this->entities->page();
256 $input = '<p><span class="beans">Hello</span> do you have <span style="white-space: discard;">biscuits</span>?</p>';
257 $expected = '<p><span>Hello</span> do you have <span>biscuits</span>?</p>';
259 $resp = $this->asAdmin()->post("/comment/{$page->id}", ['html' => $input]);
261 $this->assertDatabaseHas('comments', [
262 'entity_type' => 'page',
263 'entity_id' => $page->id,
267 $comment = $page->comments()->first();
268 $resp = $this->put("/comment/{$comment->id}", ['html' => $input]);
270 $this->assertDatabaseHas('comments', [
271 'id' => $comment->id,