CommentsTest   F
last analyzed

Complexity

Total Complexity 64

Size/Duplication

Total Lines 1213
Duplicated Lines 0 %

Importance

Changes 9
Bugs 0 Features 3
Metric Value
eloc 678
c 9
b 0
f 3
dl 0
loc 1213
rs 3.202
wmc 64

52 Methods

Rating   Name   Duplication   Size   Complexity  
A testIsPreview() 0 8 1
A testCanCreate() 0 11 1
A testMarkSpam() 0 6 1
A testGetEscapedComment() 0 3 1
A testMarkApproved() 0 6 1
A testCanEdit() 0 15 1
A testGetMember() 0 18 1
A testCanView() 0 15 1
A testSpamClass() 0 8 1
A testCanDelete() 0 15 1
A testMarkUnapproved() 0 5 1
A testCastingHelper() 0 3 1
A testGetTitle() 0 6 1
A testCommentsList() 0 71 1
A testGenerate() 0 3 1
A getMethod() 0 6 1
A testReplies() 0 66 1
A testSanitizesWithAllowHtml() 0 35 2
A testGetCMSFieldsCommentHasAuthor() 0 22 2
A testDefaultTemplateRendersHtmlWithAllowHtml() 0 33 2
A testPagedReplies() 0 26 1
A testMemberSalt() 0 3 1
A setUp() 0 8 1
A testLinks() 0 34 1
A testLink() 0 14 1
A testCheckRequest() 0 3 1
A testGetParentClassName() 0 5 1
A testFieldLabels() 0 18 2
A testRequireDefaultRecords() 0 3 1
A testGetParentTitle() 0 11 1
A testCommenterURLWrite() 0 16 2
A testDefaultEnabled() 0 51 1
A testOnBeforeDelete() 0 19 1
A testPermalink() 0 4 1
A testCommentCMSModerationList() 0 40 1
A testGetRepliesEnabled() 0 37 1
A testGetCMSFieldsWithParentComment() 0 28 2
A testGetCMSFields() 0 17 2
A testGetAuthorName() 0 26 1
A testHamComment() 0 41 1
A testApproveComment() 0 41 1
A testSpamComment() 0 41 1
A testAllReplies() 0 33 1
A testDeleteComment() 0 38 4
A testReplyForm() 0 39 2
A testPurifyHtml() 0 12 2
A testGravatar() 0 28 1
A testGetParent() 0 7 1
A testGetToken() 0 3 1
A testUpdateDepth() 0 20 1
A testCanPostComment() 0 58 1
A testAddToUrl() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like CommentsTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CommentsTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SilverStripe\Comments\Tests;
4
5
use HTMLPurifier;
0 ignored issues
show
Bug introduced by Will Rossiter
The type HTMLPurifier was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use HTMLPurifier_Config;
0 ignored issues
show
Bug introduced by Robbie Averill
The type HTMLPurifier_Config was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use ReflectionClass;
8
use SilverStripe\Comments\Extensions\CommentsExtension;
9
use SilverStripe\Comments\Model\Comment;
10
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
11
use SilverStripe\Comments\Tests\Stubs\CommentableItemDisabled;
12
use SilverStripe\Comments\Tests\Stubs\CommentableItemEnabled;
13
use SilverStripe\Core\Config\Config;
14
use SilverStripe\Dev\FunctionalTest;
15
use SilverStripe\ORM\DataObject;
16
use SilverStripe\Security\Member;
17
use SilverStripe\Security\Security;
18
19
class CommentsTest extends FunctionalTest
20
{
21
    protected static $fixture_file = 'CommentsTest.yml';
22
23
    protected static $extra_dataobjects = [
24
        CommentableItem::class,
25
        CommentableItemEnabled::class,
26
        CommentableItemDisabled::class,
27
    ];
28
29
    protected function setUp()
30
    {
31
        parent::setUp();
32
33
        // Set good default values
34
        Config::modify()->merge(CommentsExtension::class, 'comments', [
35
            'enabled' => true,
36
            'comment_permalink_prefix' => 'comment-',
37
        ]);
38
    }
39
40
    public function testCommentsList()
41
    {
42
        // comments don't require moderation so unmoderated comments can be
43
        // shown but not spam posts
44
        Config::modify()->merge(CommentableItem::class, 'comments', [
45
            'require_moderation_nonmembers' => false,
46
            'require_moderation' => false,
47
            'require_moderation_cms' => false,
48
        ]);
49
50
        $item = $this->objFromFixture(CommentableItem::class, 'spammed');
51
52
        $this->assertListEquals([
53
            ['Name' => 'Comment 1'],
54
            ['Name' => 'Comment 3']
55
        ], $item->Comments(), 'Only 2 non spam posts should be shown');
56
57
        // when moderated, only moderated, non spam posts should be shown.
58
        Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => true]);
59
60
        // Check that require_moderation overrides this option
61
        Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation' => true]);
62
63
        $this->assertListEquals(array(
64
            array('Name' => 'Comment 3')
65
        ), $item->Comments(), 'Only 1 non spam, moderated post should be shown');
66
        $this->assertEquals(1, $item->Comments()->Count());
