You are here

query_parameters_to_url.test in Query Parameters To URL 7

Query Arguments To URL tests.

File

query_parameters_to_url.test
View source
<?php

/**
 * @file
 * Query Arguments To URL tests.
 */

/**
 * General test cases.
 */
class QueryParametersToURLTestCase extends DrupalWebTestCase {

  /**
   * Returns test info.
   */
  public static function getInfo() {
    return array(
      'name' => 'Query Parameters To URL Integration tests',
      'description' => 'Test Query Parameters To URL functionality.',
      'group' => 'Query Parameters To URL',
    );
  }

  /**
   * Initial setup.
   */
  public function setUp() {
    parent::setUp(array(
      'query_parameters_to_url',
    ));

    // Enable rewriting on all paths.
    variable_set(QUERY_PARAMETERS_TO_URL_PATH_REG_EXP, '{.+}');

    // Clean URLs should be enabled for testing.
    variable_set('clean_url', 1);

    // Create test node just for the sake of something being on the front page.
    $node = array(
      'type' => 'page',
      'title' => 'Test Page Node',
      'path' => array(
        'alias' => 'test-node',
      ),
      'language' => LANGUAGE_NONE,
      'promote' => 1,
    );

    // Save the node.
    $this
      ->drupalCreateNode($node);
  }

  /**
   * Returns list of paths to test.
   */
  public function pathsToTest() {
    $paths = array();
    $paths[] = array(
      'path' => 'node',
      'options' => array(
        'query' => array(
          'a' => array(
            1,
            2,
          ),
          'b' => array(
            'c' => 3,
          ),
        ),
      ),
      'expected_path' => 'node/p/a/0__1--1__2/b/c__3',
    );
    return $paths;
  }

  /**
   * Tests if a path with query parameters gets redirected to the clean path.
   */
  public function testRedirectWithQueryParameters() {
    $this
      ->pass('Test redirecting to clean url when url has query parameters.');
    $test_cases = $this
      ->pathsToTest();
    foreach ($test_cases as $test_case) {
      list($path, $options, $expected_path) = $this
        ->unpackTestCase($test_case);

      // Make sure they are absolute URLs.
      $path = $this
        ->createRawUrl($path, $options);
      $expected_path = $this
        ->createRawUrl($expected_path, array());

      // Make HEAD request and check the headers.
      $this
        ->drupalHeadRawURL($path);
      $headers = $this
        ->drupalGetHeaders(TRUE);
      $result = array(
        '!initial_path' => $path,
        '!expected_status' => '302',
        '!expected_path' => $expected_path,
        '!actual_path' => isset($headers[0]['location']) ? $headers[0]['location'] : 'N/A',
        '!actual_status' => $headers[0][':status'],
      );
      $this
        ->assertStatusAndPath($result);
    }
  }

  /**
   * Tests if inbound/outbound hooks do their job.
   */
  public function testURLRewriting() {
    $this
      ->pass('Test if urls are properly rewritten.');
    $test_cases = $this
      ->pathsToTest();
    foreach ($test_cases as $test_case) {
      list($path, $options, $expected_path) = $this
        ->unpackTestCase($test_case);

      // Make sure they are absolute URLs.
      $expected_path = $this
        ->createRawUrl($expected_path, array());

      // Make GET request and check the headers.
      $this
        ->drupalGet($path, $options);
      $headers = $this
        ->drupalGetHeaders(TRUE);
      $result = array(
        '!initial_path' => $path,
        '!expected_status' => '200',
        '!expected_path' => $expected_path,
        '!actual_path' => $this
          ->getUrl(),
        '!actual_status' => $headers[0][':status'],
      );
      $this
        ->assertStatusAndPath($result);
    }
  }

