ContentTranslationUITest.php 14 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Definition of Drupal\entity\Tests\ContentTranslationUITest.
6 7
 */

8
namespace Drupal\content_translation\Tests;
9 10

use Drupal\Core\Entity\EntityInterface;
11
use Drupal\Core\Entity\ContentEntityBase;
12
use Drupal\Core\Language\Language;
13 14

/**
15
 * Tests the Content Translation UI.
16
 */
17
abstract class ContentTranslationUITest extends ContentTranslationTestBase {
18

19 20 21 22 23 24 25
  /**
   * The id of the entity being translated.
   *
   * @var mixed
   */
  protected $entityId;

26 27 28 29 30 31 32 33 34 35 36
  /**
   * Whether the behavior of the language selector should be tested.
   *
   * @var boolean
   */
  protected $testLanguageSelector = TRUE;

  /**
   * Tests the basic translation UI.
   */
  function testTranslationUI() {
37
    $this->doTestBasicTranslation();
38
    $this->doTestTranslationOverview();
39 40 41 42
    $this->doTestOutdatedStatus();
    $this->doTestPublishedStatus();
    $this->doTestAuthoringInfo();
    $this->doTestTranslationDeletion();
43 44 45 46 47
  }

  /**
   * Tests the basic translation workflow.
   */
48
  protected function doTestBasicTranslation() {
49 50 51
    // Create a new test entity with original values in the default language.
    $default_langcode = $this->langcodes[0];
    $values[$default_langcode] = $this->getNewEntityValues($default_langcode);
52
    $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
53
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
54
    $this->assertTrue($entity, 'Entity found in the database.');
55
    $this->drupalGet($entity->getSystemPath());
56
    $this->assertResponse(200, 'Entity URL is valid.');
57 58
    $this->drupalGet($entity->getSystemPath('drupal:content-translation-overview'));
    $this->assertNoText('Source language', 'Source language column correctly hidden.');
59 60 61 62 63 64

    $translation = $this->getTranslation($entity, $default_langcode);
    foreach ($values[$default_langcode] as $property => $value) {
      $stored_value = $this->getValue($translation, $property, $default_langcode);
      $value = is_array($value) ? $value[0]['value'] : $value;
      $message = format_string('@property correctly stored in the default language.', array('@property' => $property));
65
      $this->assertEqual($stored_value, $value, $message);
66 67
    }

68
    // Add a content translation.
69 70 71
    $langcode = 'it';
    $values[$langcode] = $this->getNewEntityValues($langcode);

72 73
    $content_translation_path = $entity->getSystemPath('drupal:content-translation-overview');
    $path = $langcode . '/' . $content_translation_path . '/add/' . $default_langcode . '/' . $langcode;
74
    $this->drupalPostForm($path, $this->getEditValues($values, $langcode), $this->getFormSubmitActionForNewTranslation($entity, $langcode));
75
    if ($this->testLanguageSelector) {
76
      $this->assertNoFieldByXPath('//select[@id="edit-langcode"]', NULL, 'Language selector correctly disabled on translations.');
77
    }
78
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
79 80
    $this->drupalGet($entity->getSystemPath('drupal:content-translation-overview'));
    $this->assertNoText('Source language', 'Source language column correctly hidden.');
81 82 83 84 85

    // Switch the source language.
    $langcode = 'fr';
    $source_langcode = 'it';
    $edit = array('source_langcode[source]' => $source_langcode);
86
    $path = $langcode . '/' . $content_translation_path . '/add/' . $default_langcode . '/' . $langcode;
87 88
    // This does not save anything, it merely reloads the form and fills in the
    // fields with the values from the different source language.
89
    $this->drupalPostForm($path, $edit, t('Change'));
90
    $this->assertFieldByXPath("//input[@name=\"{$this->fieldName}[0][value]\"]", $values[$source_langcode][$this->fieldName][0]['value'], 'Source language correctly switched.');
91 92 93

    // Add another translation and mark the other ones as outdated.
    $values[$langcode] = $this->getNewEntityValues($langcode);
94
    $edit = $this->getEditValues($values, $langcode) + array('content_translation[retranslate]' => TRUE);
95
    $path = $langcode . '/' . $content_translation_path . '/add/' . $source_langcode . '/' . $langcode;
96
    $this->drupalPostForm($path, $edit, $this->getFormSubmitActionForNewTranslation($entity, $langcode));
97
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
98 99
    $this->drupalGet($entity->getSystemPath('drupal:content-translation-overview'));
    $this->assertText('Source language', 'Source language column correctly shown.');
100 101 102 103 104 105 106 107 108 109 110

    // Check that the entered values have been correctly stored.
    foreach ($values as $langcode => $property_values) {
      $translation = $this->getTranslation($entity, $langcode);
      foreach ($property_values as $property => $value) {
        $stored_value = $this->getValue($translation, $property, $langcode);
        $value = is_array($value) ? $value[0]['value'] : $value;
        $message = format_string('%property correctly stored with language %language.', array('%property' => $property, '%language' => $langcode));
        $this->assertEqual($stored_value, $value, $message);
      }
    }
111 112
  }

113 114 115 116
  /**
   * Tests that the translation overview shows the correct values.
   */
  protected function doTestTranslationOverview() {
117
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
118
    $this->drupalGet($entity->getSystemPath('drupal:content-translation-overview'));
119 120 121 122 123 124 125 126

    foreach ($this->langcodes as $langcode) {
      if ($entity->hasTranslation($langcode)) {
        $this->assertText($entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation', array('%language' => $langcode)));
      }
    }
  }

127 128 129
  /**
   * Tests up-to-date status tracking.
   */
130
  protected function doTestOutdatedStatus() {
131
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
132 133 134 135
    $langcode = 'fr';
    $default_langcode = $this->langcodes[0];

    // Mark translations as outdated.
136
    $edit = array('content_translation[retranslate]' => TRUE);
137
    $edit_path = $entity->getSystemPath('edit-form');
138
    $this->drupalPostForm($langcode . '/' . $edit_path, $edit, $this->getFormSubmitAction($entity, $langcode));
139
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
140 141

    // Check that every translation has the correct "outdated" status.
142 143
    foreach ($this->langcodes as $added_langcode) {
      $prefix = $added_langcode != $default_langcode ? $added_langcode . '/' : '';
144
      $path = $prefix . $edit_path;
145
      $this->drupalGet($path);
146
      if ($added_langcode == $langcode) {
147
        $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
148 149
      }
      else {
150 151
        $this->assertFieldByXPath('//input[@name="content_translation[outdated]"]', TRUE, 'The translate flag is checked by default.');
        $edit = array('content_translation[outdated]' => FALSE);
152
        $this->drupalPostForm($path, $edit, $this->getFormSubmitAction($entity, $added_langcode));
153
        $this->drupalGet($path);
154
        $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is now shown.');
155
        $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
156
        $this->assertFalse($entity->translation[$added_langcode]['outdated'], 'The "outdated" status has been correctly stored.');
157 158 159 160 161 162 163
      }
    }
  }

  /**
   * Tests the translation publishing status.
   */
164
  protected function doTestPublishedStatus() {
165
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
166
    $path = $entity->getSystemPath('edit-form');
167 168 169 170

    // Unpublish translations.
    foreach ($this->langcodes as $index => $langcode) {
      if ($index > 0) {
171
        $edit = array('content_translation[status]' => FALSE);
172
        $this->drupalPostForm($langcode . '/' . $path, $edit, $this->getFormSubmitAction($entity, $langcode));
173
        $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
174
        $this->assertFalse($entity->translation[$langcode]['status'], 'The translation has been correctly unpublished.');
175 176 177
      }
    }

178 179
    // Check that the last published translation cannot be unpublished.
    $this->drupalGet($path);
180
    $this->assertFieldByXPath('//input[@name="content_translation[status]" and @disabled="disabled"]', TRUE, 'The last translation is published and cannot be unpublished.');
181 182 183 184 185
  }

  /**
   * Tests the translation authoring information.
   */
186
  protected function doTestAuthoringInfo() {
187
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
188
    $path = $entity->getSystemPath('edit-form');
189 190 191 192 193 194
    $values = array();

    // Post different authoring information for each translation.
    foreach ($this->langcodes as $index => $langcode) {
      $user = $this->drupalCreateUser();
      $values[$langcode] = array(
195
        'uid' => $user->id(),
196 197 198
        'created' => REQUEST_TIME - mt_rand(0, 1000),
      );
      $edit = array(
199
        'content_translation[name]' => $user->getUsername(),
200
        'content_translation[created]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d H:i:s O'),
201 202
      );
      $prefix = $index > 0 ? $langcode . '/' : '';
203
      $this->drupalPostForm($prefix . $path, $edit, $this->getFormSubmitAction($entity, $langcode));
204 205
    }

206
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
207
    foreach ($this->langcodes as $langcode) {
208 209
      $this->assertEqual($entity->translation[$langcode]['uid'], $values[$langcode]['uid'], 'Translation author correctly stored.');
      $this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'], 'Translation date correctly stored.');
210 211 212 213 214 215
    }

    // Try to post non valid values and check that they are rejected.
    $langcode = 'en';
    $edit = array(
      // User names have by default length 8.
216 217
      'content_translation[name]' => $this->randomName(12),
      'content_translation[created]' => '19/11/1978',
218
    );
219
    $this->drupalPostForm($path, $edit, $this->getFormSubmitAction($entity, $langcode));
220
    $this->assertTrue($this->xpath('//div[contains(@class, "error")]//ul'), 'Invalid values generate a list of form errors.');
221 222
    $this->assertEqual($entity->translation[$langcode]['uid'], $values[$langcode]['uid'], 'Translation author correctly kept.');
    $this->assertEqual($entity->translation[$langcode]['created'], $values[$langcode]['created'], 'Translation date correctly kept.');
223 224 225 226 227
  }