67
68
        // require_moderation_nonmembers still filters out unmoderated comments
69
        Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation' => false]);
70
        $this->assertEquals(1, $item->Comments()->Count());
71
72
        Config::modify()->merge(CommentableItem::class, 'comments', ['require_moderation_nonmembers' => false]);
73
        $this->assertEquals(2, $item->Comments()->Count());
74
75
        // With unmoderated comments set to display in frontend
76
        Config::modify()->merge(CommentableItem::class, 'comments', [
77
            'require_moderation' => true,
78
            'frontend_moderation' => true,
79
        ]);
80
        $this->assertEquals(1, $item->Comments()->Count());
81
82
        $this->logInWithPermission('ADMIN');
83
        $this->assertEquals(2, $item->Comments()->Count());
84
85
        // With spam comments set to display in frontend
86
        Config::modify()->merge(CommentableItem::class, 'comments', [
87
            'require_moderation' => true,
88
            'frontend_moderation' => false,
89
            'frontend_spam' => true,
90
        ]);
91
92
        $this->logOut();
93
        $this->assertEquals(1, $item->Comments()->Count());
94
95
        $this->logInWithPermission('ADMIN');
96
        $this->assertEquals(2, $item->Comments()->Count());
97
98
99
        // With spam and unmoderated comments set to display in frontend
100
        Config::modify()->merge(CommentableItem::class, 'comments', [
101
            'require_moderation' => true,
102
            'frontend_moderation' => true,
103
            'frontend_spam' => true,
104
        ]);
105
106
        $this->logOut();
107
        $this->assertEquals(1, $item->Comments()->Count());
108
109
        $this->logInWithPermission('ADMIN');
110
        $this->assertEquals(4, $item->Comments()->Count());
111
    }
112
113
    /**
114
     * Test moderation options configured via the CMS
115
     */
116
    public function testCommentCMSModerationList()
117
    {
118
        Config::modify()->merge(CommentableItem::class, 'comments', [
119
            'require_moderation' => true,
120
            'require_moderation_cms' => true,
121
        ]);
122
123
        $item = $this->objFromFixture(CommentableItem::class, 'spammed');
124
125
        $this->assertEquals('None', $item->getModerationRequired());
126
127
        $this->assertListEquals([
128
            ['Name' => 'Comment 1'],
129
            ['Name' => 'Comment 3']
130
        ], $item->Comments(), 'Only 2 non spam posts should be shown');
131
132
        // when moderated, only moderated, non spam posts should be shown.
133
        $item->ModerationRequired = 'NonMembersOnly';
134
        $item->write();
135
136
        $this->assertEquals('NonMembersOnly', $item->getModerationRequired());
137
138
        // Check that require_moderation overrides this option
139
        $item->ModerationRequired = 'Required';
140
        $item->write();
141
        $this->assertEquals('Required', $item->getModerationRequired());
142
143
        $this->assertListEquals([
144
            ['Name' => 'Comment 3']
145
        ], $item->Comments(), 'Only 1 non spam, moderated post should be shown');
146
        $this->assertEquals(1, $item->Comments()->Count());
147
148
        // require_moderation_nonmembers still filters out unmoderated comments
149
        $item->ModerationRequired = 'NonMembersOnly';
150
        $item->write();
151
        $this->assertEquals(1, $item->Comments()->Count());
152
153
        $item->ModerationRequired = 'None';
154
        $item->write();
155
        $this->assertEquals(2, $item->Comments()->Count());
156
    }
157
158
    public function testCanPostComment()
159
    {
160
        Config::modify()->merge(CommentableItem::class, 'comments', [
161
            'require_login' => false,
162
            'require_login_cms' => false,
163
            'required_permission' => false,
164
        ]);
165
        /** @var CommentableItem&CommentsExtension $item */
166
        $item = $this->objFromFixture(CommentableItem::class, 'first');
167
        /** @var CommentableItem&CommentsExtension $item2 */
168
        $item2 = $this->objFromFixture(CommentableItem::class, 'second');
169
170
        // Test restriction free commenting
171
        $this->logOut();
172
        $this->assertFalse($item->CommentsRequireLogin);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property CommentsRequireLogin does not exist on SilverStripe\Comments\Tests\Stubs\CommentableItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
173
        $this->assertTrue($item->canPostComment());
0 ignored issues
show
Bug introduced by Damian Mooyman
The method canPostComment() does not exist on SilverStripe\Comments\Tests\Stubs\CommentableItem. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

173
        $this->assertTrue($item->/** @scrutinizer ignore-call */ canPostComment());
Loading history...
174
175
        // Test permission required to post
176
        Config::modify()->merge(CommentableItem::class, 'comments', [
177
            'require_login' => true,
178
            'required_permission' => 'POSTING_PERMISSION',
179
        ]);
180
        $this->assertTrue($item->CommentsRequireLogin);
181
        $this->assertFalse($item->canPostComment());
182
        $this->logInWithPermission('WRONG_ONE');
183
        $this->assertFalse($item->canPostComment());
184
        $this->logInWithPermission('POSTING_PERMISSION');
185
        $this->assertTrue($item->canPostComment());
186
        $this->logInWithPermission('ADMIN');
187
        $this->assertTrue($item->canPostComment());
188
189
        // Test require login to post, but not any permissions
190
        Config::modify()->merge(CommentableItem::class, 'comments', [
191
            'required_permission' => false,
192
        ]);
193
        $this->assertTrue($item->CommentsRequireLogin);
194
195
        $this->logOut();
196
        $this->assertFalse($item->canPostComment());
197
        $this->logInWithPermission('ANY_PERMISSION');
198
        $this->assertTrue($item->canPostComment());
199
200
        // Test options set via CMS
201
        Config::modify()->merge(CommentableItem::class, 'comments', [
202
            'require_login' => true,
203
            'require_login_cms' => true,
204
        ]);
205
        $this->assertFalse($item->CommentsRequireLogin);
206
        $this->assertTrue($item2->CommentsRequireLogin);
207
208
        $this->logOut();
209
        $this->assertTrue($item->canPostComment());
210
        $this->assertFalse($item2->canPostComment());
211
212
        // Login grants permission to post
213
        $this->logInWithPermission('ANY_PERMISSION');
214
        $this->assertTrue($item->canPostComment());
215
        $this->assertTrue($item2->canPostComment());
216
    }