  /**
   * Tests url() encoding and decoding, affected by inbound / outbound hooks.
   */
  public function testUrlEncodingAndDecoding() {
    $this
      ->pass('Test encoding / decoding affected by inbound / outbound hooks.');
    $test_cases = array();
    $test_cases[] = array(
      'path' => 'node',
      'query' => array(
        'page' => 1,
        'f' => array(
          0 => 'bundle:standard_page',
          1 => 'sm_og_group_ref:node:100',
          2 => 'dm_field_date:[2014-11-01T00:00:00Z TO 2014-12-01T00:00:00Z]',
        ),
      ),
      'expected_path' => 'node/p/page/1/f/0__bundle%3Astandard_page--1__sm_og_group_ref%3Anode%3A100--2__dm_field_date%3A%5B2014-11-01T00%3A00%3A00Z%20TO%202014-12-01T00%3A00%3A00Z%5D',
    );
    foreach ($test_cases as $test_case) {
      $initial_url = $test_case['path'];
      $initial_query = $test_case['query'];
      $expected_path = $this
        ->createRawUrl($test_case['expected_path'], array());
      $options = array(
        'query' => $initial_query,
        'absolute' => TRUE,
      );
      $encoded_url = url($initial_url, $options);
      $message = 'Encoding initial URL:<br/>!url<br/>Initial Query:<br/>!query<br/>Encoded to:<br/>!encoded<br/>Expected:<br/>!expected';
      $message = format_string($message, array(
        '!url' => $initial_url,
        '!query' => var_export($initial_query, TRUE),
        '!encoded' => $encoded_url,
        '!expected' => $expected_path,
      ));
      $this
        ->assertEqual($encoded_url, $expected_path, $message);
      $decoded_url = $encoded_url;
      $path_language = 'en';
      query_parameters_to_url_url_inbound_alter($decoded_url, $decoded_url, $path_language);
      $initial_url_absolute = $this
        ->createRawUrl($initial_url, array());
      $message = 'Decoding clean URL:<br/>!url<br/>Decoded to:<br/>!decoded<br/>Expected:<br/>!expected';
      $message = format_string($message, array(
        '!url' => $encoded_url,
        '!decoded' => $decoded_url,
        '!expected' => $initial_url_absolute,
      ));
      $this
        ->assertEqual($decoded_url, $initial_url_absolute, $message);

      // Get the query parameters set by the inbound hook in $_GET. Make sure
      // they are URL decoded, and that only the same parameters that were set
      // in the test case are used.
      $query_parameters = drupal_get_query_parameters();
      $query_parameters_cleaned = $query_parameters;
      array_walk_recursive($query_parameters_cleaned, array(
        'QueryParametersToURLTestCase',
        'applyUrlDecode',
      ));
      foreach ($query_parameters_cleaned as $key => $parameter) {
        if (!isset($initial_query[$key])) {
          unset($query_parameters_cleaned[$key]);
        }
      }
      $message = 'Decoding clean URL:<br/>!url<br/>Decoded query parameters to:<br/>!decoded<br/>Expected query parameters:<br/>!expected';
      $message = format_string($message, array(
        '!url' => $encoded_url,
        '!decoded' => var_export($query_parameters_cleaned, TRUE),
        '!expected' => var_export($initial_query, TRUE),
      ));
      $this
        ->assertEqual($query_parameters_cleaned, $initial_query, $message);
    }
  }

  /**
   * URL decodes an array value.
   */
  protected static function applyUrlDecode(&$item, $key) {
    $item = urldecode($item);
  }

  /**
   * Creates full absolute URL without going through url function().
   */
  public function createRawUrl($path, $options) {
    global $base_url, $base_secure_url, $base_insecure_url;

    // The base_url might be rewritten from the language rewrite in domain mode.
    if (!isset($options['base_url'])) {
      if (isset($options['https']) && variable_get('https', FALSE)) {
        if ($options['https'] === TRUE) {
          $options['base_url'] = $base_secure_url;
          $options['absolute'] = TRUE;
        }
        elseif ($options['https'] === FALSE) {
          $options['base_url'] = $base_insecure_url;
          $options['absolute'] = TRUE;
        }
      }
      else {
        $options['base_url'] = $base_url;
      }
    }
    $base = $options['base_url'] . '/';
    if (isset($options['query']) && !empty($options['query'])) {
      return $base . $path . '?' . drupal_http_build_query($options['query']);
    }
    else {
      return $base . $path;
    }
  }

