You are here

public function AjaxCommentsFunctionalTest::testCommentPosting in AJAX Comments 8

Tests that comments can be posted and edited over ajax without errors.

File

tests/src/FunctionalJavascript/AjaxCommentsFunctionalTest.php, line 66

Class

AjaxCommentsFunctionalTest
Javascript functional tests for ajax comments.

Namespace

Drupal\Tests\ajax_comments\FunctionalJavascript

Code

public function testCommentPosting() {

  // Enable CKEditor.
  $format = $this
    ->randomMachineName();
  FilterFormat::create([
    'format' => $format,
    'name' => $this
      ->randomString(),
    'weight' => 1,
    'filters' => [],
  ])
    ->save();
  $settings['toolbar']['rows'] = [
    [
      [
        'name' => 'Links',
        'items' => [
          'DrupalLink',
          'DrupalUnlink',
        ],
      ],
    ],
  ];
  $editor = Editor::create([
    'format' => $format,
    'editor' => 'ckeditor',
  ]);
  $editor
    ->setSettings($settings);
  $editor
    ->save();
  $admin_user = $this
    ->drupalCreateUser([
    'access content',
    'access comments',
    // Usernames aren't shown in comment edit form autocomplete unless this
    // permission is granted.
    'access user profiles',
    'administer comments',
    'edit own comments',
    'post comments',
    'skip comment approval',
    'use text format ' . $format,
  ]);
  $this
    ->drupalLogin($admin_user);
  $node = $this
    ->drupalCreateNode([
    'type' => 'article',
    'comment' => CommentItemInterface::OPEN,
  ]);
  $this
    ->drupalGet($node
    ->toUrl());

  // Set up JavaScript to track errors.
  $onerror_javascript = <<<JS
    (function (){
      window.jsErrors = [];
      window.onerror = function (message, source, lineno, colno, error) {
        window.jsErrors.push(error);
      }
    }());
JS;
  $this
    ->getSession()
    ->executeScript($onerror_javascript);
  $page = $this
    ->getSession()
    ->getPage();

  // Post comments through ajax.
  for ($i = 0; $i < 11; $i++) {
    $comment_body_id = $page
      ->findField('comment_body[0][value]')
      ->getAttribute('id');
    $count = $i + 1;
    $ckeditor_javascript = <<<JS
    (function (){
      CKEDITOR.instances['{<span class="php-variable">$comment_body_id</span>}'].setData('New comment {<span class="php-variable">$count</span>}');
    }());
JS;
    $this
      ->getSession()
      ->executeScript($ckeditor_javascript);
    $page
      ->pressButton('Save');
    $this
      ->assertSession()
      ->assertWaitOnAjaxRequest(20000);
  }

  // Export the updated content of the page.
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $this
      ->htmlOutput($out);
  }
  $this
    ->assertSession()
    ->pageTextContains('Your comment has been posted.');
  $this
    ->assertSession()
    ->pageTextContains('New comment 1');
  $this
    ->assertSession()
    ->pageTextContains('New comment 2');
  $current_url = $this
    ->getSession()
    ->getCurrentUrl();
  $parts = parse_url($current_url);
  $path = empty($parts['path']) ? '/' : $parts['path'];
  $current_path = preg_replace('/^\\/[^\\.\\/]+\\.php\\//', '/', $path);
  $this
    ->assertSession()
    ->linkByHrefExists($current_path . '?page=1');
  $javascript_assertion = <<<JS
    (function () {
      return window.jsErrors.length === 0;
    }());
JS;
  $this
    ->assertJsCondition($javascript_assertion);

  // Using prepareRequest() followed by refreshVariables() seems to help
  // refresh the route permissions for the ajax_comments.update route.
  $this
    ->prepareRequest();
  $this
    ->refreshVariables();

  // Test updating a comment through ajax.
  $this
    ->clickLink('Edit');
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  $comment_body_id = $page
    ->find('css', 'form.ajax-comments-form-edit textarea')
    ->getAttribute('id');
  $ckeditor_javascript = <<<JS
    (function (){
      CKEDITOR.instances['{<span class="php-variable">$comment_body_id</span>}'].setData('Updated comment');
    }());
JS;
  $this
    ->getSession()
    ->executeScript($ckeditor_javascript);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }

  // Save the edits to the comment.
  $this
    ->refreshVariables();
  $save_button = $page
    ->find('css', 'form.ajax-comments-form-edit input[value=Save]');
  $this
    ->assertTrue(!empty($save_button));
  $save_button
    ->press();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }
  $this
    ->assertSession()
    ->pageTextContains('Updated comment');
  $this
    ->assertJsCondition($javascript_assertion);

  // Test the cancel button.
  $this
    ->clickLink('Edit');
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  $this
    ->assertSession()
    ->elementExists('css', 'form.ajax-comments-form-edit');
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }
  $cancel_button = $page
    ->find('css', 'form.ajax-comments-form-edit input[value=Cancel]');
  $this
    ->assertTrue(!empty($cancel_button));
  $cancel_button
    ->press();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }

  // Test replying to a comment.
  $this
    ->clickLink('Reply');
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  $comment_body_id = $page
    ->find('css', 'form.ajax-comments-form-reply textarea')
    ->getAttribute('id');
  $ckeditor_javascript = <<<JS
    (function (){
      CKEDITOR.instances['{<span class="php-variable">$comment_body_id</span>}'].setData('Comment reply');
    }());
