You are here

language_hierarchy.test in Language Hierarchy 7

Tests for Language Hierarchy module.

File

tests/language_hierarchy.test
View source
<?php

/**
 * @file
 * Tests for Language Hierarchy module.
 */

/**
 * Base class for Language Hierarchy module tests.
 */
abstract class LanguageHierarchyBaseTestCase extends DrupalWebTestCase {
  protected $current_user;
  protected $admin_user;
  protected $translator_user;
  protected $admin_user_permissions = array(
    'administer languages',
    'access administration pages',
  );
  protected $translator_user_permissions = array(
    'translate interface',
    'access administration pages',
  );

  // Hierarchical list of languages used in this test.
  protected $languages = array(
    // Portuguese, International
    array(
      'langcode' => 'pt',
      'parent' => '',
    ),
    // Portuguese, Brazil
    array(
      'langcode' => 'pt-br',
      'parent' => 'pt',
    ),
    // Portuguese, Portugal
    array(
      'langcode' => 'pt-pt',
      'parent' => 'pt',
    ),
  );
  function setUp() {
    $args = func_get_args();
    call_user_func_array(array(
      'parent',
      'setUp',
    ), $args);

    // Reset user fields to make test object reusable.
    unset($this->current_user);
    unset($this->admin_user);
    unset($this->translator_user);
  }

  /**
   * Get a language object from a language code.
   */
  public function getLanguage($langcode) {
    if (is_object($langcode)) {
      return $langcode;
    }
    else {
      $language_list = language_list();
      return $language_list[$langcode];
    }
  }

  /**
   * Install a specified language if it has not been already, otherwise make
   * sure that the language is enabled.
   *
   * @param $langcode
   *   Langcode of language to be added.
   * @param string|null $parent
   *   Langcode of parent language, NULL if there is no parent language.
   */
  public function addLanguage($langcode, $parent = NULL) {

    // Check to make sure that language has not already been installed.
    $this
      ->drupalGet('admin/config/regional/language');
    if (strpos($this
      ->drupalGetContent(), 'enabled[' . $langcode . ']') === FALSE) {

      // Doesn't have language installed so add it.
      $edit = array();
      if ($parent) {
        $edit['parent_language_list'] = $parent;
      }
      $edit['langcode'] = $langcode;
      $this
        ->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));

