diff core/modules/node/tests/src/Functional/NodeTranslationUITest.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/modules/node/tests/src/Functional/NodeTranslationUITest.php	Wed Nov 29 16:09:58 2017 +0000
@@ -0,0 +1,502 @@
+<?php
+
+namespace Drupal\Tests\node\Functional;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Url;
+use Drupal\node\Entity\Node;
+use Drupal\language\Entity\ConfigurableLanguage;
+
+/**
+ * Tests the Node Translation UI.
+ *
+ * @group node
+ */
+class NodeTranslationUITest extends ContentTranslationUITestBase {
+
+  /**
+   * {inheritdoc}
+   */
+  protected $defaultCacheContexts = [
+    'languages:language_interface',
+    'session',
+    'theme',
+    'route',
+    'timezone',
+    'url.path.parent',
+    'url.query_args:_wrapper_format',
+    'user'
+  ];
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['block', 'language', 'content_translation', 'node', 'datetime', 'field_ui', 'help'];
+
+  /**
+   * The profile to install as a basis for testing.
+   *
+   * @var string
+   */
+  protected $profile = 'standard';
+
+  protected function setUp() {
+    $this->entityTypeId = 'node';
+    $this->bundle = 'article';
+    parent::setUp();
+
+    // Ensure the help message is shown even with prefixed paths.
+    $this->drupalPlaceBlock('help_block', ['region' => 'content']);
+
+    // Display the language selector.
+    $this->drupalLogin($this->administrator);
+    $edit = ['language_configuration[language_alterable]' => TRUE];
+    $this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
+    $this->drupalLogin($this->translator);
+  }
+
+  /**
+   * Tests the basic translation UI.
+   */
+  public function testTranslationUI() {
+    parent::testTranslationUI();
+    $this->doUninstallTest();
+  }
+
+  /**
+   * Tests changing the published status on a node without fields.
+   */
+  public function testPublishedStatusNoFields() {
+    // Test changing the published status of an article without fields.
+    $this->drupalLogin($this->administrator);
+    // Delete all fields.
+    $this->drupalGet('admin/structure/types/manage/article/fields');
+    $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $this->fieldName . '/delete', [], t('Delete'));
+    $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags/delete', [], t('Delete'));
+    $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/delete', [], t('Delete'));
+
+    // Add a node.
+    $default_langcode = $this->langcodes[0];
+    $values[$default_langcode] = ['title' => [['value' => $this->randomMachineName()]]];
+    $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
+    $storage = $this->container->get('entity_type.manager')
+      ->getStorage($this->entityTypeId);
+    $storage->resetCache([$this->entityId]);
+    $entity = $storage->load($this->entityId);
+
+    // Add a content translation.
+    $langcode = 'fr';
+    $language = ConfigurableLanguage::load($langcode);
+    $values[$langcode] = ['title' => [['value' => $this->randomMachineName()]]];
+
+    $entity_type_id = $entity->getEntityTypeId();
+    $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
+      $entity->getEntityTypeId() => $entity->id(),
+      'source' => $default_langcode,
+      'target' => $langcode
+    ], ['language' => $language]);
+    $edit = $this->getEditValues($values, $langcode);
+    $edit['status[value]'] = FALSE;
+    $this->drupalPostForm($add_url, $edit, t('Save (this translation)'));
+
+    $storage->resetCache([$this->entityId]);
+    $entity = $storage->load($this->entityId);
+    $translation = $entity->getTranslation($langcode);
+    // Make sure we unpublished the node correctly.
+    $this->assertFalse($this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getTranslatorPermissions() {
+    return array_merge(parent::getTranslatorPermissions(), ['administer nodes', "edit any $this->bundle content"]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditorPermissions() {
+    return ['administer nodes', 'create article content'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAdministratorPermissions() {
+    return array_merge(parent::getAdministratorPermissions(), ['access administration pages', 'administer content types', 'administer node fields', 'access content overview', 'bypass node access', 'administer languages', 'administer themes', 'view the administration theme']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNewEntityValues($langcode) {
+    return ['title' => [['value' => $this->randomMachineName()]]] + parent::getNewEntityValues($langcode);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doTestPublishedStatus() {
+    $storage = $this->container->get('entity_type.manager')
+      ->getStorage($this->entityTypeId);
+    $storage->resetCache([$this->entityId]);
+    $entity = $storage->load($this->entityId);
+    $languages = $this->container->get('language_manager')->getLanguages();
+
+    $statuses = [
+      TRUE,
+      FALSE,
+    ];
+
+    foreach ($statuses as $index => $value) {
+      // (Un)publish the node translations and check that the translation
+      // statuses are (un)published accordingly.
+      foreach ($this->langcodes as $langcode) {
+        $options = ['language' => $languages[$langcode]];
+        $url = $entity->urlInfo('edit-form', $options);
+        $this->drupalPostForm($url, ['status[value]' => $value], t('Save') . $this->getFormSubmitSuffix($entity, $langcode), $options);
+      }
+      $storage->resetCache([$this->entityId]);
+      $entity = $storage->load($this->entityId);
+      foreach ($this->langcodes as $langcode) {
+        // The node is created as unpublished thus we switch to the published
+        // status first.
+        $status = !$index;
+        $translation = $entity->getTranslation($langcode);
+        $this->assertEqual($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doTestAuthoringInfo() {
+    $storage = $this->container->get('entity_type.manager')
+      ->getStorage($this->entityTypeId);
+    $storage->resetCache([$this->entityId]);
+    $entity = $storage->load($this->entityId);
+    $languages = $this->container->get('language_manager')->getLanguages();
+    $values = [];
+
+    // Post different base field information for each translation.
+    foreach ($this->langcodes as $langcode) {
+      $user = $this->drupalCreateUser();
+      $values[$langcode] = [
+        'uid' => $user->id(),
+        'created' => REQUEST_TIME - mt_rand(0, 1000),
+        'sticky' => (bool) mt_rand(0, 1),
+        'promote' => (bool) mt_rand(0, 1),
+      ];
+      $edit = [
+        'uid[0][target_id]' => $user->getUsername(),
+        'created[0][value][date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
+        'created[0][value][time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
+        'sticky[value]' => $values[$langcode]['sticky'],
+        'promote[value]' => $values[$langcode]['promote'],
+      ];
+      $options = ['language' => $languages[$langcode]];
+      $url = $entity->urlInfo('edit-form', $options);
+      $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options);
+    }
+
+    $storage->resetCache([$this->entityId]);
+    $entity = $storage->load($this->entityId);
+    foreach ($this->langcodes as $langcode) {
+      $translation = $entity->getTranslation($langcode);
+      $metadata = $this->manager->getTranslationMetadata($translation);
+      $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
+      $this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
+      $this->assertEqual($translation->isSticky(), $values[$langcode]['sticky'], 'Sticky of Translation correctly stored.');
+      $this->assertEqual($translation->isPromoted(), $values[$langcode]['promote'], 'Promoted of Translation correctly stored.');
+    }
+  }
+
+  /**
+   * Tests that translation page inherits admin status of edit page.
+   */
+  public function testTranslationLinkTheme() {
+    $this->drupalLogin($this->administrator);
+    $article = $this->drupalCreateNode(['type' => 'article', 'langcode' => $this->langcodes[0]]);
+
+    // Set up Seven as the admin theme and use it for node editing.
+    $this->container->get('theme_handler')->install(['seven']);
+    $edit = [];
+    $edit['admin_theme'] = 'seven';
+    $edit['use_admin_theme'] = TRUE;
+    $this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
+    $this->drupalGet('node/' . $article->id() . '/translations');
+    $this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme if edit is admin.');
+
+    // Turn off admin theme for editing, assert inheritance to translations.
+    $edit['use_admin_theme'] = FALSE;
+    $this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
+    $this->drupalGet('node/' . $article->id() . '/translations');
+    $this->assertNoRaw('core/themes/seven/css/base/elements.css', 'Translation uses frontend theme if edit is frontend.');
+
+    // Assert presence of translation page itself (vs. DisabledBundle below).
+    $this->assertResponse(200);
+  }
+
+  /**
+   * Tests that no metadata is stored for a disabled bundle.
+   */
+  public function testDisabledBundle() {
+    // Create a bundle that does not have translation enabled.
+    $disabledBundle = $this->randomMachineName();
+    $this->drupalCreateContentType(['type' => $disabledBundle, 'name' => $disabledBundle]);
+
+    // Create a node for each bundle.
+    $node = $this->drupalCreateNode([
+      'type' => $this->bundle,
+      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+    ]);
+
+    // Make sure that nothing was inserted into the {content_translation} table.
+    $rows = db_query('SELECT nid, count(nid) AS count FROM {node_field_data} WHERE type <> :type GROUP BY nid HAVING count(nid) >= 2', [':type' => $this->bundle])->fetchAll();
+    $this->assertEqual(0, count($rows));
+
+    // Ensure the translation tab is not accessible.
+    $this->drupalGet('node/' . $node->id() . '/translations');
+    $this->assertResponse(403);
+  }
+
+  /**
+   * Tests that translations are rendered properly.
+   */
+  public function testTranslationRendering() {
+    $default_langcode = $this->langcodes[0];
+    $values[$default_langcode] = $this->getNewEntityValues($default_langcode);
+    $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
+    $node = \Drupal::entityManager()->getStorage($this->entityTypeId)->load($this->entityId);
+    $node->setPromoted(TRUE);
+
+    // Create translations.
+    foreach (array_diff($this->langcodes, [$default_langcode]) as $langcode) {
+      $values[$langcode] = $this->getNewEntityValues($langcode);
+      $translation = $node->addTranslation($langcode, $values[$langcode]);
+      // Publish and promote the translation to frontpage.
+      $translation->setPromoted(TRUE);
+      $translation->setPublished(TRUE);
+    }
+    $node->save();
+
+    // Test that the frontpage view displays the correct translations.
+    \Drupal::service('module_installer')->install(['views'], TRUE);
+    $this->rebuildContainer();
+    $this->doTestTranslations('node', $values);
+
+    // Enable the translation language renderer.
+    $view = \Drupal::entityManager()->getStorage('view')->load('frontpage');
+    $display = &$view->getDisplay('default');
+    $display['display_options']['rendering_language'] = '***LANGUAGE_entity_translation***';
+    $view->save();
+
+    // Need to check from the beginning, including the base_path, in the url
+    // since the pattern for the default language might be a substring of
+    // the strings for other languages.
+    $base_path = base_path();
+
+    // Check the frontpage for 'Read more' links to each translation.
+    // See also assertTaxonomyPage() in NodeAccessBaseTableTest.
+    $node_href = 'node/' . $node->id();
+    foreach ($this->langcodes as $langcode) {
+      $this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
+      $num_match_found = 0;
+      if ($langcode == 'en') {
+        // Site default language does not have langcode prefix in the URL.
+        $expected_href = $base_path . $node_href;
+      }
+      else {
+        $expected_href = $base_path . $langcode . '/' . $node_href;
+      }
+      $pattern = '|^' . $expected_href . '$|';
+      foreach ($this->xpath("//a[text()='Read more']") as $link) {
+        if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
+          $num_match_found++;
+        }
+      }
+      $this->assertTrue($num_match_found == 1, 'There is 1 Read more link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
+    }
+
+    // Check the frontpage for 'Add new comment' links that include the
+    // language.
+    $comment_form_href = 'node/' . $node->id() . '#comment-form';
+    foreach ($this->langcodes as $langcode) {
+      $this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
+      $num_match_found = 0;
+      if ($langcode == 'en') {
+        // Site default language does not have langcode prefix in the URL.
+        $expected_href = $base_path . $comment_form_href;
+      }
+      else {
+        $expected_href = $base_path . $langcode . '/' . $comment_form_href;
+      }
+      $pattern = '|^' . $expected_href . '$|';
+      foreach ($this->xpath("//a[text()='Add new comment']") as $link) {
+        if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
+          $num_match_found++;
+        }
+      }
+      $this->assertTrue($num_match_found == 1, 'There is 1 Add new comment link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
+    }
+
+    // Test that the node page displays the correct translations.
+    $this->doTestTranslations('node/' . $node->id(), $values);
+
+    // Test that the node page has the correct alternate hreflang links.
+    $this->doTestAlternateHreflangLinks($node->urlInfo());
+  }
+
+  /**
+   * Tests that the given path displays the correct translation values.
+   *
+   * @param string $path
+   *   The path to be tested.
+   * @param array $values
+   *   The translation values to be found.
+   */
+  protected function doTestTranslations($path, array $values) {
+    $languages = $this->container->get('language_manager')->getLanguages();
+    foreach ($this->langcodes as $langcode) {
+      $this->drupalGet($path, ['language' => $languages[$langcode]]);
+      $this->assertText($values[$langcode]['title'][0]['value'], format_string('The %langcode node translation is correctly displayed.', ['%langcode' => $langcode]));
+    }
+  }
+
+  /**
+   * Tests that the given path provides the correct alternate hreflang links.
+   *
+   * @param \Drupal\Core\Url $url
+   *   The path to be tested.
+   */
+  protected function doTestAlternateHreflangLinks(Url $url) {
+    $languages = $this->container->get('language_manager')->getLanguages();
+    $url->setAbsolute();
+    $urls = [];
+    foreach ($this->langcodes as $langcode) {
+      $language_url = clone $url;
+      $urls[$langcode] = $language_url->setOption('language', $languages[$langcode]);
+    }
+    foreach ($this->langcodes as $langcode) {
+      $this->drupalGet($urls[$langcode]);
+      foreach ($urls as $alternate_langcode => $language_url) {
+        // Retrieve desired link elements from the HTML head.
+        $links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]',
+          [':href' => $language_url->toString(), ':hreflang' => $alternate_langcode]);
+        $this->assert(isset($links[0]), format_string('The %langcode node translation has the correct alternate hreflang link for %alternate_langcode: %link.', ['%langcode' => $langcode, '%alternate_langcode' => $alternate_langcode, '%link' => $url->toString()]));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
+    if (!$entity->isNew() && $entity->isTranslatable()) {
+      $translations = $entity->getTranslationLanguages();
+      if ((count($translations) > 1 || !isset($translations[$langcode])) && ($field = $entity->getFieldDefinition('status'))) {
+        return ' ' . ($field->isTranslatable() ? t('(this translation)') : t('(all translations)'));
+      }
+    }
+    return '';
+  }
+
+  /**
+   * Tests uninstalling content_translation.
+   */
+  protected function doUninstallTest() {
+    // Delete all the nodes so there is no data.
+    $nodes = Node::loadMultiple();
+    foreach ($nodes as $node) {
+      $node->delete();
+    }
+    $language_count = count(\Drupal::configFactory()->listAll('language.content_settings.'));
+    \Drupal::service('module_installer')->uninstall(['content_translation']);
+    $this->rebuildContainer();
+    $this->assertEqual($language_count, count(\Drupal::configFactory()->listAll('language.content_settings.')), 'Languages have been fixed rather than deleted during content_translation uninstall.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doTestTranslationEdit() {
+    $storage = $this->container->get('entity_type.manager')
+      ->getStorage($this->entityTypeId);
+    $storage->resetCache([$this->entityId]);
+    $entity = $storage->load($this->entityId);
+    $languages = $this->container->get('language_manager')->getLanguages();
+    $type_name = node_get_type_label($entity);
+
+    foreach ($this->langcodes as $langcode) {
+      // We only want to test the title for non-english translations.
+      if ($langcode != 'en') {
+        $options = ['language' => $languages[$langcode]];
+        $url = $entity->urlInfo('edit-form', $options);
+        $this->drupalGet($url);
+
+        $title = t('<em>Edit @type</em> @title [%language translation]', [
+          '@type' => $type_name,
+          '@title' => $entity->getTranslation($langcode)->label(),
+          '%language' => $languages[$langcode]->getName(),
+        ]);
+        $this->assertRaw($title);
+      }
+    }
+  }
+
+  /**
+   * Tests that revision translations are rendered properly.
+   */
+  public function testRevisionTranslationRendering() {
+    $storage = \Drupal::entityTypeManager()->getStorage('node');
+
+    // Create a node.
+    $nid = $this->createEntity(['title' => 'First rev en title'], 'en');
+    $node = $storage->load($nid);
+    $original_revision_id = $node->getRevisionId();
+
+    // Add a French translation.
+    $translation = $node->addTranslation('fr');
+    $translation->title = 'First rev fr title';
+    $translation->setNewRevision(FALSE);
+    $translation->save();
+
+    // Create a new revision.
+    $node->title = 'Second rev en title';
+    $node->setNewRevision(TRUE);
+    $node->save();
+
+    // Get an English view of this revision.
+    $original_revision = $storage->loadRevision($original_revision_id);
+    $original_revision_url = $original_revision->toUrl('revision')->toString();
+
+    // Should be different from regular node URL.
+    $this->assertNotIdentical($original_revision_url, $original_revision->toUrl()->toString());
+    $this->drupalGet($original_revision_url);
+    $this->assertResponse(200);
+
+    // Contents should be in English, of correct revision.
+    $this->assertText('First rev en title');
+    $this->assertNoText('First rev fr title');
+
+    // Get a French view.
+    $url_fr = $original_revision->getTranslation('fr')->toUrl('revision')->toString();
+
+    // Should have different URL from English.
+    $this->assertNotIdentical($url_fr, $original_revision->toUrl()->toString());
+    $this->assertNotIdentical($url_fr, $original_revision_url);
+    $this->drupalGet($url_fr);
+    $this->assertResponse(200);
+
+    // Contents should be in French, of correct revision.
+    $this->assertText('First rev fr title');
+    $this->assertNoText('First rev en title');
+  }
+
+}