EditLoadingTest.php

Contains \Drupal\edit\Tests\EditLoadingTest.

Namespace

Drupal\edit\Tests

File

drupal/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php
View source
<?php

/**
 * @file
 * Contains \Drupal\edit\Tests\EditLoadingTest.
 */
namespace Drupal\edit\Tests;

use Drupal\simpletest\WebTestBase;
use Drupal\edit\Ajax\MetadataCommand;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Component\Utility\Unicode;

/**
 * Tests loading of Edit and lazy-loading of in-place editors.
 */
class EditLoadingTest extends WebTestBase {

  /**
   * Modules to enable.
   *
   * @var array
   */
  public static $modules = array(
    'contextual',
    'edit',
    'filter',
    'node',
  );
  public static function getInfo() {
    return array(
      'name' => 'In-place editing loading',
      'description' => 'Tests loading of in-place editing functionality and lazy loading of its in-place editors.',
      'group' => 'Edit',
    );
  }
  function setUp() {
    parent::setUp();

    // Create a text format.
    $filtered_html_format = entity_create('filter_format', array(
      'format' => 'filtered_html',
      'name' => 'Filtered HTML',
      'weight' => 0,
      'filters' => array(),
    ));
    $filtered_html_format
      ->save();

    // Create a node type.
    $this
      ->drupalCreateContentType(array(
      'type' => 'article',
      'name' => 'Article',
    ));

    // Create one node of the above node type using the above text format.
    $this
      ->drupalCreateNode(array(
      'type' => 'article',
      'body' => array(
        0 => array(
          'value' => '<p>How are you?</p>',
          'format' => 'filtered_html',
        ),
      ),
    ));

    // Create 2 users, the only difference being the ability to use in-place
    // editing
    $basic_permissions = array(
      'access content',
      'create article content',
      'edit any article content',
      'use text format filtered_html',
      'access contextual links',
    );
    $this->author_user = $this
      ->drupalCreateUser($basic_permissions);
    $this->editor_user = $this
      ->drupalCreateUser(array_merge($basic_permissions, array(
      'access in-place editing',
    )));
  }

  /**
   * Test the loading of Edit when a user doesn't have access to it.
   */
  function testUserWithoutPermission() {
    $this
      ->drupalLogin($this->author_user);
    $this
      ->drupalGet('node/1');

    // Settings, library and in-place editors.
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertFalse(isset($settings['edit']), 'Edit settings do not exist.');
    $this
      ->assertFalse(isset($settings['ajaxPageState']['js']['core/modules/edit/js/edit.js']), 'Edit library not loaded.');
    $this
      ->assertFalse(isset($settings['ajaxPageState']['js']['core/modules/edit/js/createjs/editingWidgets/formwidget.js']), "'form' in-place editor not loaded.");

    // HTML annotation must always exist (to not break the render cache).
    $this
      ->assertRaw('data-edit-entity="node/1"');
    $this
      ->assertRaw('data-edit-id="node/1/body/und/full"');

    // Retrieving the metadata should result in an empty 403 response.
    $response = $this
      ->retrieveMetadata(array(
      'node/1/body/und/full',
    ));
    $this
      ->assertIdentical('{}', $response);
    $this
      ->assertResponse(403);
  }

  /**
   * Tests the loading of Edit when a user does have access to it.
   *
   * Also ensures lazy loading of in-place editors works.
   */
  function testUserWithPermission() {
    $this
      ->drupalLogin($this->editor_user);
    $this
      ->drupalGet('node/1');

    // Settings, library and in-place editors.
    $settings = $this
      ->drupalGetSettings();
    $this
      ->assertTrue(isset($settings['edit']), 'Edit settings exist.');
    $this
      ->assertTrue(isset($settings['ajaxPageState']['js']['core/modules/edit/js/edit.js']), 'Edit library loaded.');
    $this
      ->assertFalse(isset($settings['ajaxPageState']['js']['core/modules/edit/js/createjs/editingWidgets/formwidget.js']), "'form' in-place editor not loaded.");

    // HTML annotation must always exist (to not break the render cache).
    $this
      ->assertRaw('data-edit-entity="node/1"');
    $this
      ->assertRaw('data-edit-id="node/1/body/und/full"');

    // Retrieving the metadata should result in a 200 JSON response.
    $htmlPageDrupalSettings = $this->drupalSettings;
    $response = $this
      ->retrieveMetadata(array(
      'node/1/body/und/full',
    ));
    $this
      ->assertResponse(200);
    $expected = array(
      'node/1/body/und/full' => array(
        'label' => 'Body',
        'access' => TRUE,
        'editor' => 'form',
        'aria' => 'Entity node 1, field Body',
      ),
    );
    $this
      ->assertIdentical(drupal_json_decode($response), $expected, 'The metadata HTTP request answers with the correct JSON response.');

    // Restore drupalSettings to build the next requests; simpletest wipes them
    // after a JSON response.
    $this->drupalSettings = $htmlPageDrupalSettings;

    // Retrieving the attachments should result in a 200 response, containing:
    //  1. a settings command with useless metadata: AjaxController is dumb
    //  2. an insert command that loads the required in-place editors
    $response = $this
      ->retrieveAttachments(array(
      'form',
    ));
    $ajax_commands = drupal_json_decode($response);
    $this
      ->assertIdentical(2, count($ajax_commands), 'The attachments HTTP request results in two AJAX commands.');

    // First command: settings.
    $this
      ->assertIdentical('settings', $ajax_commands[0]['command'], 'The first AJAX command is a settings command.');

    // Second command: insert libraries into DOM.
    $this
      ->assertIdentical('insert', $ajax_commands[1]['command'], 'The second AJAX command is an append command.');
    $command = new AppendCommand('body', '<script src="' . file_create_url('core/modules/edit/js/editors/formEditor.js') . '?v=' . VERSION . '"></script>' . "\n");
    $this
      ->assertIdentical($command
      ->render(), $ajax_commands[1], 'The append command contains the expected data.');

    // Retrieving the form for this field should result in a 200 response,
    // containing only an editFieldForm command.
    $response = $this
      ->retrieveFieldForm('node/1/body/und/full');
    $this
      ->assertResponse(200);
    $ajax_commands = drupal_json_decode($response);
    $this
      ->assertIdentical(1, count($ajax_commands), 'The field form HTTP request results in three AJAX commands.');
    $this
      ->assertIdentical('editFieldForm', $ajax_commands[0]['command'], 'The first AJAX command is an editFieldForm command.');
    $this
      ->assertIdentical('<form ', Unicode::substr($ajax_commands[0]['data'], 0, 6), 'The editFieldForm command contains a form.');
  }

