entity_translation.test 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. <?php
  2. /**
  3. * @file
  4. * Tests for Entity translation module.
  5. */
  6. /**
  7. * Base class for entity translation module tests.
  8. */
  9. class EntityTranslationTestCase extends DrupalWebTestCase {
  10. protected $current_user;
  11. protected $admin_user;
  12. protected $translator_user;
  13. function setUp() {
  14. $args = func_get_args();
  15. call_user_func_array(array('parent', 'setUp'), $args);
  16. // Reset user fields to make test object reusable.
  17. unset($this->current_user);
  18. unset($this->admin_user);
  19. unset($this->translator_user);
  20. }
  21. /**
  22. * Retrieves a Drupal path or an absolute path with language.
  23. *
  24. * @param $language
  25. * Language code or language object.
  26. */
  27. function get($language, $path = '', array $options = array(), array $headers = array()) {
  28. $options['language'] = $this->getLanguage($language);
  29. return $this->drupalGet($path, $options, $headers);
  30. }
  31. /**
  32. * Posts to a Drupal path with language.
  33. */
  34. function post($language, $path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) {
  35. $options['language'] = $this->getLanguage($language);
  36. $this->drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post);
  37. }
  38. /**
  39. * Login the given user only if she has not changed.
  40. */
  41. function login($user) {
  42. if (!isset($this->current_user) || $this->current_user->uid != $user->uid) {
  43. $this->current_user = $user;
  44. $this->drupalLogin($user);
  45. }
  46. }
  47. /**
  48. * Returns a user with administration rights.
  49. *
  50. * @param $permissions
  51. * Additional permissions for administrative user.
  52. */
  53. function getAdminUser(array $permissions = array()) {
  54. if (!isset($this->admin_user)) {
  55. $this->admin_user = $this->drupalCreateUser(array_merge(array(
  56. 'bypass node access',
  57. 'administer nodes',
  58. 'administer fields',
  59. 'administer languages',
  60. 'administer content types',
  61. 'administer blocks',
  62. 'access administration pages',
  63. 'administer site configuration',
  64. ), $permissions));
  65. }
  66. return $this->admin_user;
  67. }
  68. /**
  69. * Returns a user with minimal translation rights.
  70. *
  71. * @param $permissions
  72. * Additional permissions for administrative user.
  73. */
  74. function getTranslatorUser(array $permissions = array()) {
  75. if (!isset($this->translator_user)) {
  76. $this->translator_user = $this->drupalCreateUser(array_merge(array(
  77. 'create page content',
  78. 'edit own page content',
  79. 'delete own page content',
  80. 'translate any entity',
  81. ), $permissions));
  82. }
  83. return $this->translator_user;
  84. }
  85. /**
  86. * Make sure the clean urls are enabled.
  87. */
  88. function enableCleanUrls() {
  89. $this->drupalGet('admin/config/search/clean-urls');
  90. $edit = array();
  91. $edit['clean_url'] = TRUE;
  92. $this->drupalPost(NULL, $edit, t('Save configuration'));
  93. }
  94. /**
  95. * Enable URL language detection.
  96. */
  97. function enableUrlLanguageDetection() {
  98. // Enable URL language detection and selection.
  99. $edit = array(
  100. 'language[enabled][locale-url]' => TRUE,
  101. 'language_content[enabled][locale-interface]' => TRUE
  102. );
  103. $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
  104. $this->assertRaw(t('Language negotiation configuration saved.'), t('URL language detection enabled.'));
  105. $this->drupalGet('admin/config/regional/language/configure');
  106. // Reset caches.
  107. drupal_static_reset('locale_url_outbound_alter');
  108. drupal_static_reset('language_list');
  109. }
  110. /**
  111. * Get a language object from a language code.
  112. */
  113. public function getLanguage($langcode) {
  114. if (is_object($langcode)) {
  115. return $langcode;
  116. }
  117. else {
  118. $language_list = language_list();
  119. return $language_list[$langcode];
  120. }
  121. }
  122. /**
  123. * Install a specified language if it has not been already, otherwise make sure that the language is enabled.
  124. *
  125. * @param $langcode
  126. * The language code to check.
  127. */
  128. function addLanguage($langcode) {
  129. // Check to make sure that language has not already been installed.
  130. $this->drupalGet('admin/config/regional/language');
  131. if (strpos($this->drupalGetContent(), 'enabled[' . $langcode . ']') === FALSE) {
  132. // Doesn't have language installed so add it.
  133. $edit = array();
  134. $edit['langcode'] = $langcode;
  135. $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
  136. // Make sure we are not using a stale list.
  137. drupal_static_reset('language_list');
  138. $languages = language_list('language');
  139. $this->assertTrue(array_key_exists($langcode, $languages), t('Language was installed successfully.'));
  140. if (array_key_exists($langcode, $languages)) {
  141. $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.'));
  142. }
  143. }
  144. elseif ($this->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(':name' => 'enabled[' . $langcode . ']'))) {
  145. // It is installed and enabled. No need to do anything.
  146. $this->assertTrue(TRUE, 'Language [' . $langcode . '] already installed and enabled.');
  147. }
  148. else {
  149. // It is installed but not enabled. Enable it.
  150. $this->assertTrue(TRUE, 'Language [' . $langcode . '] already installed.');
  151. $this->drupalPost(NULL, array('enabled[' . $langcode . ']' => TRUE), t('Save configuration'));
  152. $this->assertRaw(t('Configuration saved.'), t('Language successfully enabled.'));
  153. }
  154. }
  155. /**
  156. * Configure the "Basic page" content type for entity translation tests.
  157. */
  158. function configureContentType() {
  159. // Configure the "Basic page" content type to use multilingual support with
  160. // translation.
  161. $edit = array();
  162. $edit['language_content_type'] = ENTITY_TRANSLATION_ENABLED;
  163. $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
  164. $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
  165. // Set body field's cardinality to unlimited and toggle translatability.
  166. $edit = array();
  167. $edit['field[cardinality]'] = FIELD_CARDINALITY_UNLIMITED;
  168. $edit['field[translatable]'] = 1;
  169. $this->drupalPost('admin/structure/types/manage/page/fields/body', $edit, t('Save settings'));
  170. $this->assertRaw(t('Saved %field configuration.', array('%field' => 'Body')), t('Body field settings have been updated.'));
  171. // Check if the setting works.
  172. $this->drupalGet('node/add/page');
  173. $this->assertFieldById('edit-body-und-add-more', t('Add another item'), t('Add another item button found.'));
  174. }
  175. /**
  176. * Create a "Basic page" in the specified language.
  177. *
  178. * @param $title
  179. * Title of the basic page in the specified language.
  180. * @param $body
  181. * Body of the basic page in the specified language.
  182. * @param $langcode
  183. * The language code to be assigned to the specified values.
  184. */
  185. function createPage($title, $body, $langcode) {
  186. $edit = array();
  187. $language_none = LANGUAGE_NONE;
  188. $edit["title"] = $title;
  189. $edit["body[$language_none][0][value]"] = $body;
  190. $edit['language'] = $langcode;
  191. $this->drupalPost('node/add/page', $edit, t('Save'));
  192. $this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Basic page created.'));
  193. // Check to make sure the node was created.
  194. $node = $this->drupalGetNodeByTitle($title);
  195. $this->assertTrue($node, t('Node found in database.'));
  196. return $node;
  197. }
  198. /**
  199. * Create a translation.
  200. *
  201. * @param $node
  202. * Node of the basic page to create translation for.
  203. * @param $title
  204. * Title of the basic page in the specified language.
  205. * @param $body
  206. * Body of the basic page in the specified language.
  207. * @param $langcode
  208. * The language code to be assigned to the specified values.
  209. */
  210. function createTranslation($node, $title, $body, $langcode, $source_langcode = 'en') {
  211. $this->drupalGet('node/' . $node->nid . '/edit/add/' . $source_langcode . '/' . $langcode);
  212. $body_key = "body[$langcode][0][value]";
  213. $this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[$source_langcode][0]['value'], 'Original body value correctly populated.');
  214. $this->assertFieldById('edit-body-' . $langcode . '-add-more', t('Add another item'), t('Add another item button found.'));
  215. $edit = array();
  216. $edit[$body_key] = $body;
  217. $this->drupalPost(NULL, $edit, t('Save'));
  218. $this->drupalGet('node/' . $node->nid . '/translate');
  219. $this->assertLinkByHref('node/' . $node->nid . '/edit/' . $langcode, 0, t('Translation edit link found. Translation created.'));
  220. return $node;
  221. }
  222. }
  223. /**
  224. * Basic tests for the translation creation/editing workflow.
  225. */
  226. class EntityTranslationTranslationTestCase extends EntityTranslationTestCase {
  227. /**
  228. * Return the test information.
  229. */
  230. public static function getInfo() {
  231. return array(
  232. 'name' => 'Entity translation workflow',
  233. 'description' => 'Basic tests for the translation creation/editing workflow.',
  234. 'group' => 'Entity translation',
  235. );
  236. }
  237. function setUp() {
  238. parent::setUp('locale', 'entity_translation');
  239. $this->login($this->getAdminUser());
  240. $this->addLanguage('en');
  241. $this->addLanguage('es');
  242. $this->configureContentType();
  243. $this->login($this->getTranslatorUser());
  244. }
  245. /**
  246. * Test if field based translation works.
  247. *
  248. * Enable field based translation for basic pages. Add a field with a
  249. * cardinality higher than 1, to test if field_default_extract_form_values()
  250. * is invoked. Create a basic page and translate it.
  251. */
  252. function testFieldTranslation() {
  253. // Create Basic page in English.
  254. $node_title = $this->randomName();
  255. $node_body = $this->randomName();
  256. $node = $this->createPage($node_title, $node_body, 'en');
  257. // Submit translation in Spanish.
  258. $node_translation_title = $this->randomName();
  259. $node_translation_body = $this->randomName();
  260. $node_translation = $this->createTranslation($node, $node_translation_title, $node_translation_body, 'es');
  261. }
  262. }
  263. /**
  264. * Basic tests for comment related things.
  265. *
  266. * @todo Add tests for comment translation workflow.
  267. */
  268. class EntityTranslationCommentTestCase extends EntityTranslationTestCase {
  269. protected $comment_user;
  270. /**
  271. * Return the test information.
  272. */
  273. public static function getInfo() {
  274. return array(
  275. 'name' => 'Comment translation',
  276. 'description' => 'Basic tests for comment translation/filtering.',
  277. 'group' => 'Entity translation',
  278. );
  279. }
  280. function setUp() {
  281. parent::setUp('locale', 'entity_translation', 'comment');
  282. $this->login($this->getAdminUser());
  283. $this->addLanguage('en');
  284. $this->addLanguage('es');
  285. $this->enableUrlLanguageDetection();
  286. $this->configureContentType();
  287. $this->configureComments(FALSE);
  288. $this->login($this->getTranslatorUser());
  289. }
  290. function tearDown() {
  291. unset($this->comment_user);
  292. parent::tearDown();
  293. }
  294. function getCommentUser() {
  295. if (empty($this->comment_user)) {
  296. $this->comment_user = $this->drupalCreateUser(array(
  297. 'access comments',
  298. 'post comments',
  299. 'edit own comments',
  300. ));
  301. }
  302. return $this->comment_user;
  303. }
  304. /**
  305. * Enable comments and comment filtering by language.
  306. */
  307. function configureComments($filter_by_language = TRUE) {
  308. $edit = array();
  309. $edit['comment'] = COMMENT_NODE_OPEN;
  310. $edit['entity_translation_comment_filter'] = $filter_by_language;
  311. $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
  312. $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')));
  313. $this->drupalGet('admin/structure/types/manage/page');
  314. if ($filter_by_language) {
  315. $this->assertFieldChecked('edit-entity-translation-comment-filter', 'Comment filtering is enabled.');
  316. }
  317. else {
  318. $this->assertNoFieldChecked('edit-entity-translation-comment-filter', 'Comment filtering is disabled.');
  319. }
  320. }
  321. /**
  322. * Add a comment for the given node.
  323. *
  324. * @param $node
  325. * The node for which to add the comment.
  326. * @param $comment_body
  327. * The comment body text.
  328. * @param $language
  329. * The comment language.
  330. */
  331. function postComment($node, $comment_body, $language) {
  332. $edit = array();
  333. $edit['comment_body[' . LANGUAGE_NONE . '][0][value]'] = $comment_body;
  334. $this->post($language, 'comment/reply/' . $node->nid, $edit, t('Save'));
  335. }
  336. /**
  337. * Test comment filtering by language.
  338. */
  339. function testCommentLanguageFiltering() {
  340. $node = $this->createPage($this->randomName(), $this->randomName(), 'en');
  341. $this->login($this->getCommentUser());
  342. // Create comments in different languages.
  343. $comment_en = $this->randomName();
  344. $this->postComment($node, $comment_en, 'en');
  345. $comment_es = $this->randomName();
  346. $this->postComment($node, $comment_es, 'es');
  347. // Check that ALL comments are being displayed when comment language filter
  348. // is disabled (default behavior).
  349. $this->get('en', 'node/' . $node->nid);
  350. $this->assertText($comment_en, 'English comment found.');
  351. $this->assertText($comment_es, 'Spanish comment found.');
  352. // Enable comment filtering by language.
  353. $this->login($this->getAdminUser());
  354. $this->configureComments(TRUE);
  355. $this->login($this->getCommentUser());
  356. // Load page in different languages. Check that only comments matching
  357. // current language are being displayed.
  358. $this->get('en', 'node/' . $node->nid);
  359. $this->assertText($comment_en, 'English comment found.');
  360. $this->assertNoText($comment_es, 'Spanish comment not found.');
  361. $this->get('es', 'node/' . $node->nid);
  362. $this->assertNoText($comment_en, 'English comment not found.');
  363. $this->assertText($comment_es, 'Spanish comment found.');
  364. }
  365. }
  366. /**
  367. * Test CRUD hook invocation.
  368. */
  369. class EntityTranslationHookTestCase extends EntityTranslationTestCase {
  370. /**
  371. * Return the test information.
  372. */
  373. public static function getInfo() {
  374. return array(
  375. 'name' => 'Entity translation hooks',
  376. 'description' => 'Test that entity translation hooks are properly fired.',
  377. 'group' => 'Entity translation',
  378. );
  379. }
  380. function setUp() {
  381. parent::setUp('locale', 'entity_translation', 'entity_translation_test');
  382. $this->login($this->getAdminUser());
  383. $this->addLanguage('it');
  384. $this->addLanguage('es');
  385. $this->configureContentType();
  386. $this->login($this->getTranslatorUser());
  387. }
  388. /**
  389. * Test whether hooks are properly fired in the regular form workflow.
  390. */
  391. function testFormWorkflow() {
  392. // Create Basic page in English.
  393. $node_title = $this->randomName();
  394. $node_body = $this->randomName();
  395. $node = $this->createPage($node_title, $node_body, 'en');
  396. // Submit translation in Italian.
  397. $node_translation_body = $this->randomName();
  398. $this->createTranslation($node, NULL, $node_translation_body, 'it');
  399. $info = $this->getHookInfo();
  400. $this->assertTrue(!empty($info['insert']), t('Insert hook has been properly fired.'));
  401. // Edit translation in Italian.
  402. $edit = array("body[it][0][value]" => $this->randomName());
  403. $this->drupalPost('node/' . $node->nid . '/edit/it', $edit, t('Save'));
  404. $info = $this->getHookInfo();
  405. $this->assertTrue(!empty($info['update']), t('Update hook has been properly fired.'));
  406. // Delete the Basic page.
  407. $edit = array();
  408. $this->drupalPost('node/' . $node->nid . '/delete', $edit, t('Delete'));
  409. $info = $this->getHookInfo('delete');
  410. $this->assertTrue(count($info) == 2 && !empty($info['en']) && !empty($info['it']), t('Delete hook has been properly fired.'));
  411. }
  412. /**
  413. * Test whether hooks are properly fired when using the API.
  414. */
  415. function testAPI() {
  416. // Create Basic page in English.
  417. $node = $this->createPage($this->randomName(), $this->randomName(), 'en');
  418. $handler = entity_translation_get_handler('node', $node);
  419. // Create a translation in Italian.
  420. $translation = array('source' => 'en', 'language' => 'it');
  421. $handler->setTranslation($translation);
  422. $handler->saveTranslations();
  423. $node = node_load($node->nid, NULL, TRUE);
  424. $handler = entity_translation_get_handler('node', $node);
  425. $translations = $handler->getTranslations();
  426. $this->assertTrue(!empty($translations->data['it']), t('An Italian translation has been created'));
  427. $info = $this->getHookInfo();
  428. $this->assertTrue(!empty($info['insert']), t('Insert hook has been properly fired.'));
  429. // Check that the update hook is properly fired.
  430. $translation['status'] = 1;
  431. $handler->setTranslation($translation);
  432. $handler->saveTranslations();
  433. $info = $this->getHookInfo();
  434. $this->assertTrue(!empty($info['update']), t('Update hook has been properly fired.'));
  435. // Create a Spanish translation and update it before saving it.
  436. $translation = array('source' => 'it', 'language' => 'es');
  437. $handler->setTranslation($translation);
  438. $translation['status'] = 1;
  439. $handler->setTranslation($translation);
  440. $handler->saveTranslations();
  441. $node = node_load($node->nid, NULL, TRUE);
  442. $handler = entity_translation_get_handler('node', $node);
  443. $translations = $handler->getTranslations();
  444. $this->assertTrue(!empty($translations->data['es']), t('A Spanish translation has been created'));
  445. $info = $this->getHookInfo();
  446. $this->assertTrue(!empty($info['insert']), t('Insert hook has been properly fired.'));
  447. // Delete a translation after updating it without saving.
  448. $translation['status'] = 0;
  449. $handler->setTranslation($translation);
  450. $handler->removeTranslation('es');
  451. $handler->saveTranslations();
  452. $info = $this->getHookInfo();
  453. $this->assertTrue(empty($info['update']), t('Update hook has not been fired.'));
  454. $info = $this->getHookInfo('delete');
  455. $this->assertTrue(!empty($info['es']), t('Delete hook has been properly fired.'));
  456. }
  457. /**
  458. * Retrieve the information stored by hook implementations.
  459. */
  460. protected function getHookInfo($op = 'save') {
  461. $name = 'entity_translation_test_' . $op;
  462. $info = variable_get($name);
  463. variable_del($name);
  464. return $info;
  465. }
  466. }