JS;
  $this
    ->getSession()
    ->executeScript($ckeditor_javascript);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }
  $save_button = $page
    ->find('css', 'form.ajax-comments-form-reply input[value=Save]');
  $this
    ->assertTrue(!empty($save_button));
  $save_button
    ->press();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }
  $this
    ->assertSession()
    ->pageTextContains('Comment reply');
  $this
    ->assertJsCondition($javascript_assertion);

  // Test deleting a comment.
  $delete_link = $page
    ->findLink('Delete');
  $this
    ->assertTrue(!empty($delete_link));
  $delete_url = $delete_link
    ->getAttribute('href');
  $this
    ->assertTrue(!empty($delete_url));

  // Get the comment ID (in $matches[1]).
  preg_match('/comment\\/(.+)\\/delete/i', $delete_url, $matches);
  $this
    ->assertTrue(isset($matches[1]));
  $comment_to_delete = Comment::load($matches[1]);
  $comment_to_delete_body = $comment_to_delete
    ->get('comment_body')->value;
  $delete_form = $this->container
    ->get('entity_type.manager')
    ->getFormObject($comment_to_delete
    ->getEntityTypeId(), 'delete');
  $delete_form
    ->setEntity($comment_to_delete);

  // The delete confirmation question has tags stripped and is truncated
  // in the modal dialog box.
  $confirm_question = substr(strip_tags($delete_form
    ->getQuestion()), 0, 50);
  $delete_link
    ->click();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }
  $this
    ->assertSession()
    ->pageTextContains($confirm_question);
  $delete_button = $page
    ->find('css', '.ui-dialog button.button--primary.js-form-submit');
  $this
    ->assertTrue(!empty($delete_button));
  $delete_button
    ->click();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }
  $this
    ->assertSession()
    ->pageTextNotContains($comment_to_delete_body);
  $this
    ->assertJsCondition($javascript_assertion);

  // Test removing the role's permission to post comments.

  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = Role::loadMultiple($admin_user
    ->getRoles());
  foreach ($roles as $role) {
    $role
      ->revokePermission('post comments');
    $role
      ->trustData()
      ->save();
  }

  // Now try to submit a new comment. We haven't reloaded the page after
  // changing permissions, so the comment field should still be visible.
  $comment_body_id = $page
    ->findField('comment_body[0][value]')
    ->getAttribute('id');
  $ckeditor_javascript = <<<JS
    (function (){
      CKEDITOR.instances['{<span class="php-variable">$comment_body_id</span>}'].setData('This should fail.');
    }());
JS;
  $this
    ->getSession()
    ->executeScript($ckeditor_javascript);
  $page
    ->pressButton('Save');
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);

  // Confirm that the new comment does not appear.
  $this
    ->assertSession()
    ->pageTextNotContains('This should fail.');

  // Confirm that the error message DOES appear.
  $this
    ->assertSession()
    ->pageTextContains('You do not have permission to post a comment.');
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }

  // Restore the user's permission to post comments, and reload the page
  // so that the reply links are visible.

  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = Role::loadMultiple($admin_user
    ->getRoles());
  foreach ($roles as $role) {
    $role
      ->grantPermission('post comments');
    $role
      ->trustData()
      ->save();
  }

  // Reload the page.
  $this
    ->drupalGet($node
    ->toUrl());

  // Revoke the user's permission to post comments, again.

  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = Role::loadMultiple($admin_user
    ->getRoles());
  foreach ($roles as $role) {
    $role
      ->revokePermission('post comments');
    $role
      ->trustData()
      ->save();
  }

  // Click the link to reply to a comment. The link should still be present,
  // because we haven't reloaded the page since revoking the user's
  // permission.
  $reply_link = $page
    ->findLink('Reply');
  $reply_link
    ->click();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }

  // Confirm that the error message appears.
  $this
    ->assertSession()
    ->pageTextContains('You do not have permission to post a comment.');

  // Again, restore the user's permission to post comments, and
  // reload the page so that the reply links are visible.

  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = Role::loadMultiple($admin_user
    ->getRoles());
  foreach ($roles as $role) {
    $role
      ->grantPermission('post comments');
    $role
      ->trustData()
      ->save();
  }

  // Reload the page.
  $this
    ->drupalGet($node
    ->toUrl());

  // Click the link to reply to a comment.
  $reply_link = $page
    ->findLink('Reply');
  $reply_link
    ->click();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);

  // The reply form should load. Enter a comment in the reply field.
  $comment_body_id = $page
    ->find('css', 'form.ajax-comments-form-reply textarea')
    ->getAttribute('id');
  $ckeditor_javascript = <<<JS
    (function (){
      CKEDITOR.instances['{<span class="php-variable">$comment_body_id</span>}'].setData('This reply should fail.');
    }());
JS;
  $this
    ->getSession()
    ->executeScript($ckeditor_javascript);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }

  // Revoke the user's permission to post comments without reloading the page.

  /** @var \Drupal\user\RoleInterface[] $roles */
  $roles = Role::loadMultiple($admin_user
    ->getRoles());
  foreach ($roles as $role) {
    $role
      ->revokePermission('post comments');
    $role
      ->trustData()
      ->save();
  }

  // Now try to click the 'Save' button on the reply form.
  $save_button = $page
    ->find('css', 'form.ajax-comments-form-reply input[value=Save]');
  $this
    ->assertTrue(!empty($save_button));
  $save_button
    ->press();
  $this
    ->assertSession()
    ->assertWaitOnAjaxRequest(20000);
  if ($this->htmlOutputEnabled) {
    $out = $page
      ->getContent();
    $html_output = $out . '<hr />' . $this
      ->getHtmlOutputHeaders();
    $this
      ->htmlOutput($html_output);
  }

  // Confirm that the new comment does not appear.
  $this
    ->assertSession()
    ->pageTextNotContains('This reply should fail.');

  // Confirm that the error message DOES appear.
  $this
    ->assertSession()
    ->pageTextContains('You do not have permission to post a comment.');
}