HistoryCommentLinkBuilderTest.php

Namespace

Drupal\Tests\history\Unit

File

core/modules/history/tests/src/Unit/HistoryCommentLinkBuilderTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\history\Unit;

use Drupal\comment\CommentLinkBuilder;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Url;
use Drupal\history\HistoryCommentLinkBuilder;
use Drupal\Tests\Traits\Core\GeneratePermutationsTrait;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;

/**
 * This is duplicated from CommentLinkBuilderTest with history additions.
 *
 * @see \Drupal\Tests\comment\Unit\CommentLinkBuilderTest
 */
class HistoryCommentLinkBuilderTest extends UnitTestCase {
  use GeneratePermutationsTrait;
  
  /**
   * Comment manager mock.
   *
   * @var \Drupal\comment\CommentManagerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $commentManager;
  
  /**
   * String translation mock.
   *
   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $stringTranslation;
  
  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $entityTypeManager;
  
  /**
   * Module handler mock.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $moduleHandler;
  
  /**
   * Current user proxy mock.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $currentUser;
  
  /**
   * Timestamp used in test.
   *
   * @var int
   */
  protected $timestamp;
  
  /**
   * The comment link builder.
   *
   * @var \Drupal\comment\CommentLinkBuilderInterface
   */
  protected $commentLinkBuilder;
  
  /**
   * The decorated comment link builder.
   *
   * @var \Drupal\comment\CommentLinkBuilderInterface
   */
  protected $decoratedCommentLinkBuilder;
  
  /**
   * Prepares mocks for the test.
   */
  protected function setUp() : void {
    parent::setUp();
    $this->commentManager = $this->createMock('\\Drupal\\comment\\CommentManagerInterface');
    $this->stringTranslation = $this->getStringTranslationStub();
    $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
    $this->moduleHandler = $this->createMock('\\Drupal\\Core\\Extension\\ModuleHandlerInterface');
    $this->currentUser = $this->createMock('\\Drupal\\Core\\Session\\AccountProxyInterface');
    $this->commentLinkBuilder = new CommentLinkBuilder($this->currentUser, $this->commentManager, $this->moduleHandler, $this->stringTranslation, $this->entityTypeManager);
    $this->decoratedCommentLinkBuilder = new HistoryCommentLinkBuilder($this->commentLinkBuilder, $this->commentManager, $this->currentUser, $this->entityTypeManager);
    $this->commentManager
      ->expects($this->any())
      ->method('getFields')
      ->with('node')
      ->willReturn([
      'comment' => [],
    ]);
    $this->commentManager
      ->expects($this->any())
      ->method('forbiddenMessage')
      ->willReturn("Can't let you do that Dave.");
    $this->stringTranslation
      ->expects($this->any())
      ->method('formatPlural')
      ->willReturnArgument(1);
  }
  
  /**
   * Tests the buildCommentedEntityLinks method.
   *
   * @param array $node_args
   *   Arguments for the mock node.
   * @param array $context
   *   Context for the links.
   * @param bool $has_access_comments
   *   TRUE if the user has 'access comments' permission.
   * @param bool $history_exists
   *   TRUE if the history module exists.
   * @param bool $has_post_comments
   *   TRUE if the use has 'post comments' permission.
   * @param bool $is_anonymous
   *   TRUE if the user is anonymous.
   * @param array $expected
   *   Array of expected links keyed by link ID. Can be either string (link
   *   title) or array of link properties.
   *
   * @legacy-covers ::buildCommentedEntityLinks
   */
  public function testCommentLinkBuilder(array $node_args, $context, $has_access_comments, $history_exists, $has_post_comments, $is_anonymous, $expected) : void {
    $node = $this->getMockNode(...$node_args);
    $this->moduleHandler
      ->expects($this->any())
      ->method('moduleExists')
      ->with('history')
      ->willReturn($history_exists);
    $this->currentUser
      ->expects($this->any())
      ->method('hasPermission')
      ->willReturnMap([
      [
        'access comments',
        $has_access_comments,
      ],
      [
        'post comments',
        $has_post_comments,
      ],
    ]);
    $this->currentUser
      ->expects($this->any())
      ->method('isAuthenticated')
      ->willReturn(!$is_anonymous);
    $this->currentUser
      ->expects($this->any())
      ->method('isAnonymous')
      ->willReturn($is_anonymous);
    $links = $this->decoratedCommentLinkBuilder
      ->buildCommentedEntityLinks($node, $context);
    if (!empty($expected)) {
      if (!empty($links)) {
        foreach ($expected as $link => $detail) {
          if (is_array($detail)) {
            // Array of link attributes.
            foreach ($detail as $key => $value) {
              $this->assertEquals($value, $links['comment__comment']['#links'][$link][$key]);
            }
          }
          else {
            // Just the title.
            $this->assertEquals($detail, $links['comment__comment']['#links'][$link]['title']);
          }
        }
      }
      else {
        $this->fail('Expected links but found none.');
      }
    }
    else {
      $this->assertSame($links, $expected);
    }
  }
  