  /**
   * Unpacks test case.
   */
  public function unpackTestCase($test_case) {
    return array(
      $test_case['path'],
      $test_case['options'],
      $test_case['expected_path'],
    );
  }

  /**
   * Similar to drupalHead, but without piping to the url() function.
   */
  public function drupalHeadRawURL($path, array $headers = array()) {
    $out = $this
      ->curlExec(array(
      CURLOPT_NOBODY => TRUE,
      CURLOPT_URL => $path,
      CURLOPT_HTTPHEADER => $headers,
    ));
    $this
      ->refreshVariables();

    // Ensure that any changes to variables in the other thread are picked up.
    return $out;
  }

  /**
   * Success message.
   */
  public function getSuccessMessage() {
    return 'SUCCESS<br />Initial Path: !initial_path<br />Expected Path: !expected_path<br />Expected Status Code: !expected_status<br />Actual path: !actual_path<br />Actual Status: !actual_status';
  }

  /**
   * Error message.
   */
  public function getErrorMessage() {
    return 'ERROR<br />Initial Path: !initial_path<br />Expected Path: !expected_path<br />Expected Status Code: !expected_status<br />Actual path: !actual_path<br />Actual Status: !actual_status';
  }

  /**
   * Checks if HTTP status code matches.
   */
  public function expectedStatus($status, $expected_status) {
    return strpos($status, (string) $expected_status) !== FALSE;
  }

  /**
   * Asserts that the status code and paths are equivalent.
   */
  public function assertStatusAndPath($result) {

    // Make sure status and paths actually match.
    if ($this
      ->expectedStatus($result['!actual_status'], $result['!expected_status']) && $result['!actual_path'] == $result['!expected_path']) {
      return $this
        ->pass(format_string($this
        ->getSuccessMessage(), $result));
    }
    else {
      return $this
        ->fail(format_string($this
        ->getErrorMessage(), $result));
    }
  }

}

/**
 * Class QueryParametersToURLUnitTestCase.
 */
class QueryParametersToURLUnitTestCase extends DrupalUnitTestCase {

  /**
   * Returns test info.
   */
  public static function getInfo() {
    return array(
      'name' => 'Query Parameters To URL Unit tests',
      'description' => 'Test query parameter values encoding / decoding.',
      'group' => 'Query Parameters To URL',
    );
  }

  /**
   * Setup.
   */
  public function setUp() {
    drupal_load('module', 'query_parameters_to_url');
    parent::setUp();
  }

  /**
   * Returns a list of test cases.
   */
  public function getTestCases() {
    $test_cases = array();

    // ?value[a][1][2]=3&value[a][1][6]=7&value[b]=4&value[c]=5
    $test_cases[] = array(
      'parameter_value_array' => array(
        'a' => array(
          1 => array(
            2 => 3,
            6 => 7,
          ),
        ),
        'b' => 4,
        'c' => 5,
      ),
      'encoded_parameter_value_string' => 'a__1__2__3--a__1__6__7--b__4--c__5',
    );

    // ?f[0]=standard_page&f[1]=sm_og_group_ref:node:100
    $test_cases[] = array(
      'parameter_value_array' => array(
        0 => 'standard_page',
        1 => 'sm_og_group_ref:node:100',
      ),
      'encoded_parameter_value_string' => '0__standard_page--1__sm_og_group_ref:node:100',
    );
    return $test_cases;
  }

  /**
   * Unpacks a test case.
   */
  public function unpackTestCase($test_case) {
    return array(
      $test_case['parameter_value_array'],
      $test_case['encoded_parameter_value_string'],
    );
  }