      // Make sure we are not using a stale list.
      drupal_static_reset('language_list');
      $languages = language_list('language');
      $this
        ->assertTrue(array_key_exists($langcode, $languages), t('Language was installed successfully.'));
      if (array_key_exists($langcode, $languages)) {
        $this
          ->assertRaw(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array(
          '%language' => $languages[$langcode]->name,
          '@locale-help' => url('admin/help/locale'),
        )), t('Language has been created.'));
      }
    }
    elseif ($this
      ->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(
      ':name' => 'enabled[' . $langcode . ']',
    ))) {

      // It is installed and enabled. No need to do anything.
      $this
        ->assertTrue(TRUE, 'Language [' . $langcode . '] already installed and enabled.');
    }
    else {

      // It is installed but not enabled. Enable it.
      $this
        ->assertTrue(TRUE, 'Language [' . $langcode . '] already installed.');
      $this
        ->drupalPost(NULL, array(
        'enabled[' . $langcode . ']' => TRUE,
      ), t('Save configuration'));
      $this
        ->assertRaw(t('Configuration saved.'), t('Language successfully enabled.'));
    }
  }

  /**
   * Add required languages.
   */
  public function addLanguages() {
    foreach ($this->languages as $language) {

      // Add language.
      $this
        ->addLanguage($language['langcode'], $language['parent']);
    }
  }

  /**
   * Translates given string into language of choice.
   *
   * @param string $string
   *   String to translate.
   * @param string $translation
   *   Translation of that string.
   * @param string $langcode
   *   Target language of translation.
   */
  public function addStringTranslation($string, $translation, $langcode) {
    $search = array(
      'string' => $string,
      'language' => 'all',
      'translation' => 'all',
      'group' => 'all',
    );
    $this
      ->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));

    // assertText() seems to remove the input field where $string always could be
    // found, so this is not a false assert. See how assertNoText succeeds
    // later.
    $this
      ->assertText($string, 'Search found the string.');
    $this
      ->assertRaw($this
      ->getLanguageIndicator($langcode), 'String is untranslated.');

    // Assume this is the only result, given the random string.
    $this
      ->clickLink(t('edit'));

    // No t() here, it's surely not translated yet.
    $this
      ->assertText($string, 'String found on edit screen.');
    $edit = array(
      "translations[{$langcode}]" => $translation,
    );
    $this
      ->drupalPost(NULL, $edit, t('Save translations'));
    $this
      ->assertText(t('The string has been saved.'), 'The string has been saved.');
    $this
      ->assertEqual($this
      ->getUrl(), url('admin/config/regional/translate/translate', array(
      'absolute' => TRUE,
    )), 'Correct page redirection.');
  }

  /**
   * Get a language indicator used on translate interface page.
   *
   * @param $langcode
   *
   * @return string
   */
  public function getLanguageIndicator($langcode) {

    // This is the language indicator on the translation search screen for
    // untranslated strings. Copied straight from locale.inc.
    return "<em class=\"locale-untranslated\">{$langcode}</em> ";
  }

  /**
   * Enable URL language detection.
   */
  function enableUrlLanguageDetection($types = array(
    'language',
    'language_content',
  )) {

    // Enable URL language detection and selection.
    // In some cases language_content is not available.
    $edit = array();
    if (is_array($types) && in_array('language', $types) || is_string($types) && $types == 'language') {
      $edit['language[enabled][locale-url]'] = TRUE;
    }
    if (is_array($types) && in_array('language_content', $types) || is_string($types) && $types == 'language_content') {
      $edit['language_content[enabled][locale-interface]'] = TRUE;
    }
    $this
      ->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
    $this
      ->assertRaw(t('Language negotiation configuration saved.'), t('URL language detection enabled.'));
    $this
      ->drupalGet('admin/config/regional/language/configure');

    // Reset caches.
    drupal_static_reset('locale_url_outbound_alter');
    drupal_static_reset('language_list');
  }

  /**
   * Retrieves a Drupal path or an absolute path with language.
   *
   * @param $language
   *   Language code or language object.
   */
  function get($language, $path = '', array $options = array(), array $headers = array()) {
    $options['language'] = $this
      ->getLanguage($language);
    return $this
      ->drupalGet($path, $options, $headers);
  }

  /**
   * Posts to a Drupal path with language.
   */
  function post($language, $path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) {
    $options['language'] = $this
      ->getLanguage($language);
    $this
      ->drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post);
  }

  /**
   * Login the given user only if she has not changed.
   */
  function drupalLogin(stdClass $account) {
    if (!isset($this->current_user) || $this->current_user->uid != $account->uid) {
      $this->current_user = $account;
      parent::drupalLogin($account);
    }
  }

  /**
   * Create a "Basic page" in the specified language.
   *
   * @param $title
   *   Title of the basic page in the specified language.
   * @param $body
   *   Body of the basic page in the specified language.
   * @param $langcode
   *   The language code to be assigned to the specified values.
   */
  function createPageNode($title, $body, $langcode) {
    $edit = array();
    $edit["title"] = $title;

    // Language code on field of a new node is initially the current language.
    $edit["body[en][0][value]"] = $body;
    $edit['language'] = $langcode;
    $this
      ->drupalPost('node/add/page', $edit, t('Save'));
    $this
      ->assertRaw(t('Basic page %title has been created.', array(
      '%title' => $title,
    )), t('Basic page created.'));

    // Check to make sure the node was created.
    $node = $this
      ->drupalGetNodeByTitle($title);
    $this
      ->assertTrue($node, t('Node found in database.'));
    return $node;
  }

  /**
   * Returns a user with administration rights.
   *
   * @param $permissions
   *   Additional permissions for administrative user.
   */
  function getAdminUser(array $permissions = array()) {
    if (!isset($this->admin_user)) {
      $this->admin_user = $this
        ->drupalCreateUser(array_merge($this->admin_user_permissions, $permissions));
    }
    return $this->admin_user;
  }

  /**
   * Returns a user with minimal translation rights.
   *
   * @param $permissions
   *   Additional permissions for administrative user.
   */
  public function getTranslatorUser(array $permissions = array()) {
    if (!isset($this->translator_user)) {
      $this->translator_user = $this
        ->drupalCreateUser(array_merge($this->translator_user_permissions, $permissions));
    }
    return $this->translator_user;
  }

  /**
   * Log in as admin user.
   */
  public function loginAdminUser() {
    $this
      ->drupalLogin($this
      ->getAdminUser());
  }

  /**
   * Log in as translator user.
   */
  public function loginTranslatorUser() {
    $this
      ->drupalLogin($this
      ->getTranslatorUser());
  }

  /**
   * Move block to region, from block.test
   */
  function moveBlockToRegion($block, $region = 'sidebar_first') {

    // Set the created block to a specific region.
    $edit = array();
    $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region;
    $this
      ->drupalPost('admin/structure/block', $edit, t('Save blocks'));

    // Confirm that the block was moved to the proper region.
    $this
      ->assertText(t('The block settings have been updated.'), t('Block successfully moved to %region_name region.', array(
      '%region_name' => $region,
    )));

    // Confirm that the block is being displayed.
    $this
      ->drupalGet('node');
    $this
      ->assertText(check_plain($block['title']), 'Block successfully being displayed on the page.');

    // Confirm that the custom block was found at the proper region.
    $xpath = $this
      ->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
      ':region-class' => 'region region-' . str_replace('_', '-', $region),
      ':block-id' => 'block-' . $block['module'] . '-' . $block['delta'],
    ));
    $this
      ->assertFieldByXPath($xpath, NULL, format_string('Custom block found in %region_name region.', array(
      '%region_name' => $region,
    )));
  }

}