  /**
   * Data provider for ::testCommentLinkBuilder.
   */
  public static function getLinkCombinations() : array {
    $cases = [];
    // No links should be created if the entity doesn't have the field.
    $cases[] = [
      [
        FALSE,
        CommentItemInterface::OPEN,
        CommentItemInterface::FORM_BELOW,
        1,
      ],
      [
        'view_mode' => 'teaser',
      ],
      TRUE,
      TRUE,
      TRUE,
      TRUE,
      [],
    ];
    foreach ([
      'search_result',
      'search_index',
      'print',
    ] as $view_mode) {
      // Nothing should be output in these view modes.
      $cases[] = [
        [
          TRUE,
          CommentItemInterface::OPEN,
          CommentItemInterface::FORM_BELOW,
          1,
        ],
        [
          'view_mode' => $view_mode,
        ],
        TRUE,
        TRUE,
        TRUE,
        TRUE,
        [],
      ];
    }
    // All other combinations.
    $combinations = [
      'is_anonymous' => [
        FALSE,
        TRUE,
      ],
      'comment_count' => [
        0,
        1,
      ],
      'has_access_comments' => [
        0,
        1,
      ],
      'history_exists' => [
        FALSE,
        TRUE,
      ],
      'has_post_comments' => [
        0,
        1,
      ],
      'form_location' => [
        CommentItemInterface::FORM_BELOW,
        CommentItemInterface::FORM_SEPARATE_PAGE,
      ],
      'comments' => [
        CommentItemInterface::OPEN,
        CommentItemInterface::CLOSED,
        CommentItemInterface::HIDDEN,
      ],
      'view_mode' => [
        'teaser',
        'rss',
        'full',
      ],
    ];
    $permutations = static::generatePermutations($combinations);
    foreach ($permutations as $combination) {
      $case = [
        [
          TRUE,
          $combination['comments'],
          $combination['form_location'],
          $combination['comment_count'],
        ],
        [
          'view_mode' => $combination['view_mode'],
        ],
        $combination['has_access_comments'],
        $combination['history_exists'],
        $combination['has_post_comments'],
        $combination['is_anonymous'],
      ];
      $expected = [];
      // When comments are enabled in teaser mode, and comments exist, and the
      // user has access - we can output the comment count.
      if ($combination['comments'] && $combination['view_mode'] == 'teaser' && $combination['comment_count'] && $combination['has_access_comments']) {
        $expected['comment-comments'] = '1 comment';
        // And if history module exists, we can show a 'new comments' link.
        if ($combination['history_exists']) {
          $expected['comment-new-comments'] = '';
        }
      }
      // All view modes other than RSS.
      if ($combination['view_mode'] != 'rss') {
        // Where commenting is open.
        if ($combination['comments'] == CommentItemInterface::OPEN) {
          // And the user has post-comments permission.
          if ($combination['has_post_comments']) {
            // If the view mode is teaser, or the user can access comments and
            // comments exist or the form is on a separate page.
            if ($combination['view_mode'] == 'teaser' || $combination['has_access_comments'] && $combination['comment_count'] || $combination['form_location'] == CommentItemInterface::FORM_SEPARATE_PAGE) {
              // There should be an add comment link.
              $expected['comment-add'] = [
                'title' => 'Add new comment',
              ];
              if ($combination['form_location'] == CommentItemInterface::FORM_BELOW) {
                // On the same page.
                $expected['comment-add']['url'] = Url::fromRoute('node.view');
              }
              else {
                // On a separate page.
                $expected['comment-add']['url'] = Url::fromRoute('comment.reply', [
                  'entity_type' => 'node',
                  'entity' => 1,
                  'field_name' => 'comment',
                ]);
              }
            }
          }
          elseif ($combination['is_anonymous']) {
            // Anonymous users get the forbidden message if the can't post
            // comments.
            $expected['comment-forbidden'] = "Can't let you do that Dave.";
          }
        }
      }
      $case[] = $expected;
      $cases[] = $case;
    }
    return $cases;
  }
  
  /**
   * Builds a mock node based on given scenario.
   *
   * @param bool $has_field
   *   TRUE if the node has the 'comment' field.
   * @param int $comment_status
   *   One of CommentItemInterface::OPEN|HIDDEN|CLOSED.
   * @param int $form_location
   *   One of CommentItemInterface::FORM_BELOW|FORM_SEPARATE_PAGE.
   * @param int $comment_count
   *   Number of comments against the field.
   *
   * @return \Drupal\node\NodeInterface|\PHPUnit\Framework\MockObject\MockObject
   *   Mock node for testing.
   */
  protected function getMockNode($has_field, $comment_status, $form_location, $comment_count) {
    $node = $this->createMock('\\Drupal\\node\\NodeInterface');
    $node->expects($this->any())
      ->method('hasField')
      ->willReturn($has_field);
    if (empty($this->timestamp)) {
      $this->timestamp = time();
    }
    $field_item = (object) [
      'status' => $comment_status,
      'comment_count' => $comment_count,
      'last_comment_timestamp' => $this->timestamp,
    ];
    $node->expects($this->any())
      ->method('get')
      ->with('comment')
      ->willReturn($field_item);
    $field_definition = $this->createMock('\\Drupal\\Core\\Field\\FieldDefinitionInterface');
    $field_definition->expects($this->any())
      ->method('getSetting')
      ->with('form_location')
      ->willReturn($form_location);
    $node->expects($this->any())
      ->method('getFieldDefinition')
      ->with('comment')
      ->willReturn($field_definition);
    $node->expects($this->any())
      ->method('language')
      ->willReturn('und');
    $node->expects($this->any())
      ->method('getEntityTypeId')
      ->willReturn('node');
    $node->expects($this->any())
      ->method('id')
      ->willReturn(1);
    $url = Url::fromRoute('node.view');
    $node->expects($this->any())
      ->method('toUrl')
      ->willReturn($url);
    return $node;
  }

}
namespace Drupal\history;

if (!function_exists('history_read')) {
  
  /**
   * Gets a timestamp for the current user's last view of a specified node.
   */
  function history_read($nid) : int {
    return 0;
  }
}

Classes

Title Deprecated Summary
HistoryCommentLinkBuilderTest This is duplicated from CommentLinkBuilderTest with history additions.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.