  /**
   * Tests if a query parameter's values are properly encoded.
   */
  public function testQueryParameterValuesEncoding() {
    $test_cases = $this
      ->getTestCases();
    foreach ($test_cases as $test_case) {
      list($parameter_values, $expected_query_parameter_string) = $this
        ->unpackTestCase($test_case);
      $encoded_query_parameter = query_parameters_to_url_encode_query_parameter_values($parameter_values);
      $message = 'Query parameter values are encoded properly.<br/> Initial array:<br/>!initial<br/>Encoded to string:<br/>!string';
      $message = format_string($message, array(
        '!initial' => var_export($parameter_values, TRUE),
        '!string' => var_export($encoded_query_parameter, TRUE),
      ));
      $this
        ->assertEqual($encoded_query_parameter, $expected_query_parameter_string, $message);
    }
  }

  /**
   * Tests if an encoded query parameter's values is properly decoded.
   */
  public function testQueryParameterValuesDecoding() {
    $test_cases = $this
      ->getTestCases();
    foreach ($test_cases as $test_case) {
      list($expected_parameter_values, $encoded_parameter_string) = $this
        ->unpackTestCase($test_case);
      $decoded_query_parameter_values = query_parameters_to_url_decode_query_parameter_values($encoded_parameter_string);
      $message = 'Query parameter values are decoded properly.<br/> Initial string:<br/>!initial<br/>Decoded to array:<br/>!array';
      $message = format_string($message, array(
        '!initial' => var_export($encoded_parameter_string, TRUE),
        '!array' => var_export($decoded_query_parameter_values, TRUE),
      ));
      $this
        ->assertEqual($decoded_query_parameter_values, $expected_parameter_values, $message);
    }
  }

}

/**
 * Global redirect module integration.
 */
class QueryParametersToURLGlobalRedirectTestCase extends QueryParametersToURLTestCase {

  /**
   * Returns test info.
   */
  public static function getInfo() {
    return array(
      'name' => 'Query Parameters To URL Global Redirect integration tests',
      'description' => 'Test Query Parameters To URL integration with global redirect module.',
      'group' => 'Query Parameters To URL',
      'dependencies' => array(
        'globalredirect',
      ),
    );
  }

  /**
   * Initial setup.
   */
  public function setUp() {
    DrupalWebTestCase::setUp(array(
      'globalredirect',
      'query_parameters_to_url',
    ));

    // Enable rewriting on all paths.
    variable_set(QUERY_PARAMETERS_TO_URL_PATH_REG_EXP, '{.+}');

    // Clean URLs should be enabled for testing.
    variable_set('clean_url', 1);

    // Create test node just for the sake of something being on the front page.
    $node = array(
      'type' => 'page',
      'title' => 'Test Page Node',
      'path' => array(
        'alias' => 'test-node',
      ),
      'language' => LANGUAGE_NONE,
      'promote' => 1,
    );

    // Save the node.
    $this
      ->drupalCreateNode($node);
  }

