Newer
Older
<?php
Dries Buytaert
committed
/**
* @file
* Tests for translation.module
*/
class TranslationTestCase extends DrupalWebTestCase {
protected $book;
Angie Byron
committed
public static function getInfo() {
return array(
'name' => 'Translation functionality',
Angie Byron
committed
'description' => 'Create a basic page with translation, modify the page outdating translation, and update translation.',
'group' => 'Translation'
);
}
function setUp() {
Angie Byron
committed
parent::setUp('locale', 'translation', 'translation_test');
// Setup users.
Dries Buytaert
committed
$this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages', 'translate content'));
Angie Byron
committed
$this->translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate content'));
Angie Byron
committed
$this->drupalLogin($this->admin_user);
// Add languages.
$this->addLanguage('en');
$this->addLanguage('es');
Angie Byron
committed
$this->addLanguage('it');
Angie Byron
committed
// Disable Italian to test the translation behavior with disabled languages.
Dries Buytaert
committed
$edit = array('languages[it][enabled]' => FALSE);
Angie Byron
committed
$this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
Angie Byron
committed
Angie Byron
committed
// Set "Basic page" content type to use multilingual support with
// translation.
Dries Buytaert
committed
$this->drupalGet('admin/structure/types/manage/page');
$edit = array();
$edit['language_content_type'] = 2;
Dries Buytaert
committed
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
Angie Byron
committed
// Enable the language switcher block.
$language_type = LANGUAGE_TYPE_INTERFACE;
$edit = array("blocks[locale_$language_type][region]" => 'sidebar_first');
$this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
// Enable URL language detection and selection to make the language switcher
// block appear.
$edit = array('language[enabled][locale-url]' => 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->resetCaches();
Angie Byron
committed
$this->drupalLogin($this->translator);
}
Angie Byron
committed
/**
* Create a basic page with translation, modify the basic page outdating
* translation, and update translation.
*/
function testContentTranslation() {
Angie Byron
committed
// Create Basic page in English.
$node_title = $this->randomName();
$node_body = $this->randomName();
$node = $this->createPage($node_title, $node_body, 'en');
Dries Buytaert
committed
// Unpublish the original node to check that this has no impact on the
// translation overview page, publish it again afterwards.
$this->drupalLogin($this->admin_user);
$this->drupalPost('node/' . $node->nid . '/edit', array('status' => FALSE), t('Save'));
$this->drupalGet('node/' . $node->nid . '/translate');
$this->drupalPost('node/' . $node->nid . '/edit', array('status' => NODE_PUBLISHED), t('Save'));
$this->drupalLogin($this->translator);
Angie Byron
committed
// Check that the "add translation" link uses a localized path.
Angie Byron
committed
$languages = language_list();
$this->drupalGet('node/' . $node->nid . '/translate');
$this->assertLinkByHref($languages['es']->prefix . '/node/add/' . str_replace('_', '-', $node->type), 0, t('The "add translation" link for %language points to the localized path of the target language.', array('%language' => $languages['es']->name)));
// Submit translation in Spanish.
$node_translation_title = $this->randomName();
$node_translation_body = $this->randomName();
Angie Byron
committed
$node_translation = $this->createTranslation($node, $node_translation_title, $node_translation_body, 'es');
Angie Byron
committed
// Check that the "edit translation" and "view node" links use localized
// paths.
Angie Byron
committed
$this->drupalGet('node/' . $node->nid . '/translate');
$this->assertLinkByHref($languages['es']->prefix . '/node/' . $node_translation->nid . '/edit', 0, t('The "edit" link for the translation in %language points to the localized path of the translation language.', array('%language' => $languages['es']->name)));
$this->assertLinkByHref($languages['es']->prefix . '/node/' . $node_translation->nid, 0, t('The "view" link for the translation in %language points to the localized path of the translation language.', array('%language' => $languages['es']->name)));
Angie Byron
committed
// Attempt to submit a duplicate translation by visiting the node/add page
// with identical query string.
Dries Buytaert
committed
$this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => 'es')));
$this->assertRaw(t('A translation of %title in %language already exists', array('%title' => $node_title, '%language' => $languages['es']->name)), t('Message regarding attempted duplicate translation is displayed.'));
Angie Byron
committed
// Attempt a resubmission of the form - this emulates using the back button
// to return to the page then resubmitting the form without a refresh.
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $this->randomName();
Angie Byron
committed
$edit["body[$langcode][0][value]"] = $this->randomName();
Angie Byron
committed
$this->drupalPost('node/add/page', $edit, t('Save'), array('query' => array('translation' => $node->nid, 'language' => 'es')));
$duplicate = $this->drupalGetNodeByTitle($edit["title"]);
$this->assertEqual($duplicate->tnid, 0, t('The node does not have a tnid.'));
Angie Byron
committed
// Update original and mark translation as outdated.
Angie Byron
committed
$node_body = $this->randomName();
Angie Byron
committed
$node->body[LANGUAGE_NONE][0]['value'] = $node_body;
$edit = array();
Angie Byron
committed
$edit["body[$langcode][0][value]"] = $node_body;
$edit['translation[retranslate]'] = TRUE;
$this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been updated.', array('%title' => $node_title)), t('Original node updated.'));
Angie Byron
committed
// Check to make sure that interface shows translation as outdated.
$this->drupalGet('node/' . $node->nid . '/translate');
$this->assertRaw('<span class="marker">' . t('outdated') . '</span>', t('Translation marked as outdated.'));
// Update translation and mark as updated.
$edit = array();
Angie Byron
committed
$edit["body[$langcode][0][value]"] = $this->randomName();
$edit['translation[status]'] = FALSE;
$this->drupalPost('node/' . $node_translation->nid . '/edit', $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been updated.', array('%title' => $node_translation_title)), t('Translated node updated.'));
Angie Byron
committed
// Confirm that disabled languages are an option for translators when
// creating nodes.
$this->drupalGet('node/add/page');
Angie Byron
committed
$this->assertFieldByXPath('//select[@name="language"]//option', 'it', t('Italian (disabled) is available in language selection.'));
$translation_it = $this->createTranslation($node, $this->randomName(), $this->randomName(), 'it');
Angie Byron
committed
$this->assertRaw($translation_it->body[LANGUAGE_NONE][0]['value'], t('Content created in Italian (disabled).'));
Dries Buytaert
committed
// Confirm that language neutral is an option for translators when there are
// disabled languages.
$this->drupalGet('node/add/page');
$this->assertFieldByXPath('//select[@name="language"]//option', LANGUAGE_NONE, t('Language neutral is available in language selection with disabled languages.'));
$node2 = $this->createPage($this->randomName(), $this->randomName(), LANGUAGE_NONE);
$this->assertRaw($node2->body[LANGUAGE_NONE][0]['value'], t('Language neutral content created with disabled languages available.'));
// Leave just one language enabled and check that the translation overview
// page is still accessible.
$this->drupalLogin($this->admin_user);
Dries Buytaert
committed
$edit = array('languages[es][enabled]' => FALSE);
$this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
$this->drupalLogin($this->translator);
$this->drupalGet('node/' . $node->nid . '/translate');
$this->assertRaw(t('Translations of %title', array('%title' => $node->title)), t('Translation overview page available with only one language enabled.'));
}
Angie Byron
committed
/**
Angie Byron
committed
* Check that language switch links behave properly.
Angie Byron
committed
*/
Angie Byron
committed
function testLanguageSwitchLinks() {
// Create a Basic page in English and its translations in Spanish and
// Italian.
$node = $this->createPage($this->randomName(), $this->randomName(), 'en');
$translation_es = $this->createTranslation($node, $this->randomName(), $this->randomName(), 'es');
$translation_it = $this->createTranslation($node, $this->randomName(), $this->randomName(), 'it');
// Check that language switch links are correctly shown only for enabled
// languages.
$this->assertLanguageSwitchLinks($node, $translation_es);
$this->assertLanguageSwitchLinks($translation_es, $node);
$this->assertLanguageSwitchLinks($node, $translation_it, FALSE);
// Check that links to the displayed translation appear only in the language
// switcher block.
$this->assertLanguageSwitchLinks($node, $node, FALSE, 'node');
$this->assertLanguageSwitchLinks($node, $node, TRUE, 'block-locale');
// Unpublish the Spanish translation to check that the related language
// switch link is not shown.
$this->drupalLogin($this->admin_user);
$edit = array('status' => FALSE);
$this->drupalPost("node/$translation_es->nid/edit", $edit, t('Save'));
$this->drupalLogin($this->translator);
$this->assertLanguageSwitchLinks($node, $translation_es, FALSE);
Angie Byron
committed
// Check that content translation links are shown even when no language
// negotiation is configured.
Angie Byron
committed
$this->drupalLogin($this->admin_user);
$edit = array('language[enabled][locale-url]' => FALSE);
$this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
Angie Byron
committed
$this->resetCaches();
$edit = array('status' => TRUE);
$this->drupalPost("node/$translation_es->nid/edit", $edit, t('Save'));
Angie Byron
committed
$this->drupalLogin($this->translator);
Angie Byron
committed
$this->assertLanguageSwitchLinks($node, $translation_es, TRUE, 'node');
}
Angie Byron
committed
Angie Byron
committed
/**
* Test that the language switcher block alterations work as intended.
*/
function testLanguageSwitcherBlockIntegration() {
// Enable Italian to have three items in the language switcher block.
$this->drupalLogin($this->admin_user);
Dries Buytaert
committed
$edit = array('languages[it][enabled]' => TRUE);
Angie Byron
committed
$this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
$this->drupalLogin($this->translator);
// Create a Basic page in English.
$type = 'block-locale';
$node = $this->createPage($this->randomName(), $this->randomName(), 'en');
$this->assertLanguageSwitchLinks($node, $node, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $this->emptyNode('es'), TRUE, $type);
$this->assertLanguageSwitchLinks($node, $this->emptyNode('it'), TRUE, $type);
// Create the Spanish translation.
$translation_es = $this->createTranslation($node, $this->randomName(), $this->randomName(), 'es');
$this->assertLanguageSwitchLinks($node, $node, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $translation_es, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $this->emptyNode('it'), TRUE, $type);
// Create the Italian translation.
$translation_it = $this->createTranslation($node, $this->randomName(), $this->randomName(), 'it');
$this->assertLanguageSwitchLinks($node, $node, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $translation_es, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $translation_it, TRUE, $type);
Dries Buytaert
committed
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Create a language neutral node and check that the language switcher is
// left untouched.
$node2 = $this->createPage($this->randomName(), $this->randomName(), LANGUAGE_NONE);
$node2_en = (object) array('nid' => $node2->nid, 'language' => 'en');
$node2_es = (object) array('nid' => $node2->nid, 'language' => 'es');
$node2_it = (object) array('nid' => $node2->nid, 'language' => 'it');
$this->assertLanguageSwitchLinks($node2_en, $node2_en, TRUE, $type);
$this->assertLanguageSwitchLinks($node2_en, $node2_es, TRUE, $type);
$this->assertLanguageSwitchLinks($node2_en, $node2_it, TRUE, $type);
// Disable translation support to check that the language switcher is left
// untouched only for new nodes.
$this->drupalLogin($this->admin_user);
$edit = array('language_content_type' => 0);
$this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->drupalLogin($this->translator);
// Existing translations trigger alterations even if translation support is
// disabled.
$this->assertLanguageSwitchLinks($node, $node, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $translation_es, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $translation_it, TRUE, $type);
// Check that new nodes with a language assigned do not trigger language
// switcher alterations when translation support is disabled.
$node = $this->createPage($this->randomName(), $this->randomName());
$node_es = (object) array('nid' => $node->nid, 'language' => 'es');
$node_it = (object) array('nid' => $node->nid, 'language' => 'it');
$this->assertLanguageSwitchLinks($node, $node, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $node_es, TRUE, $type);
$this->assertLanguageSwitchLinks($node, $node_it, TRUE, $type);
Angie Byron
committed
}
/**
* Reset static caches to make the test code match the client site behavior.
*/
function resetCaches() {
drupal_static_reset('locale_url_outbound_alter');
}
/**
* Return an empty node data structure.
*/
function emptyNode($langcode) {
return (object) array('nid' => NULL, 'language' => $langcode);
Angie Byron
committed
}
/**
* Install a the specified language if it has not been already. Otherwise make sure that
* the language is enabled.
*
Angie Byron
committed
* @param $language_code
* The language code the check.
*/
function addLanguage($language_code) {
// Check to make sure that language has not already been installed.
Dries Buytaert
committed
$this->drupalGet('admin/config/regional/language');
Dries Buytaert
committed
if (strpos($this->drupalGetContent(), 'languages[' . $language_code . '][enabled]') === FALSE) {
// Doesn't have language installed so add it.
$edit = array();
$edit['predefined_langcode'] = $language_code;
Dries Buytaert
committed
$this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
Angie Byron
committed
// Make sure we are not using a stale list.
Dries Buytaert
committed
drupal_static_reset('language_list');
$languages = language_list('language');
$this->assertTrue(array_key_exists($language_code, $languages), t('Language was installed successfully.'));
if (array_key_exists($language_code, $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[$language_code]->name, '@locale-help' => url('admin/help/locale'))), t('Language has been created.'));
}
}
Dries Buytaert
committed
elseif ($this->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(':name' => 'languages[' . $language_code . '][enabled]'))) {
Dries Buytaert
committed
// It's installed and enabled. No need to do anything.
$this->assertTrue(true, 'Language [' . $language_code . '] already installed and enabled.');
}
else {
Dries Buytaert
committed
// It's installed but not enabled. Enable it.
$this->assertTrue(true, 'Language [' . $language_code . '] already installed.');
Dries Buytaert
committed
$this->drupalPost(NULL, array('languages[' . $language_code . '][enabled]' => TRUE), t('Save configuration'));
$this->assertRaw(t('Configuration saved.'), t('Language successfully enabled.'));
}
}
/**
Angie Byron
committed
* Create a "Basic page" in the specified language.
*
Angie Byron
committed
* @param $title
* Title of basic page in specified language.
* @param $body
* Body of basic page in specified language.
* @param
* $language Language code.
*/
Dries Buytaert
committed
function createPage($title, $body, $language = NULL) {
$edit = array();
$langcode = LANGUAGE_NONE;
$edit["title"] = $title;
Angie Byron
committed
$edit["body[$langcode][0][value]"] = $body;
Dries Buytaert
committed
if (!empty($language)) {
$edit['language'] = $language;
}
$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.
Angie Byron
committed
$node = $this->drupalGetNodeByTitle($title);
$this->assertTrue($node, t('Node found in database.'));
return $node;
}
Angie Byron
committed
/**
* Create a translation for the specified basic page in the specified
* language.
*
* @param $node
* The basic page to create translation for.
* @param $title
* Title of basic page in specified language.
* @param $body
* Body of basic page in specified language.
* @param $language
* Language code.
*/
function createTranslation($node, $title, $body, $language) {
$this->drupalGet('node/add/page', array('query' => array('translation' => $node->nid, 'target' => $language)));
Angie Byron
committed
$langcode = LANGUAGE_NONE;
$body_key = "body[$langcode][0][value]";
Angie Byron
committed
$this->assertFieldByXPath('//input[@id="edit-title"]', $node->title, "Original title value correctly populated.");
Angie Byron
committed
$this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[LANGUAGE_NONE][0]['value'], "Original body value correctly populated.");
Angie Byron
committed
$edit = array();
$edit["title"] = $title;
$edit[$body_key] = $body;
$this->drupalPost(NULL, $edit, t('Save'));
$this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Translation created.'));
// Check to make sure that translation was successful.
$translation = $this->drupalGetNodeByTitle($title);
$this->assertTrue($translation, t('Node found in database.'));
$this->assertTrue($translation->tnid == $node->nid, t('Translation set id correctly stored.'));
return $translation;
}
Angie Byron
committed
/**
* Assert that an element identified by the given XPath has the given content.
*
* @param $xpath
* XPath used to find the element.
* @param array $arguments
* An array of arguments with keys in the form ':name' matching the
* placeholders in the query. The values may be either strings or numeric
* values.
* @param $value
* The text content of the matched element to assert.
* @param $message
* Message to display.
* @param $group
* The group this message belongs to.
*
* @return
* TRUE on pass, FALSE on fail.
*/
function assertContentByXPath($xpath, array $arguments = array(), $value = NULL, $message = '', $group = 'Other') {
Angie Byron
committed
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
$found = $this->findContentByXPath($xpath, $arguments, $value);
return $this->assertTrue($found, $message, $group);
}
/**
* Check that the specified language switch links are found/not found.
*
* @param $node
* The node to display.
* @param $translation
* The translation whose link has to be checked.
* @param $find
* TRUE if the link must be present in the node page.
* @param $types
* The page areas to be checked.
*
* @return
* TRUE if the language switch links are found/not found.
*/
function assertLanguageSwitchLinks($node, $translation, $find = TRUE, $types = NULL) {
if (empty($types)) {
$types = array('node', 'block-locale');
}
elseif (is_string($types)) {
$types = array($types);
}
$result = TRUE;
$languages = language_list();
$page_language = $languages[$node->language];
$translation_language = $languages[$translation->language];
$url = url("node/$translation->nid", array('language' => $translation_language));
$this->drupalGet("node/$node->nid", array('language' => $page_language));
foreach ($types as $type) {
catch
committed
$args = array('%translation_language' => $translation_language->name, '%page_language' => $page_language->name, '%type' => $type);
Angie Byron
committed
if ($find) {
$message = t('[%page_language] Language switch item found for %translation_language language in the %type page area.', $args);
}
else {
$message = t('[%page_language] Language switch item not found for %translation_language language in the %type page area.', $args);
}
if (!empty($translation->nid)) {
$xpath = '//div[contains(@class, :type)]//a[@href=:url]';
}
else {
$xpath = '//div[contains(@class, :type)]//span[@class="locale-untranslated"]';
}
catch
committed
$found = $this->findContentByXPath($xpath, array(':type' => $type, ':url' => $url), $translation_language->name);
Angie Byron
committed
$result = $this->assertTrue($found == $find, $message) && $result;
}
return $result;
}
/**
* Search for elements matching the given xpath and value.
*/
function findContentByXPath($xpath, array $arguments = array(), $value = NULL) {
Angie Byron
committed
$elements = $this->xpath($xpath, $arguments);
$found = TRUE;
if ($value && $elements) {
$found = FALSE;
foreach ($elements as $element) {
if ((string) $element == $value) {
$found = TRUE;
break;
}
}
}
Angie Byron
committed
return $elements && $found;