Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / comment / tests / src / Unit / CommentLinkBuilderTest.php
1 <?php
2
3 namespace Drupal\Tests\comment\Unit;
4
5 use Drupal\comment\CommentLinkBuilder;
6 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
7 use Drupal\Core\Url;
8 use Drupal\node\NodeInterface;
9 use Drupal\Tests\Traits\Core\GeneratePermutationsTrait;
10 use Drupal\Tests\UnitTestCase;
11
12 /**
13  * @coversDefaultClass \Drupal\comment\CommentLinkBuilder
14  * @group comment
15  */
16 class CommentLinkBuilderTest extends UnitTestCase {
17
18   use GeneratePermutationsTrait;
19
20   /**
21    * Comment manager mock.
22    *
23    * @var \Drupal\comment\CommentManagerInterface|\PHPUnit_Framework_MockObject_MockObject
24    */
25   protected $commentManager;
26
27   /**
28    * String translation mock.
29    *
30    * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
31    */
32   protected $stringTranslation;
33
34   /**
35    * The entity manager service.
36    *
37    * @var \Drupal\Core\Entity\EntityManagerInterface
38    */
39   protected $entityManager;
40
41   /**
42    * Module handler mock.
43    *
44    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
45    */
46   protected $moduleHandler;
47
48   /**
49    * Current user proxy mock.
50    *
51    * @var \Drupal\Core\Session\AccountProxyInterface|\PHPUnit_Framework_MockObject_MockObject
52    */
53   protected $currentUser;
54
55   /**
56    * Timestamp used in test.
57    *
58    * @var int
59    */
60   protected $timestamp;
61
62   /**
63    * @var \Drupal\comment\CommentLinkBuilderInterface;
64    */
65   protected $commentLinkBuilder;
66
67   /**
68    * Prepares mocks for the test.
69    */
70   protected function setUp() {
71     $this->commentManager = $this->getMock('\Drupal\comment\CommentManagerInterface');
72     $this->stringTranslation = $this->getStringTranslationStub();
73     $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
74     $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
75     $this->currentUser = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
76     $this->commentLinkBuilder = new CommentLinkBuilder($this->currentUser, $this->commentManager, $this->moduleHandler, $this->stringTranslation, $this->entityManager);
77     $this->commentManager->expects($this->any())
78       ->method('getFields')
79       ->with('node')
80       ->willReturn([
81         'comment' => [],
82       ]);
83     $this->commentManager->expects($this->any())
84       ->method('forbiddenMessage')
85       ->willReturn("Can't let you do that Dave.");
86     $this->stringTranslation->expects($this->any())
87       ->method('formatPlural')
88       ->willReturnArgument(1);
89   }
90
91   /**
92    * Test the buildCommentedEntityLinks method.
93    *
94    * @param \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject $node
95    *   Mock node.
96    * @param array $context
97    *   Context for the links.
98    * @param bool $has_access_comments
99    *   TRUE if the user has 'access comments' permission.
100    * @param bool $history_exists
101    *   TRUE if the history module exists.
102    * @param bool $has_post_comments
103    *   TRUE if the use has 'post comments' permission.
104    * @param bool $is_anonymous
105    *   TRUE if the user is anonymous.
106    * @param array $expected
107    *   Array of expected links keyed by link ID. Can be either string (link
108    *   title) or array of link properties.
109    *
110    * @dataProvider getLinkCombinations
111    *
112    * @covers ::buildCommentedEntityLinks
113    */
114   public function testCommentLinkBuilder(NodeInterface $node, $context, $has_access_comments, $history_exists, $has_post_comments, $is_anonymous, $expected) {
115     $this->moduleHandler->expects($this->any())
116       ->method('moduleExists')
117       ->with('history')
118       ->willReturn($history_exists);
119     $this->currentUser->expects($this->any())
120       ->method('hasPermission')
121       ->willReturnMap([
122         ['access comments', $has_access_comments],
123         ['post comments', $has_post_comments],
124       ]);
125     $this->currentUser->expects($this->any())
126       ->method('isAuthenticated')
127       ->willReturn(!$is_anonymous);
128     $this->currentUser->expects($this->any())
129       ->method('isAnonymous')
130       ->willReturn($is_anonymous);
131     $links = $this->commentLinkBuilder->buildCommentedEntityLinks($node, $context);
132     if (!empty($expected)) {
133       if (!empty($links)) {
134         foreach ($expected as $link => $detail) {
135           if (is_array($detail)) {
136             // Array of link attributes.
137             foreach ($detail as $key => $value) {
138               $this->assertEquals($value, $links['comment__comment']['#links'][$link][$key]);
139             }
140           }
141           else {
142             // Just the title.
143             $this->assertEquals($detail, $links['comment__comment']['#links'][$link]['title']);
144           }
145         }
146       }
147       else {
148         $this->fail('Expected links but found none.');
149       }
150     }
151     else {
152       $this->assertSame($links, $expected);
153     }
154   }
155
156   /**
157    * Data provider for ::testCommentLinkBuilder.
158    */
159   public function getLinkCombinations() {
160     $cases = [];
161     // No links should be created if the entity doesn't have the field.
162     $cases[] = [
163       $this->getMockNode(FALSE, CommentItemInterface::OPEN, CommentItemInterface::FORM_BELOW, 1),
164       ['view_mode' => 'teaser'],
165       TRUE,
166       TRUE,
167       TRUE,
168       TRUE,
169       [],
170     ];
171     foreach (['search_result', 'search_index', 'print'] as $view_mode) {
172       // Nothing should be output in these view modes.
173       $cases[] = [
174         $this->getMockNode(TRUE, CommentItemInterface::OPEN, CommentItemInterface::FORM_BELOW, 1),
175         ['view_mode' => $view_mode],
176         TRUE,
177         TRUE,
178         TRUE,
179         TRUE,
180         [],
181       ];
182     }
183     // All other combinations.
184     $combinations = [
185       'is_anonymous' => [FALSE, TRUE],
186       'comment_count' => [0, 1],
187       'has_access_comments' => [0, 1],
188       'history_exists' => [FALSE, TRUE],
189       'has_post_comments'   => [0, 1],
190       'form_location'            => [CommentItemInterface::FORM_BELOW, CommentItemInterface::FORM_SEPARATE_PAGE],
191       'comments'        => [
192         CommentItemInterface::OPEN,
193         CommentItemInterface::CLOSED,
194         CommentItemInterface::HIDDEN,
195       ],
196       'view_mode' => [
197         'teaser', 'rss', 'full',
198       ],
199     ];
200     $permutations = $this->generatePermutations($combinations);
201     foreach ($permutations as $combination) {
202       $case = [
203         $this->getMockNode(TRUE, $combination['comments'], $combination['form_location'], $combination['comment_count']),
204         ['view_mode' => $combination['view_mode']],
205         $combination['has_access_comments'],
206         $combination['history_exists'],
207         $combination['has_post_comments'],
208         $combination['is_anonymous'],
209       ];
210       $expected = [];
211       // When comments are enabled in teaser mode, and comments exist, and the
212       // user has access - we can output the comment count.
213       if ($combination['comments'] && $combination['view_mode'] == 'teaser' && $combination['comment_count'] && $combination['has_access_comments']) {
214         $expected['comment-comments'] = '1 comment';
215         // And if history module exists, we can show a 'new comments' link.
216         if ($combination['history_exists']) {
217           $expected['comment-new-comments'] = '';
218         }
219       }
220       // All view modes other than RSS.
221       if ($combination['view_mode'] != 'rss') {
222         // Where commenting is open.
223         if ($combination['comments'] == CommentItemInterface::OPEN) {
224           // And the user has post-comments permission.
225           if ($combination['has_post_comments']) {
226             // If the view mode is teaser, or the user can access comments and
227             // comments exist or the form is on a separate page.
228             if ($combination['view_mode'] == 'teaser' || ($combination['has_access_comments'] && $combination['comment_count']) || $combination['form_location'] == CommentItemInterface::FORM_SEPARATE_PAGE) {
229               // There should be a add comment link.
230               $expected['comment-add'] = ['title' => 'Add new comment'];
231               if ($combination['form_location'] == CommentItemInterface::FORM_BELOW) {
232                 // On the same page.
233                 $expected['comment-add']['url'] = Url::fromRoute('node.view');
234               }
235               else {
236                 // On a separate page.
237                 $expected['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => 'node', 'entity' => 1, 'field_name' => 'comment']);
238               }
239             }
240           }
241           elseif ($combination['is_anonymous']) {
242             // Anonymous users get the forbidden message if the can't post
243             // comments.
244             $expected['comment-forbidden'] = "Can't let you do that Dave.";
245           }
246         }
247       }
248
249       $case[] = $expected;
250       $cases[] = $case;
251     }
252     return $cases;
253   }
254
255   /**
256    * Builds a mock node based on given scenario.
257    *
258    * @param bool $has_field
259    *   TRUE if the node has the 'comment' field.
260    * @param int $comment_status
261    *   One of CommentItemInterface::OPEN|HIDDEN|CLOSED
262    * @param int $form_location
263    *   One of CommentItemInterface::FORM_BELOW|FORM_SEPARATE_PAGE
264    * @param int $comment_count
265    *   Number of comments against the field.
266    *
267    * @return \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject
268    *   Mock node for testing.
269    */
270   protected function getMockNode($has_field, $comment_status, $form_location, $comment_count) {
271     $node = $this->getMock('\Drupal\node\NodeInterface');
272     $node->expects($this->once())
273       ->method('hasField')
274       ->willReturn($has_field);
275
276     if (empty($this->timestamp)) {
277       $this->timestamp = time();
278     }
279     $field_item = (object) [
280       'status' => $comment_status,
281       'comment_count' => $comment_count,
282       'last_comment_timestamp' => $this->timestamp,
283     ];
284     $node->expects($this->any())
285       ->method('get')
286       ->with('comment')
287       ->willReturn($field_item);
288
289     $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
290     $field_definition->expects($this->any())
291       ->method('getSetting')
292       ->with('form_location')
293       ->willReturn($form_location);
294     $node->expects($this->any())
295       ->method('getFieldDefinition')
296       ->with('comment')
297       ->willReturn($field_definition);
298
299     $node->expects($this->any())
300       ->method('language')
301       ->willReturn('und');
302
303     $node->expects($this->any())
304       ->method('getEntityTypeId')
305       ->willReturn('node');
306
307     $node->expects($this->any())
308       ->method('id')
309       ->willReturn(1);
310
311     $url = Url::fromRoute('node.view');
312     $node->expects($this->any())
313       ->method('urlInfo')
314       ->willReturn($url);
315     $node->expects($this->any())
316       ->method('url')
317       ->willReturn(['route_name' => 'node.view']);
318
319     return $node;
320   }
321
322 }
323
324 namespace Drupal\comment;
325
326 if (!function_exists('history_read')) {
327   function history_read() {
328     return 0;
329   }
330 }