  /**
   * Tests if a path with query parameters gets redirected to the clean path.
   */
  public function testRedirectWithQueryParameters() {
    $this
      ->pass('Test redirecting to clean url when url has query parameters.');
    $test_cases = $this
      ->pathsToTest();
    foreach ($test_cases as $test_case) {
      list($path, $options, $expected_path) = $this
        ->unpackTestCase($test_case);

      // Make sure they are absolute URLs.
      $raw_path = $this
        ->createRawUrl($path, $options);

      // The expected path in case when global redirect is enabled, and the path
      // was the frontpage, is to remove the front page prefix, so it's empty.
      $expected_path_parts = explode('/', $expected_path);
      array_shift($expected_path_parts);
      $expected_path = implode('/', $expected_path_parts);
      $raw_expected_path = $this
        ->createRawUrl($expected_path, array());

      // Make HEAD request and check the headers.
      $this
        ->drupalHeadRawURL($raw_path);
      $headers = $this
        ->drupalGetHeaders(TRUE);

      // First Global Redirect does it's own redirect, to a clean front page,
      // but with the query parameters present in usual form.
      $expected_redirect_path = $this
        ->createRawUrl('', $options);
      $result_from_global_redirect = array(
        '!initial_path' => $path,
        '!expected_status' => '301',
        '!expected_path' => $expected_redirect_path,
        '!actual_path' => isset($headers[0]['location']) ? $headers[0]['location'] : 'N/A',
        '!actual_status' => $headers[0][':status'],
      );
      $this
        ->assertStatusAndPath($result_from_global_redirect);

      // Then we check for query parameters to url redirect, which redirects
      // to the clean URL form.
      $result_from_our_module_redirect = array(
        '!initial_path' => $path,
        '!expected_status' => '302',
        '!expected_path' => $raw_expected_path,
        '!actual_path' => isset($headers[1]['location']) ? $headers[1]['location'] : 'N/A',
        '!actual_status' => $headers[1][':status'],
      );
      $this
        ->assertStatusAndPath($result_from_our_module_redirect);
    }
  }

  /**
   * Tests if inbound/outbound hooks do their job.
   */
  public function testURLRewriting() {
    $this
      ->pass('Test if urls are properly rewritten.');
    $test_cases = $this
      ->pathsToTest();
    foreach ($test_cases as $test_case) {
      list($path, $options, $expected_path) = $this
        ->unpackTestCase($test_case);

      // The expected path in case when global redirect is enabled, and the path
      // was the frontpage, is to remove the front page prefix, so it's empty.
      $expected_path_parts = explode('/', $expected_path);
      array_shift($expected_path_parts);
      $expected_path = implode('/', $expected_path_parts);
      $raw_expected_path = $this
        ->createRawUrl($expected_path, array());

      // Make GET request and check the headers.
      $this
        ->drupalGet($path, $options);
      $headers = $this
        ->drupalGetHeaders(TRUE);

      // First Global Redirect does it's own redirect, to a clean front page,
      // but with the query parameters present in usual form.
      $expected_redirect_path = $this
        ->createRawUrl('', $options);
      $result = array(
        '!initial_path' => $path,
        '!expected_status' => '301',
        '!expected_path' => $expected_redirect_path,
        '!actual_path' => isset($headers[0]['location']) ? $headers[0]['location'] : 'N/A',
        '!actual_status' => $headers[0][':status'],
      );
      $success = $this
        ->assertStatusAndPath($result);
      if (!$success) {
        $this
          ->fail('<pre>' . print_r($headers, TRUE) . '</pre>');
      }

      // Then we check for query parameters to url redirect, which redirects
      // to the clean URL form.
      $result = array(
        '!initial_path' => $headers[0]['location'],
        '!expected_status' => '302',
        '!expected_path' => $raw_expected_path,
        '!actual_path' => isset($headers[1]['location']) ? $headers[1]['location'] : 'N/A',
        '!actual_status' => $headers[1][':status'],
      );
      $success = $this
        ->assertStatusAndPath($result);
      if (!$success) {
        $this
          ->fail('<pre>' . print_r($headers, TRUE) . '</pre>');
      }

      // Then we get to the actual clean URL.
      $result = array(
        '!initial_path' => $headers[1]['location'],
        '!expected_status' => '200',
        '!expected_path' => $raw_expected_path,
        '!actual_path' => $this
          ->getUrl(),
        '!actual_status' => $headers[2][':status'],
      );
      $success = $this
        ->assertStatusAndPath($result);
      if (!$success) {
        $this
          ->fail('<pre>' . print_r($headers, TRUE) . '</pre>');
      }
    }
  }

}

/**
 * Menu item save tests.
 */
class QueryParametersToURLMenuItemTestCase extends DrupalWebTestCase {
  protected $admin_user;