/**
 * Functional tests for string (interface) translation.
 */
class LanguageHierarchyStringTranslationWebTestCase extends LanguageHierarchyBaseTestCase {
  public static function getInfo() {
    return array(
      'name' => 'String translation hierarchy',
      'description' => 'Test string translation with language inheritance.',
      'group' => 'Language Hierarchy',
    );
  }
  public function setUp() {
    parent::setUp('language_hierarchy');
  }

  /**
   * Adds a language and tests string translation by users with the appropriate permissions.
   */
  public function testStringTranslation() {

    // This will be the string to be translated.
    $string = $this
      ->randomName(16);

    // This will be the translation of $string.
    $translation = $this
      ->randomName(16);
    $this
      ->loginAdminUser();

    // Add languages used by this test case.
    $this
      ->addLanguages();
    $this
      ->drupalLogout();

    // Search for the string and translate it.
    $this
      ->loginTranslatorUser();

    // Add strings to database.
    foreach ($this->languages as $language) {
      t($string, array(), array(
        'langcode' => $language['langcode'],
      ));
    }

    // Reset locale cache.
    locale_reset();
    $parent_langcode = $this->languages[0]['langcode'];
    $child_langcode = $this->languages[1]['langcode'];
    $child_langcode2 = $this->languages[2]['langcode'];

    // Translate $string for parent language.
    $this
      ->addStringTranslation($string, $translation, $parent_langcode);

    // Translate $string for one of child languages.
    $translation2 = $this
      ->randomName(16);
    $this
      ->addStringTranslation($string, $translation2, $child_langcode2);

    // Check if t() works as expected.
    $parent_translation = t($string, array(), array(
      'langcode' => $parent_langcode,
    ));
    $this
      ->assertTrue($string != $translation && $parent_translation == $translation, 't() works for parent.');

    // Check if inheritance of t() works as expected.
    $child_translation = t($string, array(), array(
      'langcode' => $child_langcode,
    ));
    $this
      ->assertTrue($string != $translation && $child_translation == $translation, 't() inherited from parent.');

    // Check if translated string for child language works as expected.
    $child_translation2 = t($string, array(), array(
      'langcode' => $child_langcode2,
    ));
    $this
      ->assertTrue($string != $translation2 && $child_translation2 == $translation2, 't() with translation for child.');
  }
  public function tearDown() {
    parent::tearDown();
    unset($this->admin_user);
    unset($this->translator_user);
    unset($this->current_user);
  }

}

/**
 * Functional tests for i18n_string (user-defined strings) translation.
 */
class LanguageHierarchyI18nStringTranslationWebTestCase extends LanguageHierarchyBaseTestCase {
  public static function getInfo() {
    return array(
      'name' => 'User-defined string translation hierarchy',
      'description' => 'Test i18n_string translation with language inheritance.',
      'group' => 'Language Hierarchy',
    );
  }
  public function setUp() {

    // We can use any of the modules that define a text group, to use it for testing
    parent::setUp('language_hierarchy', 'i18n_menu');
    $this->translator_user_permissions[] = 'translate user-defined strings';
  }