217
    public function testDeleteComment()
218
    {
219
        // Test anonymous user
220
        $this->logOut();
221
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
222
        $commentID = $comment->ID;
223
        $this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
224
        $delete = $this->get('comments/delete/' . $comment->ID . '?ajax=1');
225
        $this->assertEquals(403, $delete->getStatusCode());
226
        $check = DataObject::get_by_id(Comment::class, $commentID);
227
        $this->assertTrue($check && $check->exists());
228
229
        // Test non-authenticated user
230
        $this->logInAs('visitor');
231
        $this->assertNull($comment->DeleteLink(), 'No permission to see delete link');
232
233
        // Test authenticated user
234
        $this->logInAs('commentadmin');
235
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
236
        $commentID = $comment->ID;
237
        $adminComment1Link = $comment->DeleteLink();
238
        $this->assertContains('comments/delete/' . $commentID . '?t=', $adminComment1Link);
239
240
        // Test that this link can't be shared / XSS exploited
241
        $this->logInAs('commentadmin2');
242
        $delete = $this->get($adminComment1Link);
243
        $this->assertEquals(400, $delete->getStatusCode());
244
        $check = DataObject::get_by_id(Comment::class, $commentID);
245
        $this->assertTrue($check && $check->exists());
246
247
        // Test that this other admin can delete the comment with their own link
248
        $adminComment2Link = $comment->DeleteLink();
249
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
250
        $this->autoFollowRedirection = false;
251
        $delete = $this->get($adminComment2Link);
252
        $this->assertEquals(302, $delete->getStatusCode());
253
        $check = DataObject::get_by_id(Comment::class, $commentID);
254
        $this->assertFalse($check && $check->exists());
255
    }
256
257
    public function testSpamComment()
258
    {
259
        // Test anonymous user
260
        $this->logOut();
261
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
262
        $commentID = $comment->ID;
263
        $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
264
        $spam = $this->get('comments/spam/' . $comment->ID . '?ajax=1');
265
        $this->assertEquals(403, $spam->getStatusCode());
266
        $check = DataObject::get_by_id(Comment::class, $commentID);
267
        $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
268
269
        // Test non-authenticated user
270
        $this->logInAs('visitor');
271
        $this->assertNull($comment->SpamLink(), 'No permission to see mark as spam link');
272
273
        // Test authenticated user
274
        $this->logInAs('commentadmin');
275
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
276
        $commentID = $comment->ID;
277
        $adminComment1Link = $comment->SpamLink();
278
        $this->assertContains('comments/spam/' . $commentID . '?t=', $adminComment1Link);
279
280
        // Test that this link can't be shared / XSS exploited
281
        $this->logInAs('commentadmin2');
282
        $spam = $this->get($adminComment1Link);
283
        $this->assertEquals(400, $spam->getStatusCode());
284
        $check = DataObject::get_by_id(Comment::class, $comment->ID);
285
        $this->assertEquals(0, $check->IsSpam, 'No permission to mark as spam');
286
287
        // Test that this other admin can spam the comment with their own link
288
        $adminComment2Link = $comment->SpamLink();
289
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
290
        $this->autoFollowRedirection = false;
291
        $spam = $this->get($adminComment2Link);
292
        $this->assertEquals(302, $spam->getStatusCode());
293
        $check = DataObject::get_by_id(Comment::class, $commentID);
294
        $this->assertEquals(1, $check->IsSpam);
295
296
        // Cannot re-spam spammed comment
297
        $this->assertNull($check->SpamLink());
298
    }
299
300
    public function testHamComment()