  /**
   * Tests translation deletion.
   */
228
  protected function doTestTranslationDeletion() {
229
    // Confirm and delete a translation.
230
    $langcode = 'fr';
231
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
232 233
    $path = $entity->getSystemPath('edit-form');
    $this->drupalPostForm($langcode . '/' . $path, array(), t('Delete translation'));
234
    $this->drupalPostForm(NULL, array(), t('Delete'));
235
    $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
236 237
    if ($this->assertTrue(is_object($entity), 'Entity found')) {
      $translations = $entity->getTranslationLanguages();
238
      $this->assertTrue(count($translations) == 2 && empty($translations[$langcode]), 'Translation successfully deleted.');
239
    }
240 241 242 243 244 245 246 247 248 249 250 251 252 253
  }

  /**
   * Returns an array of entity field values to be tested.
   */
  protected function getNewEntityValues($langcode) {
    return array($this->fieldName => array(array('value' => $this->randomName(16))));
  }

  /**
   * Returns an edit array containing the values to be posted.
   */
  protected function getEditValues($values, $langcode, $new = FALSE) {
    $edit = $values[$langcode];
254
    $langcode = $new ? Language::LANGCODE_NOT_SPECIFIED : $langcode;
255 256
    foreach ($values[$langcode] as $property => $value) {
      if (is_array($value)) {
257
        $edit["{$property}[0][value]"] = $value[0]['value'];
258 259 260 261 262 263
        unset($edit[$property]);
      }
    }
    return $edit;
  }

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
  /**
   * Returns the form action value when submitting a new translation.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity being tested.
   * @param string $langcode
   *   Language code for the form.
   *
   * @return string
   *   Name of the button to hit.
   */
  protected function getFormSubmitActionForNewTranslation(EntityInterface $entity, $langcode) {
    $entity->addTranslation($langcode, $entity->toArray());
    return $this->getFormSubmitAction($entity, $langcode);
  }

280 281
  /**
   * Returns the form action value to be used to submit the entity form.
282 283 284
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity being tested.
285 286
   * @param string $langcode
   *   Language code for the form.
287 288 289
   *
   * @return string
   *   Name of the button to hit.
290
   */
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
  protected function getFormSubmitAction(EntityInterface $entity, $langcode) {
    return t('Save') . $this->getFormSubmitSuffix($entity, $langcode);
  }

