Mercurial > hg > isophonics-drupal-site
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Tests\node\Functional; | |
4 | |
5 use Drupal\Core\Entity\EntityInterface; | |
6 use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase; | |
7 use Drupal\Core\Language\LanguageInterface; | |
8 use Drupal\Core\Url; | |
9 use Drupal\node\Entity\Node; | |
10 use Drupal\language\Entity\ConfigurableLanguage; | |
11 | |
12 /** | |
13 * Tests the Node Translation UI. | |
14 * | |
15 * @group node | |
16 */ | |
17 class NodeTranslationUITest extends ContentTranslationUITestBase { | |
18 | |
19 /** | |
20 * {inheritdoc} | |
21 */ | |
22 protected $defaultCacheContexts = [ | |
23 'languages:language_interface', | |
24 'session', | |
25 'theme', | |
26 'route', | |
27 'timezone', | |
28 'url.path.parent', | |
29 'url.query_args:_wrapper_format', | |
30 'user' | |
31 ]; | |
32 | |
33 /** | |
34 * Modules to enable. | |
35 * | |
36 * @var array | |
37 */ | |
38 public static $modules = ['block', 'language', 'content_translation', 'node', 'datetime', 'field_ui', 'help']; | |
39 | |
40 /** | |
41 * The profile to install as a basis for testing. | |
42 * | |
43 * @var string | |
44 */ | |
45 protected $profile = 'standard'; | |
46 | |
47 protected function setUp() { | |
48 $this->entityTypeId = 'node'; | |
49 $this->bundle = 'article'; | |
50 parent::setUp(); | |
51 | |
52 // Ensure the help message is shown even with prefixed paths. | |
53 $this->drupalPlaceBlock('help_block', ['region' => 'content']); | |
54 | |
55 // Display the language selector. | |
56 $this->drupalLogin($this->administrator); | |
57 $edit = ['language_configuration[language_alterable]' => TRUE]; | |
58 $this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type')); | |
59 $this->drupalLogin($this->translator); | |
60 } | |
61 | |
62 /** | |
63 * Tests the basic translation UI. | |
64 */ | |
65 public function testTranslationUI() { | |
66 parent::testTranslationUI(); | |
67 $this->doUninstallTest(); | |
68 } | |
69 | |
70 /** | |
71 * Tests changing the published status on a node without fields. | |
72 */ | |
73 public function testPublishedStatusNoFields() { | |
74 // Test changing the published status of an article without fields. | |
75 $this->drupalLogin($this->administrator); | |
76 // Delete all fields. | |
77 $this->drupalGet('admin/structure/types/manage/article/fields'); | |
78 $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $this->fieldName . '/delete', [], t('Delete')); | |
79 $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags/delete', [], t('Delete')); | |
80 $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/delete', [], t('Delete')); | |
81 | |
82 // Add a node. | |
83 $default_langcode = $this->langcodes[0]; | |
84 $values[$default_langcode] = ['title' => [['value' => $this->randomMachineName()]]]; | |
85 $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode); | |
86 $storage = $this->container->get('entity_type.manager') | |
87 ->getStorage($this->entityTypeId); | |
88 $storage->resetCache([$this->entityId]); | |
89 $entity = $storage->load($this->entityId); | |
90 | |
91 // Add a content translation. | |
92 $langcode = 'fr'; | |
93 $language = ConfigurableLanguage::load($langcode); | |
94 $values[$langcode] = ['title' => [['value' => $this->randomMachineName()]]]; | |
95 | |
96 $entity_type_id = $entity->getEntityTypeId(); | |
97 $add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [ | |
98 $entity->getEntityTypeId() => $entity->id(), | |
99 'source' => $default_langcode, | |
100 'target' => $langcode | |
101 ], ['language' => $language]); | |
102 $edit = $this->getEditValues($values, $langcode); | |
103 $edit['status[value]'] = FALSE; | |
104 $this->drupalPostForm($add_url, $edit, t('Save (this translation)')); | |
105 | |
106 $storage->resetCache([$this->entityId]); | |
107 $entity = $storage->load($this->entityId); | |
108 $translation = $entity->getTranslation($langcode); | |
109 // Make sure we unpublished the node correctly. | |
110 $this->assertFalse($this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.'); | |
111 } | |
112 | |
113 /** | |
114 * {@inheritdoc} | |
115 */ | |
116 protected function getTranslatorPermissions() { | |
117 return array_merge(parent::getTranslatorPermissions(), ['administer nodes', "edit any $this->bundle content"]); | |
118 } | |
119 | |
120 /** | |
121 * {@inheritdoc} | |
122 */ | |
123 protected function getEditorPermissions() { | |
124 return ['administer nodes', 'create article content']; | |
125 } | |
126 | |
127 /** | |
128 * {@inheritdoc} | |
129 */ | |
130 protected function getAdministratorPermissions() { | |
131 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']); | |
132 } | |
133 | |
134 /** | |
135 * {@inheritdoc} | |
136 */ | |
137 protected function getNewEntityValues($langcode) { | |
138 return ['title' => [['value' => $this->randomMachineName()]]] + parent::getNewEntityValues($langcode); | |
139 } | |
140 | |
141 /** | |
142 * {@inheritdoc} | |
143 */ | |
144 protected function doTestPublishedStatus() { | |
145 $storage = $this->container->get('entity_type.manager') | |
146 ->getStorage($this->entityTypeId); | |
147 $storage->resetCache([$this->entityId]); | |
148 $entity = $storage->load($this->entityId); | |
149 $languages = $this->container->get('language_manager')->getLanguages(); | |
150 | |
151 $statuses = [ | |
152 TRUE, | |
153 FALSE, | |
154 ]; | |
155 | |
156 foreach ($statuses as $index => $value) { | |
157 // (Un)publish the node translations and check that the translation | |
158 // statuses are (un)published accordingly. | |
159 foreach ($this->langcodes as $langcode) { | |
160 $options = ['language' => $languages[$langcode]]; | |
161 $url = $entity->urlInfo('edit-form', $options); | |
162 $this->drupalPostForm($url, ['status[value]' => $value], t('Save') . $this->getFormSubmitSuffix($entity, $langcode), $options); | |
163 } | |
164 $storage->resetCache([$this->entityId]); | |
165 $entity = $storage->load($this->entityId); | |
166 foreach ($this->langcodes as $langcode) { | |
167 // The node is created as unpublished thus we switch to the published | |
168 // status first. | |
169 $status = !$index; | |
170 $translation = $entity->getTranslation($langcode); | |
171 $this->assertEqual($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.'); | |
172 } | |
173 } | |
174 } | |
175 | |
176 /** | |
177 * {@inheritdoc} | |
178 */ | |
179 protected function doTestAuthoringInfo() { | |
180 $storage = $this->container->get('entity_type.manager') | |
181 ->getStorage($this->entityTypeId); | |
182 $storage->resetCache([$this->entityId]); | |
183 $entity = $storage->load($this->entityId); | |
184 $languages = $this->container->get('language_manager')->getLanguages(); | |
185 $values = []; | |
186 | |
187 // Post different base field information for each translation. | |
188 foreach ($this->langcodes as $langcode) { | |
189 $user = $this->drupalCreateUser(); | |
190 $values[$langcode] = [ | |
191 'uid' => $user->id(), | |
192 'created' => REQUEST_TIME - mt_rand(0, 1000), | |
193 'sticky' => (bool) mt_rand(0, 1), | |
194 'promote' => (bool) mt_rand(0, 1), | |
195 ]; | |
196 $edit = [ | |
197 'uid[0][target_id]' => $user->getUsername(), | |
198 'created[0][value][date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'), | |
199 'created[0][value][time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'), | |
200 'sticky[value]' => $values[$langcode]['sticky'], | |
201 'promote[value]' => $values[$langcode]['promote'], | |
202 ]; | |
203 $options = ['language' => $languages[$langcode]]; | |
204 $url = $entity->urlInfo('edit-form', $options); | |
205 $this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options); | |
206 } | |
207 | |
208 $storage->resetCache([$this->entityId]); | |
209 $entity = $storage->load($this->entityId); | |
210 foreach ($this->langcodes as $langcode) { | |
211 $translation = $entity->getTranslation($langcode); | |
212 $metadata = $this->manager->getTranslationMetadata($translation); | |
213 $this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.'); | |
214 $this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.'); | |
215 $this->assertEqual($translation->isSticky(), $values[$langcode]['sticky'], 'Sticky of Translation correctly stored.'); | |
216 $this->assertEqual($translation->isPromoted(), $values[$langcode]['promote'], 'Promoted of Translation correctly stored.'); | |
217 } | |
218 } | |
219 | |
220 /** | |
221 * Tests that translation page inherits admin status of edit page. | |
222 */ | |
223 public function testTranslationLinkTheme() { | |
224 $this->drupalLogin($this->administrator); | |
225 $article = $this->drupalCreateNode(['type' => 'article', 'langcode' => $this->langcodes[0]]); | |
226 | |
227 // Set up Seven as the admin theme and use it for node editing. | |
228 $this->container->get('theme_handler')->install(['seven']); | |
229 $edit = []; | |
230 $edit['admin_theme'] = 'seven'; | |
231 $edit['use_admin_theme'] = TRUE; | |
232 $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); | |
233 $this->drupalGet('node/' . $article->id() . '/translations'); | |
234 $this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme if edit is admin.'); | |
235 | |
236 // Turn off admin theme for editing, assert inheritance to translations. | |
237 $edit['use_admin_theme'] = FALSE; | |
238 $this->drupalPostForm('admin/appearance', $edit, t('Save configuration')); | |
239 $this->drupalGet('node/' . $article->id() . '/translations'); | |
240 $this->assertNoRaw('core/themes/seven/css/base/elements.css', 'Translation uses frontend theme if edit is frontend.'); | |
241 | |
242 // Assert presence of translation page itself (vs. DisabledBundle below). | |
243 $this->assertResponse(200); | |
244 } | |
245 | |
246 /** | |
247 * Tests that no metadata is stored for a disabled bundle. | |
248 */ | |
249 public function testDisabledBundle() { | |
250 // Create a bundle that does not have translation enabled. | |
251 $disabledBundle = $this->randomMachineName(); | |
252 $this->drupalCreateContentType(['type' => $disabledBundle, 'name' => $disabledBundle]); | |
253 | |
254 // Create a node for each bundle. | |
255 $node = $this->drupalCreateNode([ | |
256 'type' => $this->bundle, | |
257 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, | |
258 ]); | |
259 | |
260 // Make sure that nothing was inserted into the {content_translation} table. | |
261 $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(); | |
262 $this->assertEqual(0, count($rows)); | |
263 | |
264 // Ensure the translation tab is not accessible. | |
265 $this->drupalGet('node/' . $node->id() . '/translations'); | |
266 $this->assertResponse(403); | |
267 } | |
268 | |
269 /** | |
270 * Tests that translations are rendered properly. | |
271 */ | |
272 public function testTranslationRendering() { | |
273 $default_langcode = $this->langcodes[0]; | |
274 $values[$default_langcode] = $this->getNewEntityValues($default_langcode); | |
275 $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode); | |
276 $node = \Drupal::entityManager()->getStorage($this->entityTypeId)->load($this->entityId); | |
277 $node->setPromoted(TRUE); | |
278 | |
279 // Create translations. | |
280 foreach (array_diff($this->langcodes, [$default_langcode]) as $langcode) { | |
281 $values[$langcode] = $this->getNewEntityValues($langcode); | |
282 $translation = $node->addTranslation($langcode, $values[$langcode]); | |
283 // Publish and promote the translation to frontpage. | |
284 $translation->setPromoted(TRUE); | |
285 $translation->setPublished(TRUE); | |
286 } | |
287 $node->save(); | |
288 | |
289 // Test that the frontpage view displays the correct translations. | |
290 \Drupal::service('module_installer')->install(['views'], TRUE); | |
291 $this->rebuildContainer(); | |
292 $this->doTestTranslations('node', $values); | |
293 | |
294 // Enable the translation language renderer. | |
295 $view = \Drupal::entityManager()->getStorage('view')->load('frontpage'); | |
296 $display = &$view->getDisplay('default'); | |
297 $display['display_options']['rendering_language'] = '***LANGUAGE_entity_translation***'; | |
298 $view->save(); | |
299 | |
300 // Need to check from the beginning, including the base_path, in the url | |
301 // since the pattern for the default language might be a substring of | |
302 // the strings for other languages. | |
303 $base_path = base_path(); | |
304 | |
305 // Check the frontpage for 'Read more' links to each translation. | |
306 // See also assertTaxonomyPage() in NodeAccessBaseTableTest. | |
307 $node_href = 'node/' . $node->id(); | |
308 foreach ($this->langcodes as $langcode) { | |
309 $this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]); | |
310 $num_match_found = 0; | |
311 if ($langcode == 'en') { | |
312 // Site default language does not have langcode prefix in the URL. | |
313 $expected_href = $base_path . $node_href; | |
314 } | |
315 else { | |
316 $expected_href = $base_path . $langcode . '/' . $node_href; | |
317 } | |
318 $pattern = '|^' . $expected_href . '$|'; | |
319 foreach ($this->xpath("//a[text()='Read more']") as $link) { | |
320 if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) { | |
321 $num_match_found++; | |
322 } | |
323 } | |
324 $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 . '.)'); | |
325 } | |
326 | |
327 // Check the frontpage for 'Add new comment' links that include the | |
328 // language. | |
329 $comment_form_href = 'node/' . $node->id() . '#comment-form'; | |
330 foreach ($this->langcodes as $langcode) { | |
331 $this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]); | |
332 $num_match_found = 0; | |
333 if ($langcode == 'en') { | |
334 // Site default language does not have langcode prefix in the URL. | |
335 $expected_href = $base_path . $comment_form_href; | |
336 } | |
337 else { | |
338 $expected_href = $base_path . $langcode . '/' . $comment_form_href; | |
339 } | |
340 $pattern = '|^' . $expected_href . '$|'; | |
341 foreach ($this->xpath("//a[text()='Add new comment']") as $link) { | |
342 if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) { | |
343 $num_match_found++; | |
344 } | |
345 } | |
346 $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 . '.)'); | |
347 } | |
348 | |
349 // Test that the node page displays the correct translations. | |
350 $this->doTestTranslations('node/' . $node->id(), $values); | |
351 | |
352 // Test that the node page has the correct alternate hreflang links. | |
353 $this->doTestAlternateHreflangLinks($node->urlInfo()); | |
354 } | |
355 | |
356 /** | |
357 * Tests that the given path displays the correct translation values. | |
358 * | |
359 * @param string $path | |
360 * The path to be tested. | |
361 * @param array $values | |
362 * The translation values to be found. | |
363 */ | |
364 protected function doTestTranslations($path, array $values) { | |
365 $languages = $this->container->get('language_manager')->getLanguages(); | |
366 foreach ($this->langcodes as $langcode) { | |
367 $this->drupalGet($path, ['language' => $languages[$langcode]]); | |
368 $this->assertText($values[$langcode]['title'][0]['value'], format_string('The %langcode node translation is correctly displayed.', ['%langcode' => $langcode])); | |
369 } | |
370 } | |
371 | |
372 /** | |
373 * Tests that the given path provides the correct alternate hreflang links. | |
374 * | |
375 * @param \Drupal\Core\Url $url | |
376 * The path to be tested. | |
377 */ | |
378 protected function doTestAlternateHreflangLinks(Url $url) { | |
379 $languages = $this->container->get('language_manager')->getLanguages(); | |
380 $url->setAbsolute(); | |
381 $urls = []; | |
382 foreach ($this->langcodes as $langcode) { | |
383 $language_url = clone $url; | |
384 $urls[$langcode] = $language_url->setOption('language', $languages[$langcode]); | |
385 } | |
386 foreach ($this->langcodes as $langcode) { | |
387 $this->drupalGet($urls[$langcode]); | |
388 foreach ($urls as $alternate_langcode => $language_url) { | |
389 // Retrieve desired link elements from the HTML head. | |
390 $links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]', | |
391 [':href' => $language_url->toString(), ':hreflang' => $alternate_langcode]); | |
392 $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()])); | |
393 } | |
394 } | |
395 } | |
396 | |
397 /** | |
398 * {@inheritdoc} | |
399 */ | |
400 protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) { | |
401 if (!$entity->isNew() && $entity->isTranslatable()) { | |
402 $translations = $entity->getTranslationLanguages(); | |
403 if ((count($translations) > 1 || !isset($translations[$langcode])) && ($field = $entity->getFieldDefinition('status'))) { | |
404 return ' ' . ($field->isTranslatable() ? t('(this translation)') : t('(all translations)')); | |
405 } | |
406 } | |
407 return ''; | |
408 } | |
409 | |
410 /** | |
411 * Tests uninstalling content_translation. | |
412 */ | |
413 protected function doUninstallTest() { | |
414 // Delete all the nodes so there is no data. | |
415 $nodes = Node::loadMultiple(); | |
416 foreach ($nodes as $node) { | |
417 $node->delete(); | |
418 } | |
419 $language_count = count(\Drupal::configFactory()->listAll('language.content_settings.')); | |
420 \Drupal::service('module_installer')->uninstall(['content_translation']); | |
421 $this->rebuildContainer(); | |
422 $this->assertEqual($language_count, count(\Drupal::configFactory()->listAll('language.content_settings.')), 'Languages have been fixed rather than deleted during content_translation uninstall.'); | |
423 } | |
424 | |
425 /** | |
426 * {@inheritdoc} | |
427 */ | |
428 protected function doTestTranslationEdit() { | |
429 $storage = $this->container->get('entity_type.manager') | |
430 ->getStorage($this->entityTypeId); | |
431 $storage->resetCache([$this->entityId]); | |
432 $entity = $storage->load($this->entityId); | |
433 $languages = $this->container->get('language_manager')->getLanguages(); | |
434 $type_name = node_get_type_label($entity); | |
435 | |
436 foreach ($this->langcodes as $langcode) { | |
437 // We only want to test the title for non-english translations. | |
438 if ($langcode != 'en') { | |
439 $options = ['language' => $languages[$langcode]]; | |
440 $url = $entity->urlInfo('edit-form', $options); | |
441 $this->drupalGet($url); | |
442 | |
443 $title = t('<em>Edit @type</em> @title [%language translation]', [ | |
444 '@type' => $type_name, | |
445 '@title' => $entity->getTranslation($langcode)->label(), | |
446 '%language' => $languages[$langcode]->getName(), | |
447 ]); | |
448 $this->assertRaw($title); | |
449 } | |
450 } | |
451 } | |
452 | |
453 /** | |
454 * Tests that revision translations are rendered properly. | |
455 */ | |
456 public function testRevisionTranslationRendering() { | |
457 $storage = \Drupal::entityTypeManager()->getStorage('node'); | |
458 | |
459 // Create a node. | |
460 $nid = $this->createEntity(['title' => 'First rev en title'], 'en'); | |
461 $node = $storage->load($nid); | |
462 $original_revision_id = $node->getRevisionId(); | |
463 | |
464 // Add a French translation. | |
465 $translation = $node->addTranslation('fr'); | |
466 $translation->title = 'First rev fr title'; | |
467 $translation->setNewRevision(FALSE); | |
468 $translation->save(); | |
469 | |
470 // Create a new revision. | |
471 $node->title = 'Second rev en title'; | |
472 $node->setNewRevision(TRUE); | |
473 $node->save(); | |
474 | |
475 // Get an English view of this revision. | |
476 $original_revision = $storage->loadRevision($original_revision_id); | |
477 $original_revision_url = $original_revision->toUrl('revision')->toString(); | |
478 | |
479 // Should be different from regular node URL. | |
480 $this->assertNotIdentical($original_revision_url, $original_revision->toUrl()->toString()); | |
481 $this->drupalGet($original_revision_url); | |
482 $this->assertResponse(200); | |
483 | |
484 // Contents should be in English, of correct revision. | |
485 $this->assertText('First rev en title'); | |
486 $this->assertNoText('First rev fr title'); | |
487 | |
488 // Get a French view. | |
489 $url_fr = $original_revision->getTranslation('fr')->toUrl('revision')->toString(); | |
490 | |
491 // Should have different URL from English. | |
492 $this->assertNotIdentical($url_fr, $original_revision->toUrl()->toString()); | |
493 $this->assertNotIdentical($url_fr, $original_revision_url); | |
494 $this->drupalGet($url_fr); | |
495 $this->assertResponse(200); | |
496 | |
497 // Contents should be in French, of correct revision. | |
498 $this->assertText('First rev fr title'); | |
499 $this->assertNoText('First rev en title'); | |
500 } | |
501 | |
502 } |