301
    {
302
        // Test anonymous user
303
        $this->logOut();
304
        $comment = $this->objFromFixture(Comment::class, 'secondComC');
305
        $commentID = $comment->ID;
306
        $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
307
        $ham = $this->get('comments/ham/' . $comment->ID . '?ajax=1');
308
        $this->assertEquals(403, $ham->getStatusCode());
309
        $check = DataObject::get_by_id(Comment::class, $commentID);
310
        $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
311
312
        // Test non-authenticated user
313
        $this->logInAs('visitor');
314
        $this->assertNull($comment->HamLink(), 'No permission to see mark as ham link');
315
316
        // Test authenticated user
317
        $this->logInAs('commentadmin');
318
        $comment = $this->objFromFixture(Comment::class, 'secondComC');
319
        $commentID = $comment->ID;
320
        $adminComment1Link = $comment->HamLink();
321
        $this->assertContains('comments/ham/' . $commentID . '?t=', $adminComment1Link);
322
323
        // Test that this link can't be shared / XSS exploited
324
        $this->logInAs('commentadmin2');
325
        $ham = $this->get($adminComment1Link);
326
        $this->assertEquals(400, $ham->getStatusCode());
327
        $check = DataObject::get_by_id(Comment::class, $comment->ID);
328
        $this->assertEquals(1, $check->IsSpam, 'No permission to mark as ham');
329
330
        // Test that this other admin can ham the comment with their own link
331
        $adminComment2Link = $comment->HamLink();
332
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
333
        $this->autoFollowRedirection = false;
334
        $ham = $this->get($adminComment2Link);
335
        $this->assertEquals(302, $ham->getStatusCode());
336
        $check = DataObject::get_by_id(Comment::class, $commentID);
337
        $this->assertEquals(0, $check->IsSpam);
338
339
        // Cannot re-ham hammed comment
340
        $this->assertNull($check->HamLink());
341
    }
342
343
    public function testApproveComment()
344
    {
345
        // Test anonymous user
346
        $this->logOut();
347
        $comment = $this->objFromFixture(Comment::class, 'secondComB');
348
        $commentID = $comment->ID;
349
        $this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
350
        $approve = $this->get('comments/approve/' . $comment->ID . '?ajax=1');
351
        $this->assertEquals(403, $approve->getStatusCode());
352
        $check = DataObject::get_by_id(Comment::class, $commentID);
353
        $this->assertEquals(0, $check->Moderated, 'No permission to approve');
354
355
        // Test non-authenticated user
356
        $this->logInAs('visitor');
357
        $this->assertNull($comment->ApproveLink(), 'No permission to see approve link');
358
359
        // Test authenticated user
360
        $this->logInAs('commentadmin');
361
        $comment = $this->objFromFixture(Comment::class, 'secondComB');
362
        $commentID = $comment->ID;
363
        $adminComment1Link = $comment->ApproveLink();
364
        $this->assertContains('comments/approve/' . $commentID . '?t=', $adminComment1Link);
365
366
        // Test that this link can't be shared / XSS exploited
367
        $this->logInAs('commentadmin2');
368
        $approve = $this->get($adminComment1Link);
369
        $this->assertEquals(400, $approve->getStatusCode());
370
        $check = DataObject::get_by_id(Comment::class, $comment->ID);
371
        $this->assertEquals(0, $check->Moderated, 'No permission to approve');
372
373
        // Test that this other admin can approve the comment with their own link
374
        $adminComment2Link = $comment->ApproveLink();
375
        $this->assertNotEquals($adminComment2Link, $adminComment1Link);
376
        $this->autoFollowRedirection = false;
377
        $approve = $this->get($adminComment2Link);
378
        $this->assertEquals(302, $approve->getStatusCode());
379
        $check = DataObject::get_by_id(Comment::class, $commentID);
380
        $this->assertEquals(1, $check->Moderated);
381
382
        // Cannot re-approve approved comment
383
        $this->assertNull($check->ApproveLink());
384
    }
385
386
    public function testCommenterURLWrite()
387
    {
388
        $comment = new Comment();
389
        // We only care about the CommenterURL, so only set that
390
        // Check a http and https URL. Add more test urls here as needed.
391
        $protocols = [
392
            'Http',
393
            'Https',
394
        ];
395
        $url = '://example.com';
396
397
        foreach ($protocols as $protocol) {
398
            $comment->CommenterURL = $protocol . $url;
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property CommenterURL does not exist on SilverStripe\Comments\Model\Comment. Since you implemented __set, consider adding a @property annotation.
Loading history...
399
            // The protocol should stay as if, assuming it is valid
400
            $comment->write();
401
            $this->assertEquals($comment->CommenterURL, $protocol . $url, $protocol . ':// is a valid protocol');
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property CommenterURL does not exist on SilverStripe\Comments\Model\Comment. Since you implemented __get, consider adding a @property annotation.
Loading history...
402
        }
403
    }
404
405
    public function testSanitizesWithAllowHtml()