  /**
   * Retrieve Edit metadata from the server. May also result in additional
   * JavaScript settings and CSS/JS being loaded.
   *
   * @param array $ids
   *   An array of edit ids.
   *
   * @return string
   *   The response body.
   */
  protected function retrieveMetadata($ids) {

    // Build POST values.
    $post = array();
    for ($i = 0; $i < count($ids); $i++) {
      $post['fields[' . $i . ']'] = $ids[$i];
    }

    // Serialize POST values.
    foreach ($post as $key => $value) {

      // Encode according to application/x-www-form-urlencoded
      // Both names and values needs to be urlencoded, according to
      // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
      $post[$key] = urlencode($key) . '=' . urlencode($value);
    }
    $post = implode('&', $post);

    // Perform HTTP request.
    return $this
      ->curlExec(array(
      CURLOPT_URL => url('edit/metadata', array(
        'absolute' => TRUE,
      )),
      CURLOPT_POST => TRUE,
      CURLOPT_POSTFIELDS => $post . $this
        ->getAjaxPageStatePostData(),
      CURLOPT_HTTPHEADER => array(
        'Accept: application/json',
        'Content-Type: application/x-www-form-urlencoded',
      ),
    ));
  }

  /**
   * Retrieves AJAX commands to load attachments for the given in-place editors.
   *
   * @param array $editors
   *   An array of in-place editor ids.
   *
   * @return string
   *   The response body.
   */
  protected function retrieveAttachments($editors) {

    // Build POST values.
    $post = array();
    for ($i = 0; $i < count($editors); $i++) {
      $post['editors[' . $i . ']'] = $editors[$i];
    }

    // Serialize POST values.
    foreach ($post as $key => $value) {

      // Encode according to application/x-www-form-urlencoded
      // Both names and values needs to be urlencoded, according to
      // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
      $post[$key] = urlencode($key) . '=' . urlencode($value);
    }
    $post = implode('&', $post);

    // Perform HTTP request.
    return $this
      ->curlExec(array(
      CURLOPT_URL => url('edit/attachments', array(
        'absolute' => TRUE,
      )),
      CURLOPT_POST => TRUE,
      CURLOPT_POSTFIELDS => $post . $this
        ->getAjaxPageStatePostData(),
      CURLOPT_HTTPHEADER => array(
        'Accept: application/vnd.drupal-ajax',
        'Content-Type: application/x-www-form-urlencoded',
      ),
    ));
  }

  /**
   * Retrieve field form from the server. May also result in additional
   * JavaScript settings and CSS/JS being loaded.
   *
   * @param string $field_id
   *   An Edit field ID.
   *
   * @return string
   *   The response body.
   */
  protected function retrieveFieldForm($field_id) {

    // Build & serialize POST value.
    $post = urlencode('nocssjs') . '=' . urlencode('true');

    // Perform HTTP request.
    return $this
      ->curlExec(array(
      CURLOPT_URL => url('edit/form/' . $field_id, array(
        'absolute' => TRUE,
      )),
      CURLOPT_POST => TRUE,
      CURLOPT_POSTFIELDS => $post . $this
        ->getAjaxPageStatePostData(),
      CURLOPT_HTTPHEADER => array(
        'Accept: application/vnd.drupal-ajax',
        'Content-Type: application/x-www-form-urlencoded',
      ),
    ));
  }

  /**
   * Get extra information to the POST data as ajax.js does.
   *
   * @return string
   *   Additional post data.
   */
  protected function getAjaxPageStatePostData() {
    $extra_post = '';
    $drupal_settings = $this->drupalSettings;
    if (isset($drupal_settings['ajaxPageState'])) {
      $extra_post .= '&' . urlencode('ajax_page_state[theme]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme']);
      $extra_post .= '&' . urlencode('ajax_page_state[theme_token]') . '=' . urlencode($drupal_settings['ajaxPageState']['theme_token']);
      foreach ($drupal_settings['ajaxPageState']['css'] as $key => $value) {
        $extra_post .= '&' . urlencode("ajax_page_state[css][{$key}]") . '=1';
      }
      foreach ($drupal_settings['ajaxPageState']['js'] as $key => $value) {
        $extra_post .= '&' . urlencode("ajax_page_state[js][{$key}]") . '=1';
      }
    }
    return $extra_post;
  }

}

Classes

Namesort descending Description
EditLoadingTest Tests loading of Edit and lazy-loading of in-place editors.