  /**
   * Returns test info.
   */
  public static function getInfo() {
    return array(
      'name' => 'Query Parameters To URL Menu Item tests',
      'description' => 'Test saving menu items with rewritten URLs that contain encoded query parameters.',
      'group' => 'Query Parameters To URL',
    );
  }

  /**
   * Initial setup.
   */
  public function setUp() {
    parent::setUp(array(
      'menu',
      'query_parameters_to_url',
    ));

    // Enable rewriting on all paths.
    variable_set(QUERY_PARAMETERS_TO_URL_PATH_REG_EXP, '{.+}');

    // Clean URLs should be enabled for testing.
    variable_set('clean_url', 1);

    // Create privileged user.
    $permissions = array(
      'access administration pages',
      'administer blocks',
      'administer menu',
      'administer site configuration',
      'create page content',
      'edit any page content',
      'delete any page content',
    );
    $this->admin_user = $this
      ->drupalCreateUser($permissions);
  }

  /**
   * Tests saving menu items that contain rewritten URLs with parameters.
   */
  public function testMenuItemSave() {
    $this
      ->drupalLogin($this->admin_user);
    $alias = 'test-node';

    // Create test node which will be inserted into the menu item.
    $node = array(
      'type' => 'page',
      'title' => 'Test Page Node',
      'path' => array(
        'alias' => $alias,
      ),
      'language' => LANGUAGE_NONE,
      'promote' => 1,
    );

    // Save the node.
    $saved_node = $this
      ->drupalCreateNode($node);
    $encoded_url = 'node/' . $saved_node->nid . '/p/a/0__1--1__2/b/c__3';
    $expected_stripped_url = 'node/' . $saved_node->nid;
    $link_title = 'Test Menu Item';

    // Add the menu item.
    $edit = array();
    $edit['link_title'] = $link_title;
    $edit['link_path'] = $encoded_url;
    $this
      ->drupalPost('admin/structure/menu/manage/main-menu/add', $edit, t('Save'));

    // Check that the URL encoded parameters were stripped, because menu item
    // saving was not enabled yet.
    $this
      ->assertText(t('The menu system stores system paths only, but will use the URL alias for display. !link_path has been stored as !normal_path', array(
      '!link_path' => $encoded_url,
      '!normal_path' => $expected_stripped_url,
    )));

    // Enable menu item saving.
    variable_set(QUERY_PARAMETERS_TO_URL_ALLOW_REWRITTEN_MENU_ITEM_SAVE, TRUE);

    // Get the menu item listing page.
    $this
      ->drupalGet('admin/structure/menu/manage/main-menu');

    // Get the edit link for the created menu item.
    $this
      ->clickLink('edit', 1);
    $edit_menu_item_url = $this
      ->getUrl();

    // Re-submit the proper URL.
    $edit = array();
    $edit['link_title'] = $link_title;
    $edit['link_path'] = $encoded_url;
    $this
      ->drupalPost($edit_menu_item_url, $edit, t('Save'));

    // This time the message should not appear, because we enabled saving of
    // menu items that contain rewritten URLs.
    $this
      ->assertNoText(t('The menu system stores system paths only, but will use the URL alias for display. !link_path has been stored as !normal_path', array(
      '!link_path' => $encoded_url,
      '!normal_path' => $expected_stripped_url,
    )));

    // Get the URL saved for the menu link.
    $this
      ->drupalGet('admin/structure/menu/manage/main-menu');
    $this
      ->assertLinkByHref($encoded_url);

    // Click on the link, and check that it led to the proper page.
    $this
      ->clickLink($link_title);
    $url = $this
      ->getUrl();
    $this
      ->assertEqual($url, $this
      ->getAbsoluteUrl($encoded_url));
  }

}

Classes

Namesort descending Description
QueryParametersToURLGlobalRedirectTestCase Global redirect module integration.
QueryParametersToURLMenuItemTestCase Menu item save tests.
QueryParametersToURLTestCase General test cases.
QueryParametersToURLUnitTestCase Class QueryParametersToURLUnitTestCase.