406
    {
407
        if (!class_exists('\\HTMLPurifier')) {
408
            $this->markTestSkipped('HTMLPurifier class not found');
409
        }
410
411
        // Add p for paragraph
412
        // NOTE: The config method appears to append to the existing array
413
        Config::modify()->merge(CommentableItem::class, 'comments', [
414
            'html_allowed_elements' => ['p'],
415
        ]);
416
417
        // Without HTML allowed
418
        $comment1 = new Comment();
419
        $comment1->AllowHtml = false;
420
        $comment1->ParentClass = CommentableItem::class;
0 ignored issues
show
Bug Best Practice introduced by Robbie Averill
The property ParentClass does not exist on SilverStripe\Comments\Model\Comment. Since you implemented __set, consider adding a @property annotation.
Loading history...
421
        $comment1->Comment = '<p><script>alert("w00t")</script>my comment</p>';
422
        $comment1->write();
423
        $this->assertEquals(
424
            '<p><script>alert("w00t")</script>my comment</p>',
425
            $comment1->Comment,
426
            'Does not remove HTML tags with html_allowed=false, ' .
427
            'which is correct behaviour because the HTML will be escaped'
428
        );
429
430
        // With HTML allowed
431
        $comment2 = new Comment();
432
        $comment2->AllowHtml = true;
433
        $comment2->ParentClass = CommentableItem::class;
434
        $comment2->Comment = '<p><script>alert("w00t")</script>my comment</p>';
435
        $comment2->write();
436
        $this->assertEquals(
437
            '<p>my comment</p>',
438
            $comment2->Comment,
439
            'Removes HTML tags which are not on the whitelist'
440
        );
441
    }
442
443
    public function testDefaultTemplateRendersHtmlWithAllowHtml()
444
    {
445
        if (!class_exists('\\HTMLPurifier')) {
446
            $this->markTestSkipped('HTMLPurifier class not found');
447
        }
448
449
        Config::modify()->merge(CommentableItem::class, 'comments', [
450
            'html_allowed_elements' => ['p'],
451
        ]);
452
453
        $item = new CommentableItem();
454
        $item->write();
455
456
        // Without HTML allowed
457
        $comment = new Comment();
458
        $comment->Comment = '<p>my comment</p>';
459
        $comment->AllowHtml = false;
460
        $comment->ParentID = $item->ID;
461
        $comment->ParentClass = CommentableItem::class;
0 ignored issues
show
Bug Best Practice introduced by Robbie Averill
The property ParentClass does not exist on SilverStripe\Comments\Model\Comment. Since you implemented __set, consider adding a @property annotation.
Loading history...
462
        $comment->write();
463
464
        $html = $item->customise(['CommentsEnabled' => true])->renderWith('CommentsInterface');
465
        $this->assertContains(
466
            '&lt;p&gt;my comment&lt;/p&gt;',
467
            $html
468
        );
469
470
        $comment->AllowHtml = true;
471
        $comment->write();
472
        $html = $item->customise(['CommentsEnabled' => true])->renderWith('CommentsInterface');
473
        $this->assertContains(
474
            '<p>my comment</p>',
475
            $html
476
        );
477
    }
478
479
480
    /**
481
     * Tests whether comments are enabled or disabled by default
482
     */
483
    public function testDefaultEnabled()