  /**
   * Adds a language and tests i18n_string translation by users with the appropriate permissions.
   */
  public function testI18nStringTranslation() {

    // This will be the string to be translated.
    $string = $this
      ->randomName(16);

    // This will be the translation of $string.
    $translation = $this
      ->randomName(16);
    $this
      ->loginAdminUser();

    // Add languages used by this test case.
    $this
      ->addLanguages();
    $this
      ->drupalLogout();

    // Search for the string and translate it.
    $this
      ->loginTranslatorUser();
    $textgroup = 'menu';

    // Save source string and store translation.
    $name = "{$textgroup}:item:1:title";
    i18n_string_update($name, $string);

    // Reset cache for text group
    i18n_string_textgroup($textgroup)
      ->cache_reset();
    $parent_langcode = $this->languages[0]['langcode'];
    $child_langcode = $this->languages[1]['langcode'];
    $child_langcode2 = $this->languages[2]['langcode'];
    $translation2 = $this
      ->randomName(16);
    $translations = array(
      // Translate $string for parent language.
      $parent_langcode => $translation,
      // Translate $string for one of child languages.
      $child_langcode2 => $translation2,
    );
    $this
      ->addI18nStringTranslation($textgroup, $string, $translations);

    // Check if i18n_string() works as expected: a translation should be found
    // for the parent language.
    $parent_translation = i18n_string_translate($name, 'NOT FOUND', array(
      'langcode' => $parent_langcode,
    ));
    $this
      ->assertTrue($string != $translation && $parent_translation == $translation, 'i18n_string() works for parent.');

    // Check if inheritance of i18n_string() works as expected: no specific
    // translation for this child language, so translation in parent language
    // returned.
    $child_translation = i18n_string_translate($name, 'NOT FOUND', array(
      'langcode' => $child_langcode,
    ));
    $this
      ->assertTrue($string != $translation && $child_translation == $translation, 'i18n_string() inherited from parent.');

    // Check if translated string for child language works as expected: a
    // specific translation for this child language should be found.
    $child_translation2 = i18n_string_translate($name, 'NOT FOUND', array(
      'langcode' => $child_langcode2,
    ));
    $this
      ->assertTrue($string != $translation2 && $child_translation2 == $translation2, 'i18n_string() with translation for child.');
  }

  /**
   * Create translation for string in textgroup
   *
   * Adapted from Drupali18nTestCase::createStringTranslation().
   *
   * @param $translations
   *   Optional array of langcode => translation. If not present, it will be generated.
   */
  function addI18nStringTranslation($textgroup, $name, $translations) {

    // This is the language indicator on the translation search screen for
    // untranslated strings. Copied straight from locale.inc.
    $language_indicator = "<em class=\"locale-untranslated\">";

    // Search for the name and translate it.
    $search = array(
      'string' => $name,
      'language' => 'all',
      'translation' => 'all',
      'group' => $textgroup,
    );
    $this
      ->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));

    // assertText() seems to remove the input field where $name always could be
    // found, so this is not a false assert. See how assertNoText succeeds
    // later.
    $this
      ->assertText(check_plain($name), t('Search found the name.'));
    $this
      ->assertRaw($language_indicator, t('Name is untranslated.'));

    // Assume this is the only result, given the random name.
    $this
      ->clickLink(t('edit'));

    // We save the lid from the path.
    $matches = array();
    preg_match('!admin/config/regional/translate/edit/(\\d+)!', $this
      ->getUrl(), $matches);
    $lid = $matches[1];

    // No t() here, it's surely not translated yet.
    $this
      ->assertText(check_plain($name), t('name found on edit screen.'));
    foreach ($translations as $langcode => $translation) {
      $edit["translations[{$langcode}]"] = $translation;
    }
    $this
      ->drupalPost(NULL, $edit, t('Save translations'));
    $this
      ->assertText(t('The string has been saved.'), t('The string has been saved.'));
    $this
      ->assertEqual($this
      ->getUrl(), url('admin/config/regional/translate/translate', array(
      'absolute' => TRUE,
    )), t('Correct page redirection.'));
    $this
      ->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));

    // The indicator should not be here.
    foreach ($translations as $langcode => $translation) {
      $this
        ->assertNoRaw($language_indicator . $langcode . '</em>', t('String is translated.'));
    }
    return $translations;
  }

}

Classes

Namesort descending Description
LanguageHierarchyBaseTestCase Base class for Language Hierarchy module tests.
LanguageHierarchyI18nStringTranslationWebTestCase Functional tests for i18n_string (user-defined strings) translation.
LanguageHierarchyStringTranslationWebTestCase Functional tests for string (interface) translation.