  /**
   * Returns appropriate submit button suffix based on translatability.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity being tested.
   * @param string $langcode
   *   Language code for the form.
   *
   * @return string
   *   Submit button suffix based on translatability.
   */
  protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
    return '';
308 309
  }

310 311 312
  /**
   * Returns the translation object to use to retrieve the translated values.
   *
313
   * @param \Drupal\Core\Entity\EntityInterface $entity
314 315 316 317 318 319 320 321
   *   The entity being tested.
   * @param string $langcode
   *   The language code identifying the translation to be retrieved.
   *
   * @return \Drupal\Core\TypedData\TranslatableInterface
   *   The translation object to act on.
   */
  protected function getTranslation(EntityInterface $entity, $langcode) {
322
    return $entity->getTranslation($langcode);
323 324 325 326 327
  }

  /**
   * Returns the value for the specified property in the given language.
   *
328
   * @param \Drupal\Core\Entity\EntityInterface $translation
329 330 331 332 333 334 335 336 337
   *   The translation object the property value should be retrieved from.
   * @param string $property
   *   The property name.
   * @param string $langcode
   *   The property value.
   *
   * @return
   *   The property value.
   */
338
  protected function getValue(EntityInterface $translation, $property, $langcode) {
339
    $key = $property == 'user_id' ? 'target_id' : 'value';
340
    return $translation->get($property)->{$key};
341 342 343
  }

}