484
    {
485
        Config::modify()->merge(CommentableItem::class, 'comments', [
486
            'enabled_cms' => true,
487
            'require_moderation_cms' => true,
488
            'require_login_cms' => true,
489
        ]);
490
491
        // With default = true
492
        $obj = new CommentableItem();
493
        $this->assertTrue((bool)$obj->getCommentsOption('enabled'), "Default setting is enabled");
0 ignored issues
show
Bug introduced by Damian Mooyman
The method getCommentsOption() does not exist on SilverStripe\Comments\Tests\Stubs\CommentableItem. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

493
        $this->assertTrue((bool)$obj->/** @scrutinizer ignore-call */ getCommentsOption('enabled'), "Default setting is enabled");
Loading history...
494
        $this->assertTrue((bool)$obj->ProvideComments);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property ProvideComments does not exist on SilverStripe\Comments\Tests\Stubs\CommentableItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
495
        $this->assertEquals('None', $obj->ModerationRequired);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property ModerationRequired does not exist on SilverStripe\Comments\Tests\Stubs\CommentableItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
496
        $this->assertFalse((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property CommentsRequireLogin does not exist on SilverStripe\Comments\Tests\Stubs\CommentableItem. Since you implemented __get, consider adding a @property annotation.
Loading history...
497
498
        $obj = new CommentableItemEnabled();
499
        $this->assertTrue((bool)$obj->ProvideComments);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property ProvideComments does not exist on SilverStripe\Comments\Te...\CommentableItemEnabled. Since you implemented __get, consider adding a @property annotation.
Loading history...
500
        $this->assertEquals('Required', $obj->ModerationRequired);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property ModerationRequired does not exist on SilverStripe\Comments\Te...\CommentableItemEnabled. Since you implemented __get, consider adding a @property annotation.
Loading history...
501
        $this->assertTrue((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property CommentsRequireLogin does not exist on SilverStripe\Comments\Te...\CommentableItemEnabled. Since you implemented __get, consider adding a @property annotation.
Loading history...
502
503
        $obj = new CommentableItemDisabled();
504
        $this->assertFalse((bool)$obj->ProvideComments);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property ProvideComments does not exist on SilverStripe\Comments\Te...CommentableItemDisabled. Since you implemented __get, consider adding a @property annotation.
Loading history...
505
        $this->assertEquals('None', $obj->ModerationRequired);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property ModerationRequired does not exist on SilverStripe\Comments\Te...CommentableItemDisabled. Since you implemented __get, consider adding a @property annotation.
Loading history...
506
        $this->assertFalse((bool)$obj->CommentsRequireLogin);
0 ignored issues
show
Bug Best Practice introduced by Damian Mooyman
The property CommentsRequireLogin does not exist on SilverStripe\Comments\Te...CommentableItemDisabled. Since you implemented __get, consider adding a @property annotation.
Loading history...
507
508
        // With default = false
509
        // Because of config rules about falsey values, apply config to object directly
510
        Config::modify()->merge(CommentableItem::class, 'comments', [
511
            'enabled' => false,
512
            'require_login' => true,
513
            'require_moderation' => true,
514
        ]);
515
516
        $obj = new CommentableItem();
517
518
        $this->assertFalse((bool)$obj->getCommentsOption('enabled'), 'Default setting is disabled');
519
        $this->assertFalse((bool)$obj->ProvideComments);
520
        $this->assertEquals('Required', $obj->ModerationRequired);
521
        $this->assertTrue((bool)$obj->CommentsRequireLogin);
522
523
        $obj = new CommentableItemEnabled();
524
525
        $this->assertTrue((bool)$obj->ProvideComments);
526
        $this->assertEquals('Required', $obj->ModerationRequired);
527
        $this->assertTrue((bool)$obj->CommentsRequireLogin);
528
529
        $obj = new CommentableItemDisabled();
530
531
        $this->assertFalse((bool)$obj->ProvideComments);
532
        $this->assertEquals('None', $obj->ModerationRequired);
533
        $this->assertFalse((bool)$obj->CommentsRequireLogin);
534
    }
535
536
    public function testOnBeforeDelete()
537
    {
538
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
539
540
        $child = new Comment();
541
        $child->Name = 'Fred Bloggs';
542
        $child->Comment = 'Child of firstComA';
543
        $child->write();
544
        $comment->ChildComments()->add($child);
545
        $this->assertEquals(4, $comment->ChildComments()->count());
546
547
        $commentID = $comment->ID;
548
        $childCommentID = $child->ID;
549
550
        $comment->delete();
551
552
        // assert that the new child been deleted
553
        $this->assertNull(DataObject::get_by_id(Comment::class, $commentID));
554
        $this->assertNull(DataObject::get_by_id(Comment::class, $childCommentID));
555
    }
556
557
    public function testRequireDefaultRecords()
558
    {
559
        $this->markTestSkipped('TODO');
560
    }
561
562
    public function testLink()
563
    {
564
        $comment = $this->objFromFixture(Comment::class, 'thirdComD');
565
        $this->assertEquals(
566
            'CommentableItemController#comment-' . $comment->ID,
567
            $comment->Link()
568
        );
569
        $this->assertEquals($comment->ID, $comment->ID);
570
571
        // An orphan comment has no link
572
        $comment->ParentID = 0;
573
        $comment->ParentClass = null;
574
        $comment->write();
575
        $this->assertEquals('', $comment->Link());
576
    }
577
578
    public function testPermalink()
579
    {
580
        $comment = $this->objFromFixture(Comment::class, 'thirdComD');
581
        $this->assertEquals('comment-' . $comment->ID, $comment->Permalink());
582
    }
583
584
    /**
585
     * Test field labels are defined
586
     */
587
    public function testFieldLabels()
588
    {
589
        /** @var Comment $comment */
590
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
591
592
        $labels = $comment->FieldLabels();
593
        $expected = [
594
            'Name',
595
            'Comment',
596
            'Email',
597
            'URL',
598
            'IsSpam',
599
            'Moderated',
600
            'ParentTitle',
601
            'Created',
602
        ];
603
        foreach ($expected as $key) {
604
            $this->assertArrayHasKey($key, $labels);
605
        }
606
    }
607
608
    public function testGetParent()
609
    {
610
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
611
        $item = $this->objFromFixture(CommentableItem::class, 'first');
612
        $parent = $comment->Parent();
613
        $this->assertSame($item->getClassName(), $parent->getClassName());
614
        $this->assertSame($item->ID, $parent->ID);
615
    }
616
617
    public function testGetParentTitle()
618
    {
619
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
620
        $title = $comment->getParentTitle();
621
        $this->assertEquals('First', $title);
622
623
        // Title from a comment with no parent is blank
624
        $comment->ParentID = 0;
625
        $comment->ParentClass = null;
626
        $comment->write();
627
        $this->assertEquals('', $comment->getParentTitle());
628
    }
629
630
    public function testGetParentClassName()
631
    {
632
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
633
        $className = $comment->getParentClassName();
634
        $this->assertEquals(CommentableItem::class, $className);
635
    }
636
637
    public function testCastingHelper()
638
    {
639
        $this->markTestSkipped('TODO');
640
    }
641
642
    public function testGetEscapedComment()
643
    {
644
        $this->markTestSkipped('TODO');
645
    }
646
647
    public function testIsPreview()
648
    {
649
        $comment = new Comment();
650
        $comment->Name = 'Fred Bloggs';
651
        $comment->Comment = 'this is a test comment';
652
        $this->assertTrue($comment->isPreview());
653
        $comment->write();
654
        $this->assertFalse($comment->isPreview());
655
    }
656
657
    public function testCanCreate()
658
    {
659
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
660
661
        // admin can create - this is always false
662
        $this->logInAs('commentadmin');
663
        $this->assertFalse($comment->canCreate());
664
665
        // visitor can view
666
        $this->logInAs('visitor');
667
        $this->assertFalse($comment->canCreate());
668
    }
669
670
    public function testCanView()
671
    {
672
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
673
674
        // admin can view
675
        $this->logInAs('commentadmin');
676
        $this->assertTrue($comment->canView());
677
678
        // visitor can view
679
        $this->logInAs('visitor');
680
        $this->assertTrue($comment->canView());
681
682
        $comment->ParentID = 0;
683
        $comment->write();
684
        $this->assertFalse($comment->canView());
685
    }
686
687
    public function testCanEdit()
688
    {
689
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
690
691
        // admin can edit
692
        $this->logInAs('commentadmin');
693
        $this->assertTrue($comment->canEdit());
694
695
        // visitor cannot
696
        $this->logInAs('visitor');
697
        $this->assertFalse($comment->canEdit());
698
699
        $comment->ParentID = 0;
700
        $comment->write();
701
        $this->assertFalse($comment->canEdit());
702
    }
703
704
    public function testCanDelete()
705
    {
706
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
707
708
        // admin can delete
709
        $this->logInAs('commentadmin');
710
        $this->assertTrue($comment->canDelete());
711
712
        // visitor cannot
713
        $this->logInAs('visitor');
714
        $this->assertFalse($comment->canDelete());
715
716
        $comment->ParentID = 0;
717
        $comment->write();
718
        $this->assertFalse($comment->canDelete());
719
    }
720
721
    public function testGetMember()
722
    {
723
        $this->logInAs('visitor');
724
        $current = Security::getCurrentUser();
725
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
726
        $method = $this->getMethod('getMember');
727
728
        // null case
729
        $member = $method->invokeArgs($comment, array());
730
        $this->assertEquals($current->ID, $member->ID);
731
732
        // numeric ID case
733
        $member = $method->invokeArgs($comment, array($current->ID));
734
        $this->assertEquals($current->ID, $member->ID);
735
736
        // identity case
737
        $member = $method->invokeArgs($comment, array($current));
738
        $this->assertEquals($current->ID, $member->ID);
739
    }
740
741
    public function testGetAuthorName()
742
    {
743
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
744
        $this->assertEquals(
745
            'FA',
746
            $comment->getAuthorName()
747
        );
748
749
        $comment->Name = '';
750
        $this->assertEquals(
751
            '',
752
            $comment->getAuthorName()
753
        );
754
755
        $author = $this->objFromFixture(Member::class, 'visitor');
756
        $comment->AuthorID = $author->ID;
757
        $comment->write();
758
        $this->assertEquals(
759
            'visitor',
760
            $comment->getAuthorName()
761
        );
762
763
        // null the names, expect null back
764
        $comment->Name = null;
765
        $comment->AuthorID = 0;
766
        $this->assertNull($comment->getAuthorName());
767
    }
768
769
770
    public function testLinks()
771
    {
772
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
773
        $this->logInAs('commentadmin');
774
775
        $method = $this->getMethod('ActionLink');
776
777
        // test with starts of strings and tokens and salts change each time
778
        $this->assertContains(
779
            '/comments/theaction/' . $comment->ID,
780
            $method->invokeArgs($comment, array('theaction'))
781
        );
782
783
        $this->assertContains(
784
            '/comments/delete/' . $comment->ID,
785
            $comment->DeleteLink()
786
        );
787
788
        $this->assertContains(
789
            '/comments/spam/' . $comment->ID,
790
            $comment->SpamLink()
791
        );
792
793
        $comment->markSpam();
794
        $this->assertContains(
795
            '/comments/ham/' . $comment->ID,
796
            $comment->HamLink()
797
        );
798
799
        //markApproved
800
        $comment->markUnapproved();
801
        $this->assertContains(
802
            '/comments/approve/' . $comment->ID,
803
            $comment->ApproveLink()
804
        );
805
    }
806
807
    public function testMarkSpam()
808
    {
809
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
810
        $comment->markSpam();
811
        $this->assertTrue($comment->Moderated);
812
        $this->assertTrue($comment->IsSpam);
813
    }
814
815
    public function testMarkApproved()
816
    {
817
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
818
        $comment->markApproved();
819
        $this->assertTrue($comment->Moderated);
820
        $this->assertFalse($comment->IsSpam);
821
    }
822
823
    public function testMarkUnapproved()
824
    {
825
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
826
        $comment->markApproved();
827
        $this->assertTrue($comment->Moderated);
828
    }
829
830
    public function testSpamClass()
831
    {
832
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
833
        $this->assertEquals('notspam', $comment->spamClass());
834
        $comment->Moderated = false;
835
        $this->assertEquals('unmoderated', $comment->spamClass());
836
        $comment->IsSpam = true;
837
        $this->assertEquals('spam', $comment->spamClass());
838
    }
839
840
    public function testGetTitle()
841
    {
842
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
843
        $this->assertEquals(
844
            'Comment by FA on First',
845
            $comment->getTitle()
846
        );
847
    }
848
849
    public function testGetCMSFields()
850
    {
851
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
852
        $fields = $comment->getCMSFields();
853
        $names = [];
854
        foreach ($fields as $field) {
855
            $names[] = $field->getName();
856
        }
857
        $expected = [
858
            'Created',
859
            'Name',
860
            'Comment',
861
            'Email',
862
            'URL',
863
            'Options',
864
        ];
865
        $this->assertEquals($expected, $names);
866
    }
867
868
    public function testGetCMSFieldsCommentHasAuthor()
869
    {
870
        $member = Member::get()->filter('FirstName', 'visitor')->first();
871
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
872
        $comment->AuthorID = $member->ID;
873
        $comment->write();
874
875
        $fields = $comment->getCMSFields();
876
        $names = [];
877
        foreach ($fields as $field) {
878
            $names[] = $field->getName();
879
        }
880
        $expected = [
881
            'Created',
882
            'Name',
883
            'AuthorMember',
884
            'Comment',
885
            'Email',
886
            'URL',
887
            'Options',
888
        ];
889
        $this->assertEquals($expected, $names);
890
    }
891
892
    public function testGetCMSFieldsWithParentComment()
893
    {
894
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
895
896
        $child = new Comment();
897
        $child->Name = 'John Smith';
898
        $child->Comment = 'This is yet another test commnent';
899
        $child->ParentCommentID = $comment->ID;
0 ignored issues
show
Bug Best Practice introduced by Gordon Anderson
The property ParentCommentID does not exist on SilverStripe\Comments\Model\Comment. Since you implemented __set, consider adding a @property annotation.
Loading history...
900
        $child->write();
901
902
        $fields = $child->getCMSFields();
903
        $names = [];
904
        foreach ($fields as $field) {
905
            $names[] = $field->getName();
906
        }
907
        $expected = [
908
            'Created',
909
            'Name',
910
            'Comment',
911
            'Email',
912
            'URL',
913
            'Options',
914
            'ParentComment_Title',
915
            'ParentComment_Created',
916
            'ParentComment_AuthorName',
917
            'ParentComment_EscapedComment',
918
        ];
919
        $this->assertEquals($expected, $names);
920
    }
921
922
    public function testPurifyHtml()
923
    {
924
        if (!class_exists(HTMLPurifier_Config::class)) {
925
            $this->markTestSkipped('HTMLPurifier class not found');
926
        }
927
928
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
929
930
        $dirtyHTML = '<p><script>alert("w00t")</script>my comment</p>';
931
        $this->assertEquals(
932
            'my comment',
933
            $comment->purifyHtml($dirtyHTML)
934
        );
935
    }
936
937
    public function testGravatar()
938
    {
939
        // Turn gravatars on
940
        Config::modify()->merge(CommentableItem::class, 'comments', [
941
            'use_gravatar' => true,
942
            'gravatar_size' => 80,
943
            'gravatar_default' => 'identicon',
944
            'gravatar_rating' => 'g',
945
        ]);
946
947
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
948
949
        $this->assertEquals(
950
            'https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s'
951
            . '=80&d=identicon&r=g',
952
            $comment->Gravatar()
953
        );
954
955
        // Turn gravatars off
956
        Config::modify()->merge(CommentableItem::class, 'comments', [
957
            'use_gravatar' => false,
958
        ]);
959
960
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
961
962
        $this->assertEquals(
963
            '',
964
            $comment->Gravatar()
965
        );
966
    }
967
968
    public function testGetRepliesEnabled()
969
    {
970
        Config::modify()->merge(CommentableItem::class, 'comments', [
971
            'nested_comments' => false,
972
        ]);
973
974
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
975
        $this->assertFalse($comment->getRepliesEnabled());
976
977
        Config::modify()->merge(CommentableItem::class, 'comments', [
978
            'nested_comments' => true,
979
            'nested_depth' => 4,
980
        ]);
981
982
        $this->assertTrue($comment->getRepliesEnabled());
983
984
        $comment->Depth = 4;
985
        $this->assertFalse($comment->getRepliesEnabled());
986
987
988
        // 0 indicates no limit for nested_depth
989
        Config::modify()->merge(CommentableItem::class, 'comments', [
990
            'nested_comments' => true,
991
            'nested_depth' => 0,
992
        ]);
993
994
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
995
        $comment->Depth = 234;
996
997
        $comment->markUnapproved();
998
        $this->assertFalse($comment->getRepliesEnabled());
999
1000
        $comment->markSpam();
1001
        $this->assertFalse($comment->getRepliesEnabled());
1002
1003
        $comment->markApproved();
1004
        $this->assertTrue($comment->getRepliesEnabled());
1005
    }
1006
1007
    public function testAllReplies()
1008
    {
1009
        Config::modify()->merge(CommentableItem::class, 'comments', [
1010
            'nested_comments' => true,
1011
            'nested_depth' => 4,