first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
# Attach to file settings for testing. The base route does not matter.
system.file_system_settings:
title: 'Test config translation'
base_route_name: system.file_system_settings
names:
- config_translation_test.content

View File

@@ -0,0 +1,13 @@
name: 'Configuration Translation Test'
description: 'Helpers to test the configuration translation system'
type: module
package: Testing
# version: VERSION
dependencies:
- drupal:config_translation
- drupal:config_test
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222

View File

@@ -0,0 +1,7 @@
# Add a default local task for the file system settings page, so that the local
# task added by Configuration Translation becomes visible. This facilitates
# manual testing.
system.file_system_settings:
route_name: system.file_system_settings
title: Settings
base_route: system.file_system_settings

View File

@@ -0,0 +1,85 @@
<?php
/**
* @file
* Configuration Translation Test module.
*/
use Drupal\Core\Extension\Extension;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_system_info_alter().
*/
function config_translation_test_system_info_alter(array &$info, Extension $file, $type) {
// @see \Drupal\config_translation\Tests\ConfigTranslationUiThemeTest
if ($file->getType() == 'theme' && $file->getName() == 'config_translation_test_theme') {
$info['hidden'] = FALSE;
}
}
/**
* Implements hook_entity_type_alter().
*/
function config_translation_test_entity_type_alter(array &$entity_types) {
// Remove entity definition for these entity types from config_test module.
unset($entity_types['config_test_no_status']);
unset($entity_types['config_query_test']);
}
/**
* Implements hook_config_translation_info_alter().
*/
function config_translation_test_config_translation_info_alter(&$info) {
if (\Drupal::state()->get('config_translation_test_config_translation_info_alter')) {
// Limit account settings config files to only one of them.
$info['entity.user.admin_form']['names'] = ['user.settings'];
// Add one more config file to the site information page.
$info['system.site_information_settings']['names'][] = 'system.rss';
}
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for ConfigTranslationFormBase.
*
* Adds a list of configuration names to the top of the configuration
* translation form.
*
* @see \Drupal\config_translation\Form\ConfigTranslationFormBase
*/
function config_translation_test_form_config_translation_form_alter(&$form, FormStateInterface $form_state) {
if (\Drupal::state()->get('config_translation_test_alter_form_alter')) {
$form['#base_altered'] = TRUE;
}
}
/**
* Implements hook_form_FORM_ID_alter() for ConfigTranslationAddForm.
*
* Changes the title to include the source language.
*
* @see \Drupal\config_translation\Form\ConfigTranslationAddForm
*/
function config_translation_test_form_config_translation_add_form_alter(&$form, FormStateInterface $form_state) {
if (\Drupal::state()->get('config_translation_test_alter_form_alter')) {
$form['#altered'] = TRUE;
}
}
/**
* Implements hook_form_FORM_ID_alter() for ConfigTranslationEditForm.
*
* Adds a column to the configuration translation edit form that shows the
* current translation. Note that this column would not be displayed by default,
* as the columns are hardcoded in
* config_translation_manage_form_element.html.twig. The template would need to
* be overridden for the column to be displayed.
*
* @see \Drupal\config_translation\Form\ConfigTranslationEditForm
*/
function config_translation_test_form_config_translation_edit_form_alter(&$form, FormStateInterface $form_state) {
if (\Drupal::state()->get('config_translation_test_alter_form_alter')) {
$form['#altered'] = TRUE;
}
}

View File

@@ -0,0 +1,5 @@
services:
_defaults:
autoconfigure: true
config_translation_test_event_subscriber:
class: Drupal\config_translation_test\EventSubscriber\ConfigTranslationTestSubscriber

View File

@@ -0,0 +1,38 @@
<?php
namespace Drupal\config_translation_test\EventSubscriber;
use Drupal\config_translation\Event\ConfigMapperPopulateEvent;
use Drupal\config_translation\Event\ConfigTranslationEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Adds configuration names to configuration mapper on POPULATE_MAPPER event.
*/
class ConfigTranslationTestSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
ConfigTranslationEvents::POPULATE_MAPPER => [
['addConfigNames'],
],
];
}
/**
* Reacts to the populating of a configuration mapper.
*
* @param \Drupal\config_translation\Event\ConfigMapperPopulateEvent $event
* The configuration mapper event.
*/
public function addConfigNames(ConfigMapperPopulateEvent $event) {
$mapper = $event->getMapper();
if ($mapper->getBaseRouteName() === 'system.site_information_settings' && $mapper->getLangcode() === 'en') {
$mapper->addConfigName('config_translation_test.content');
}
}
}

View File

@@ -0,0 +1,188 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\filter\Entity\FilterFormat;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Translate settings and entities to various languages.
*
* @group config_translation
*/
class ConfigTranslationCacheTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'block',
'config_translation',
'config_translation_test',
'contact',
'contact_test',
'contextual',
'entity_test',
'field_test',
'field_ui',
'filter',
'filter_test',
'node',
'views',
'views_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Languages to enable.
*
* @var array
*/
protected $langcodes = ['fr', 'ta'];
/**
* Administrator user for tests.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* Translator user for tests.
*
* @var \Drupal\user\UserInterface
*/
protected $translatorUser;
/**
* String translation storage object.
*
* @var \Drupal\locale\StringStorageInterface
*/
protected $localeStorage;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$translator_permissions = [
'translate configuration',
];
/** @var \Drupal\filter\FilterFormatInterface $filter_test_format */
$filter_test_format = FilterFormat::load('filter_test');
/** @var \Drupal\filter\FilterFormatInterface $filtered_html_format */
$filtered_html_format = FilterFormat::load('filtered_html');
/** @var \Drupal\filter\FilterFormatInterface $full_html_format */
$full_html_format = FilterFormat::load('full_html');
$admin_permissions = array_merge($translator_permissions, [
'administer languages',
'administer site configuration',
'link to any page',
'administer contact forms',
'administer filters',
$filtered_html_format->getPermissionName(),
$full_html_format->getPermissionName(),
$filter_test_format->getPermissionName(),
'access site-wide contact form',
'access contextual links',
'administer account settings',
'administer themes',
'bypass node access',
'administer content types',
'translate interface',
'administer entity_test fields',
]);
// Create and login user.
$this->translatorUser = $this->drupalCreateUser($translator_permissions);
$this->adminUser = $this->drupalCreateUser($admin_permissions);
// Add languages.
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('page_title_block');
}
/**
* Tests the translation of field and field storage configuration.
*/
public function testFieldConfigTranslation(): void {
// Add a test field which has a translatable field setting and a
// translatable field storage setting.
$field_name = $this->randomMachineName();
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'test_field',
]);
$translatable_storage_setting = $this->randomString();
$field_storage->setSetting('translatable_storage_setting', $translatable_storage_setting);
$field_storage->save();
$bundle = $this->randomMachineName();
entity_test_create_bundle($bundle);
$field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'bundle' => $bundle,
]);
$translatable_field_setting = $this->randomString();
$field->setSetting('translatable_field_setting', $translatable_field_setting);
$field->save();
$this->drupalLogin($this->translatorUser);
$this->drupalGet("/entity_test/structure/$bundle/fields/entity_test.$bundle.$field_name/translate");
$this->clickLink('Add');
$this->assertSession()->pageTextContains('Translatable field setting');
$this->assertSession()->assertEscaped($translatable_field_setting);
$this->assertSession()->pageTextContains('Translatable storage setting');
$this->assertSession()->assertEscaped($translatable_storage_setting);
// Add translation for label.
$field_label_fr = $this->randomString();
$edit = [
"translation[config_names][field.field.entity_test.$bundle.$field_name][label]" => $field_label_fr,
];
$this->submitForm($edit, 'Save translation');
$this->drupalLogout();
// Check if the translated label appears.
$this->drupalLogin($this->adminUser);
$this->drupalGet("/fr/entity_test/structure/$bundle/fields");
$this->assertSession()->assertEscaped($field_label_fr);
// Clear cache on French version and check for translated label.
$this->drupalGet('/fr/admin/config/development/performance');
$this->submitForm([], 'Clear all caches');
$this->drupalGet("/fr/entity_test/structure/$bundle/fields");
// Check if the translation is still there.
$this->assertSession()->assertEscaped($field_label_fr);
// Clear cache on default version and check for translated label.
$this->drupalGet('/admin/config/development/performance');
$this->submitForm([], 'Clear all caches');
$this->drupalGet("/fr/entity_test/structure/$bundle/fields");
// Check if the translation is still there.
$this->assertSession()->assertEscaped($field_label_fr);
}
}

View File

@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Tests the content translation behaviors on date formats.
*
* @group config_translation
*/
class ConfigTranslationDateFormatUiTest extends BrowserTestBase {
protected static $modules = [
'language',
'config_translation',
'system',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Enable additional languages.
$langcodes = ['de', 'es'];
foreach ($langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$user = $this->drupalCreateUser([
'administer site configuration',
'translate configuration',
]);
$this->drupalLogin($user);
}
/**
* Tests date format translation behavior.
*/
public function testDateFormatUI(): void {
$this->drupalGet('admin/config/regional/date-time');
// Assert translation link unlocked date format.
$this->assertSession()->linkByHrefExists('admin/config/regional/date-time/formats/manage/medium/translate');
// Assert translation link locked date format.
$this->assertSession()->linkByHrefExists('admin/config/regional/date-time/formats/manage/html_datetime/translate');
// Date pattern is visible on unlocked date formats.
$this->drupalGet('admin/config/regional/date-time/formats/manage/medium/translate/de/add');
$this->assertSession()->fieldExists('translation[config_names][core.date_format.medium][pattern]');
// Date pattern is not visible on locked date formats.
$this->drupalGet('admin/config/regional/date-time/formats/manage/html_datetime/translate/es/add');
$this->assertSession()->fieldNotExists('translation[config_names][core.date_format.html_datetime][pattern]');
}
/**
* Tests date format translation.
*/
public function testDateFormatTranslation(): void {
$this->drupalGet('admin/config/regional/date-time');
// Check for medium format.
$this->assertSession()->linkByHrefExists('admin/config/regional/date-time/formats/manage/medium');
// Save default language configuration for a new format.
$edit = [
'label' => 'Custom medium date',
'id' => 'custom_medium',
'date_format_pattern' => 'Y. m. d. H:i',
];
$this->drupalGet('admin/config/regional/date-time/formats/add');
$this->submitForm($edit, 'Add format');
// Test translating a default shipped format and our custom format.
$formats = [
'medium' => 'Default medium date',
'custom_medium' => 'Custom medium date',
];
foreach ($formats as $id => $label) {
$translation_base_url = 'admin/config/regional/date-time/formats/manage/' . $id . '/translate';
$this->drupalGet($translation_base_url);
// 'Add' link should be present for German translation.
$translation_page_url = "$translation_base_url/de/add";
$this->assertSession()->linkByHrefExists($translation_page_url);
// Make sure original text is present on this page.
$this->drupalGet($translation_page_url);
$this->assertSession()->pageTextContains($label);
// Make sure that the date library is added.
$this->assertSession()->responseContains('core/modules/system/js/system.date.js');
// Update translatable fields.
$edit = [
'translation[config_names][core.date_format.' . $id . '][label]' => $id . ' - DE',
'translation[config_names][core.date_format.' . $id . '][pattern]' => 'D',
];
// Save language specific version of form.
$this->drupalGet($translation_page_url);
$this->submitForm($edit, 'Save translation');
// Get translation and check we've got the right value.
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'core.date_format.' . $id);
$expected = [
'label' => $id . ' - DE',
'pattern' => 'D',
];
$this->assertEquals($expected, $override->get());
// Formatting the date 8 / 27 / 1985 @ 13:37 EST with pattern D should
// display "Tue".
$formatted_date = $this->container->get('date.formatter')->format(494015820, $id, NULL, 'America/New_York', 'de');
$this->assertEquals('Tue', $formatted_date, 'Got the right formatted date using the date format translation pattern.');
}
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\FunctionalTests\Installer\InstallerTestBase;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
/**
* Installs the config translation module on a site installed in non english.
*
* @group config_translation
*/
class ConfigTranslationInstallTest extends InstallerTestBase {
use ContentTypeCreationTrait;
/**
* {@inheritdoc}
*/
protected $langcode = 'eo';
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUpLanguage() {
// Place custom local translations in the translations directory.
mkdir(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.eo.po', $this->getPo('eo'));
parent::setUpLanguage();
$this->translations['Save and continue'] = 'Save and continue eo';
}
/**
* Returns the string for the test .po file.
*
* @param string $langcode
* The language code.
*
* @return string
* Contents for the test .po file.
*/
protected function getPo($langcode) {
return <<<PO
msgid ""
msgstr ""
msgid "Save and continue"
msgstr "Save and continue $langcode"
msgid "Anonymous"
msgstr "Anonymous $langcode"
msgid "Language"
msgstr "Language $langcode"
PO;
}
public function testConfigTranslation(): void {
\Drupal::service('module_installer')->install(['node', 'field_ui']);
$this->createContentType(['type' => 'article']);
$this->drupalGet('admin/config/regional/language/add');
$this->submitForm(['predefined_langcode' => 'en'], 'Add custom language');
$this->drupalGet('admin/config/regional/language/add');
$this->submitForm(['predefined_langcode' => 'fr'], 'Add custom language');
$edit = [
'modules[config_translation][enable]' => TRUE,
];
$this->drupalGet('admin/modules');
$this->submitForm($edit, 'Install');
$this->drupalGet('/admin/structure/types/manage/article/fields');
$this->assertSession()->statusCodeEquals(200);
}
}

View File

@@ -0,0 +1,525 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
use Drupal\shortcut\Entity\ShortcutSet;
use Drupal\contact\Entity\ContactForm;
use Drupal\filter\Entity\FilterFormat;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Visit all lists.
*
* @group config_translation
* @see \Drupal\config_translation\Tests\ConfigTranslationViewListUiTest
*/
class ConfigTranslationListUiTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'block',
'config_translation',
'contact',
'block_content',
'field',
'field_ui',
'menu_ui',
'node',
'shortcut',
'taxonomy',
'image',
'responsive_image',
'toolbar',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Admin user with all needed permissions.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$permissions = [
'access site-wide contact form',
'administer blocks',
'administer block content',
'administer block types',
'access block library',
'administer contact forms',
'administer content types',
'administer block_content fields',
'administer filters',
'administer menu',
'administer node fields',
'administer permissions',
'administer shortcuts',
'administer site configuration',
'administer taxonomy',
'administer account settings',
'administer languages',
'administer image styles',
'administer responsive images',
'translate configuration',
];
// Create and log in user.
$this->adminUser = $this->drupalCreateUser($permissions);
$this->drupalLogin($this->adminUser);
// Enable import of translations. By default this is disabled for automated
// tests.
$this->config('locale.settings')
->set('translation.import_enabled', TRUE)
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
->save();
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Tests the block listing for the translate operation.
*
* There are no blocks placed in the testing profile. Add one, then check
* for Translate operation.
*/
protected function doBlockListTest() {
// Add a test block, any block will do.
// Set the machine name so the translate link can be built later.
$id = $this->randomMachineName(16);
$this->drupalPlaceBlock('system_powered_by_block', ['id' => $id]);
// Get the Block listing.
$this->drupalGet('admin/structure/block');
$translate_link = 'admin/structure/block/manage/' . $id . '/translate';
// Test if the link to translate the block is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the menu listing for the translate operation.
*/
protected function doMenuListTest() {
// Create a test menu to decouple looking for translate operations link so
// this does not test more than necessary.
$this->drupalGet('admin/structure/menu/add');
// Lowercase the machine name.
$menu_name = $this->randomMachineName(16);
$label = $this->randomMachineName(16);
$edit = [
'id' => $menu_name,
'description' => '',
'label' => $label,
];
// Create the menu by posting the form.
$this->drupalGet('admin/structure/menu/add');
$this->submitForm($edit, 'Save');
// Get the Menu listing.
$this->drupalGet('admin/structure/menu');
$translate_link = 'admin/structure/menu/manage/' . $menu_name . '/translate';
// Test if the link to translate the menu is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Check if the Link is not added if you are missing 'translate
// configuration' permission.
$permissions = [
'administer menu',
];
$this->drupalLogin($this->drupalCreateUser($permissions));
// Get the Menu listing.
$this->drupalGet('admin/structure/menu');
$translate_link = 'admin/structure/menu/manage/' . $menu_name . '/translate';
// Test if the link to translate the menu is NOT on the page.
$this->assertSession()->linkByHrefNotExists($translate_link);
// Log in as Admin again otherwise the rest will fail.
$this->drupalLogin($this->adminUser);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the vocabulary listing for the translate operation.
*/
protected function doVocabularyListTest() {
// Create a test vocabulary to decouple looking for translate operations
// link so this does not test more than necessary.
$vocabulary = Vocabulary::create([
'name' => $this->randomMachineName(),
'description' => $this->randomMachineName(),
'vid' => $this->randomMachineName(),
]);
$vocabulary->save();
// Get the Taxonomy listing.
$this->drupalGet('admin/structure/taxonomy');
$translate_link = 'admin/structure/taxonomy/manage/' . $vocabulary->id() . '/translate';
// Test if the link to translate the vocabulary is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
// Test if the local task for translation is on this page.
$this->assertSession()->linkExists('Translate taxonomy vocabulary');
$local_task_url = parse_url($this->getSession()->getPage()->findLink('Translate taxonomy vocabulary')->getAttribute('href'));
$this->assertSame(base_path() . $translate_link, $local_task_url['path']);
}
/**
* Tests the content block listing for the translate operation.
*/
public function doCustomContentTypeListTest() {
// Create a test block type to decouple looking for translate
// operations link so this does not test more than necessary.
$block_content_type = BlockContentType::create([
'id' => $this->randomMachineName(16),
'label' => $this->randomMachineName(),
'revision' => FALSE,
]);
$block_content_type->save();
// Get the block type listing.
$this->drupalGet('admin/structure/block-content');
$translate_link = 'admin/structure/block-content/manage/' . $block_content_type->id() . '/translate';
// Test if the link to translate the block type is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the contact forms listing for the translate operation.
*/
public function doContactFormsListTest() {
// Create a test contact form to decouple looking for translate operations
// link so this does not test more than necessary.
$contact_form = ContactForm::create([
'id' => $this->randomMachineName(16),
'label' => $this->randomMachineName(),
]);
$contact_form->save();
// Get the contact form listing.
$this->drupalGet('admin/structure/contact');
$translate_link = 'admin/structure/contact/manage/' . $contact_form->id() . '/translate';
// Test if the link to translate the contact form is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the content type listing for the translate operation.
*/
public function doContentTypeListTest() {
// Create a test content type to decouple looking for translate operations
// link so this does not test more than necessary.
$content_type = $this->drupalCreateContentType([
'type' => $this->randomMachineName(16),
'name' => $this->randomMachineName(),
]);
// Get the content type listing.
$this->drupalGet('admin/structure/types');
$translate_link = 'admin/structure/types/manage/' . $content_type->id() . '/translate';
// Test if the link to translate the content type is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the formats listing for the translate operation.
*/
public function doFormatsListTest() {
// Create a test format to decouple looking for translate operations
// link so this does not test more than necessary.
$filter_format = FilterFormat::create([
'format' => $this->randomMachineName(16),
'name' => $this->randomMachineName(),
]);
$filter_format->save();
// Get the format listing.
$this->drupalGet('admin/config/content/formats');
$translate_link = 'admin/config/content/formats/manage/' . $filter_format->id() . '/translate';
// Test if the link to translate the format is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the shortcut listing for the translate operation.
*/
public function doShortcutListTest() {
// Create a test shortcut to decouple looking for translate operations
// link so this does not test more than necessary.
$shortcut = ShortcutSet::create([
'id' => $this->randomMachineName(16),
'label' => $this->randomString(),
]);
$shortcut->save();
// Get the shortcut listing.
$this->drupalGet('admin/config/user-interface/shortcut');
$translate_link = 'admin/config/user-interface/shortcut/manage/' . $shortcut->id() . '/translate';
// Test if the link to translate the shortcut is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the role listing for the translate operation.
*/
public function doUserRoleListTest() {
// Create a test role to decouple looking for translate operations
// link so this does not test more than necessary.
$role_id = $this->randomMachineName(16);
$this->drupalCreateRole([], $role_id);
// Get the role listing.
$this->drupalGet('admin/people/roles');
$translate_link = 'admin/people/roles/manage/' . $role_id . '/translate';
// Test if the link to translate the role is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the language listing for the translate operation.
*/
public function doLanguageListTest() {
// Create a test language to decouple looking for translate operations
// link so this does not test more than necessary.
ConfigurableLanguage::createFromLangcode('ga')->save();
// Get the language listing.
$this->drupalGet('admin/config/regional/language');
$translate_link = 'admin/config/regional/language/edit/ga/translate';
// Test if the link to translate the language is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the image style listing for the translate operation.
*/
public function doImageStyleListTest() {
// Get the image style listing.
$this->drupalGet('admin/config/media/image-styles');
$translate_link = 'admin/config/media/image-styles/manage/medium/translate';
// Test if the link to translate the style is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the responsive image mapping listing for the translate operation.
*/
public function doResponsiveImageListTest() {
$edit = [];
$edit['label'] = $this->randomMachineName();
$edit['id'] = strtolower($edit['label']);
$edit['fallback_image_style'] = 'thumbnail';
$this->drupalGet('admin/config/media/responsive-image-style/add');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Responsive image style {$edit['label']} saved.");
// Get the responsive image style listing.
$this->drupalGet('admin/config/media/responsive-image-style');
$translate_link = 'admin/config/media/responsive-image-style/' . $edit['id'] . '/translate';
// Test if the link to translate the style is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests the field listing for the translate operation.
*/
public function doFieldListTest() {
// Create a base content type.
$content_type = $this->drupalCreateContentType([
'type' => $this->randomMachineName(16),
'name' => $this->randomMachineName(),
]);
// Create a block content type.
$block_content_type = BlockContentType::create([
'id' => 'basic',
'label' => 'Basic',
'revision' => FALSE,
]);
$block_content_type->save();
$field = FieldConfig::create([
// The field storage is guaranteed to exist because it is supplied by the
// block_content module.
'field_storage' => FieldStorageConfig::loadByName('block_content', 'body'),
'bundle' => $block_content_type->id(),
'label' => 'Body',
'settings' => [
'display_summary' => FALSE,
'allowed_formats' => [],
],
]);
$field->save();
// Look at a few fields on a few entity types.
$pages = [
[
'list' => 'admin/structure/types/manage/' . $content_type->id() . '/fields',
'field' => 'node.' . $content_type->id() . '.body',
],
[
'list' => 'admin/structure/block-content/manage/basic/fields',
'field' => 'block_content.basic.body',
],
];
foreach ($pages as $values) {
// Get fields listing.
$this->drupalGet($values['list']);
$translate_link = $values['list'] . '/' . $values['field'] . '/translate';
// Test if the link to translate the field is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
}
/**
* Tests the date format listing for the translate operation.
*/
public function doDateFormatListTest() {
// Get the date format listing.
$this->drupalGet('admin/config/regional/date-time');
$translate_link = 'admin/config/regional/date-time/formats/manage/long/translate';
// Test if the link to translate the format is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests a given settings page for the translate operation.
*
* @param string $link
* URL of the settings page to test.
*/
public function doSettingsPageTest($link) {
// Get the settings page.
$this->drupalGet($link);
$translate_link = $link . '/translate';
// Test if the link to translate the settings page is present.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
}
/**
* Tests if translate link is added to operations in all configuration lists.
*/
public function testTranslateOperationInListUi(): void {
// All lists based on paths provided by the module.
$this->doBlockListTest();
$this->doMenuListTest();
$this->doVocabularyListTest();
$this->doCustomContentTypeListTest();
$this->doContactFormsListTest();
$this->doContentTypeListTest();
$this->doFormatsListTest();
$this->doShortcutListTest();
$this->doUserRoleListTest();
$this->doLanguageListTest();
$this->doImageStyleListTest();
$this->doResponsiveImageListTest();
$this->doDateFormatListTest();
$this->doFieldListTest();
// Views is tested in Drupal\config_translation\Tests\ConfigTranslationViewListUiTest
// Test the maintenance settings page.
$this->doSettingsPageTest('admin/config/development/maintenance');
// Test the site information settings page.
$this->doSettingsPageTest('admin/config/system/site-information');
// Test the account settings page.
$this->doSettingsPageTest('admin/config/people/accounts');
}
}

View File

@@ -0,0 +1,211 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\Component\Utility\Html;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\BrowserTestBase;
/**
* Translate settings and entities to various languages.
*
* @group config_translation
*/
class ConfigTranslationOverviewTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'block',
'config_test',
'config_translation',
'config_translation_test',
'contact',
'contextual',
'entity_test_operation',
'field_ui',
'node',
'views',
'views_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Languages to enable.
*
* @var array
*/
protected $langcodes = ['fr', 'ta'];
/**
* String translation storage object.
*
* @var \Drupal\locale\StringStorageInterface
*/
protected $localeStorage;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$permissions = [
'translate configuration',
'administer languages',
'administer site configuration',
'administer contact forms',
'access site-wide contact form',
'access contextual links',
'administer views',
];
// Create and log in user.
$this->drupalLogin($this->drupalCreateUser($permissions));
// Add languages.
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$this->localeStorage = $this->container->get('locale.storage');
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('page_title_block');
}
/**
* Tests the config translation mapper page.
*/
public function testMapperListPage(): void {
$this->drupalGet('admin/config/regional/config-translation');
$this->assertSession()->linkByHrefExists('admin/config/regional/config-translation/config_test');
$this->assertSession()->linkByHrefExists('admin/config/people/accounts/translate');
// Make sure there is only a single operation for each dropbutton, either
// 'List' or 'Translate'.
foreach ($this->cssSelect('ul.dropbutton') as $i => $dropbutton) {
$this->assertCount(1, $dropbutton->findAll('xpath', 'li'));
$this->assertTrue(($dropbutton->getText() === 'Translate') || ($dropbutton->getText() === 'List'));
}
$labels = [
'&$nxd~i0',
'some "label" with quotes',
$this->randomString(),
];
$storage = \Drupal::entityTypeManager()->getStorage('config_test');
foreach ($labels as $label) {
$test_entity = $storage->create([
'id' => $this->randomMachineName(),
'label' => $label,
]);
$test_entity->save();
$base_url = 'admin/structure/config_test/manage/' . $test_entity->id();
$this->drupalGet('admin/config/regional/config-translation/config_test');
$this->assertSession()->linkByHrefExists($base_url . '/translate');
$this->assertSession()->assertEscaped($test_entity->label());
// Make sure there is only a single 'Translate' operation for each
// dropbutton.
foreach ($this->cssSelect('ul.dropbutton') as $i => $dropbutton) {
$this->assertCount(1, $dropbutton->findAll('xpath', 'li'));
$this->assertSame('Translate', $dropbutton->getText());
}
$entity_type = \Drupal::entityTypeManager()->getDefinition($test_entity->getEntityTypeId());
$this->drupalGet($base_url . '/translate');
$title = $test_entity->label() . ' ' . $entity_type->getSingularLabel();
$title = 'Translations for <em class="placeholder">' . Html::escape($title) . '</em>';
$this->assertSession()->responseContains($title);
$this->assertSession()->responseContains('<th>Language</th>');
$this->drupalGet($base_url);
$this->assertSession()->linkExists('Translate test configuration');
}
}
/**
* Tests availability of hidden entities in the translation overview.
*/
public function testHiddenEntities(): void {
// Hidden languages are only available to translate through the
// configuration translation listings.
$this->drupalGet('admin/config/regional/config-translation/configurable_language');
$this->assertSession()->pageTextContains('Not applicable');
$this->assertSession()->linkByHrefExists('admin/config/regional/language/edit/zxx/translate');
$this->assertSession()->pageTextContains('Not specified');
$this->assertSession()->linkByHrefExists('admin/config/regional/language/edit/und/translate');
// Hidden date formats are only available to translate through the
// configuration translation listings. Test a couple of them.
$this->drupalGet('admin/config/regional/config-translation/date_format');
$this->assertSession()->pageTextContains('HTML Date');
$this->assertSession()->linkByHrefExists('admin/config/regional/date-time/formats/manage/html_date/translate');
$this->assertSession()->pageTextContains('HTML Year');
$this->assertSession()->linkByHrefExists('admin/config/regional/date-time/formats/manage/html_year/translate');
}
/**
* Tests that overrides do not affect listing screens.
*/
public function testListingPageWithOverrides(): void {
$original_label = 'Default';
$overridden_label = 'Overridden label';
$config_test_storage = $this->container->get('entity_type.manager')->getStorage('config_test');
// Set up an override.
$settings['config']['config_test.dynamic.dotted.default']['label'] = (object) [
'value' => $overridden_label,
'required' => TRUE,
];
$this->writeSettings($settings);
// Test that the overridden label is loaded with the entity.
$this->assertEquals($overridden_label, $config_test_storage->load('dotted.default')->label());
// Test that the original label on the listing page is intact.
$this->drupalGet('admin/config/regional/config-translation/config_test');
$this->assertSession()->pageTextContains($original_label);
$this->assertSession()->pageTextNotContains($overridden_label);
}
/**
* Tests the field listing for the translate operation.
*/
public function testListingFieldsPage(): void {
// Create a content type.
$node_type = NodeType::create([
'type' => 'basic',
'name' => 'Basic',
]);
$node_type->save();
$field = FieldConfig::create([
// The field storage is guaranteed to exist because it is supplied by the
// node module.
'field_storage' => FieldStorageConfig::loadByName('node', 'body'),
'bundle' => $node_type->id(),
'label' => 'Body',
'settings' => ['display_summary' => FALSE],
]);
$field->save();
$this->drupalGet('admin/config/regional/config-translation/node_fields');
$this->assertSession()->pageTextContains('Body');
$this->assertSession()->pageTextContains('Basic');
$this->assertSession()->linkByHrefExists('admin/structure/types/manage/basic/fields/node.basic.body/translate');
}
}

View File

@@ -0,0 +1,440 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\Component\Utility\Html;
use Drupal\Core\Language\Language;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\NodeType;
// cspell:ignore testcontent tuvan
/**
* Translate settings and entities to various languages.
*
* @group config_translation
* @group #slow
*/
class ConfigTranslationUiModulesTest extends ConfigTranslationUiTestBase {
/**
* Tests the contact form translation.
*/
public function testContactConfigEntityTranslation(): void {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/structure/contact');
// Check for default contact form configuration entity from Contact module.
$this->assertSession()->linkByHrefExists('admin/structure/contact/manage/feedback');
// Save default language configuration.
$label = 'Send your feedback';
$edit = [
'label' => $label,
'recipients' => 'sales@example.com,support@example.com',
'reply' => 'Thank you for your mail',
];
$this->drupalGet('admin/structure/contact/manage/feedback');
$this->submitForm($edit, 'Save');
// Ensure translation link is present.
$translation_base_url = 'admin/structure/contact/manage/feedback/translate';
$this->assertSession()->linkByHrefExists($translation_base_url);
// Make sure translate tab is present.
$this->drupalGet('admin/structure/contact/manage/feedback');
$this->assertSession()->linkExists('Translate contact form');
// Visit the form to confirm the changes.
$this->drupalGet('contact/feedback');
$this->assertSession()->pageTextContains($label);
foreach ($this->langcodes as $langcode) {
$this->drupalGet($translation_base_url);
$this->assertSession()->linkExists('Translate contact form');
// 'Add' link should be present for $langcode translation.
$translation_page_url = "$translation_base_url/$langcode/add";
$this->assertSession()->linkByHrefExists($translation_page_url);
// Make sure original text is present on this page.
$this->drupalGet($translation_page_url);
$this->assertSession()->pageTextContains($label);
// Update translatable fields.
$edit = [
'translation[config_names][contact.form.feedback][label]' => 'Website feedback - ' . $langcode,
'translation[config_names][contact.form.feedback][reply]' => 'Thank you for your mail - ' . $langcode,
];
// Save language specific version of form.
$this->drupalGet($translation_page_url);
$this->submitForm($edit, 'Save translation');
// Expect translated values in language specific file.
$override = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'contact.form.feedback');
$expected = [
'label' => 'Website feedback - ' . $langcode,
'reply' => 'Thank you for your mail - ' . $langcode,
];
$this->assertEquals($expected, $override->get());
// Check for edit, delete links (and no 'add' link) for $langcode.
$this->assertSession()->linkByHrefNotExists("$translation_base_url/$langcode/add");
$this->assertSession()->linkByHrefExists("$translation_base_url/$langcode/edit");
$this->assertSession()->linkByHrefExists("$translation_base_url/$langcode/delete");
// Visit language specific version of form to check label.
$this->drupalGet($langcode . '/contact/feedback');
$this->assertSession()->pageTextContains('Website feedback - ' . $langcode);
// Submit feedback.
$edit = [
'subject[0][value]' => 'Test subject',
'message[0][value]' => 'Test message',
];
$this->submitForm($edit, 'Send message');
}
// Now that all language translations are present, check translation and
// original text all appear in any translated page on the translation
// forms.
foreach ($this->langcodes as $langcode) {
$langcode_prefixes = array_merge([''], $this->langcodes);
foreach ($langcode_prefixes as $langcode_prefix) {
$this->drupalGet(ltrim("$langcode_prefix/$translation_base_url/$langcode/edit", '/'));
$this->assertSession()->fieldValueEquals('translation[config_names][contact.form.feedback][label]', 'Website feedback - ' . $langcode);
$this->assertSession()->pageTextContains($label);
}
}
// We get all emails so no need to check inside the loop.
$captured_emails = $this->getMails();
// Check language specific auto reply text in email body.
foreach ($captured_emails as $email) {
if ($email['id'] == 'contact_page_autoreply') {
// Trim because we get an added newline for the body.
$this->assertEquals('Thank you for your mail - ' . $email['langcode'], trim($email['body']));
}
}
// Test that delete links work and operations perform properly.
foreach ($this->langcodes as $langcode) {
$language = \Drupal::languageManager()->getLanguage($langcode)->getName();
$this->drupalGet("$translation_base_url/$langcode/delete");
$this->assertSession()->pageTextContains("Are you sure you want to delete the $language translation of $label contact form?");
// Assert link back to list page to cancel delete is present.
$this->assertSession()->linkByHrefExists($translation_base_url);
$this->submitForm([], 'Delete');
$this->assertSession()->pageTextContains("$language translation of $label contact form was deleted");
$this->assertSession()->linkByHrefExists("$translation_base_url/$langcode/add");
$this->assertSession()->linkByHrefNotExists("translation_base_url/$langcode/edit");
$this->assertSession()->linkByHrefNotExists("$translation_base_url/$langcode/delete");
// Expect no language specific file present anymore.
$override = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'contact.form.feedback');
$this->assertTrue($override->isNew());
}
// Check configuration page with translator user. Should have no access.
$this->drupalLogout();
$this->drupalLogin($this->translatorUser);
$this->drupalGet('admin/structure/contact/manage/feedback');
$this->assertSession()->statusCodeEquals(403);
// While translator can access the translation page, the edit link is not
// present due to lack of permissions.
$this->drupalGet($translation_base_url);
$this->assertSession()->linkNotExists('Edit');
// Check 'Add' link for French.
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/add");
}
/**
* Tests the views translation interface.
*/
public function testViewsTranslationUI(): void {
$this->drupalLogin($this->adminUser);
$description = 'All content promoted to the front page.';
$human_readable_name = 'Frontpage';
$display_settings_default = 'Default';
$display_options_default = '(Empty)';
$translation_base_url = 'admin/structure/views/view/frontpage/translate';
$this->drupalGet($translation_base_url);
// Check 'Add' link of French to visit add page.
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/add");
$this->clickLink('Add');
// Make sure original text is present on this page.
$this->assertSession()->pageTextContains($description);
$this->assertSession()->pageTextContains($human_readable_name);
// Update Views Fields for French.
$edit = [
'translation[config_names][views.view.frontpage][description]' => $description . " FR",
'translation[config_names][views.view.frontpage][label]' => $human_readable_name . " FR",
'translation[config_names][views.view.frontpage][display][default][display_title]' => $display_settings_default . " FR",
'translation[config_names][views.view.frontpage][display][default][display_options][title]' => $display_options_default . " FR",
];
$this->drupalGet("{$translation_base_url}/fr/add");
$this->submitForm($edit, 'Save translation');
$this->assertSession()->pageTextContains('Successfully saved French translation.');
// Check for edit, delete links (and no 'add' link) for French language.
$this->assertSession()->linkByHrefNotExists("$translation_base_url/fr/add");
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/edit");
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/delete");
// Check translation saved proper.
$this->drupalGet("$translation_base_url/fr/edit");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.frontpage][description]', $description . " FR");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.frontpage][label]', $human_readable_name . " FR");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.frontpage][display][default][display_title]', $display_settings_default . " FR");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.frontpage][display][default][display_options][title]', $display_options_default . " FR");
}
/**
* Tests the translation of field and field storage configuration.
*/
public function testFieldConfigTranslation(): void {
// Add a test field which has a translatable field setting and a
// translatable field storage setting.
$field_name = $this->randomMachineName();
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'test_field',
]);
$translatable_storage_setting = $this->randomString();
$field_storage->setSetting('translatable_storage_setting', $translatable_storage_setting);
$field_storage->save();
$bundle = $this->randomMachineName();
entity_test_create_bundle($bundle);
$field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'bundle' => $bundle,
]);
$translatable_field_setting = $this->randomString();
$field->setSetting('translatable_field_setting', $translatable_field_setting);
$field->save();
$this->drupalLogin($this->translatorUser);
$this->drupalGet("/entity_test/structure/$bundle/fields/entity_test.$bundle.$field_name/translate");
$this->clickLink('Add');
$this->assertSession()->pageTextContains('Translatable field setting');
$this->assertSession()->assertEscaped($translatable_field_setting);
$this->assertSession()->pageTextContains('Translatable storage setting');
$this->assertSession()->assertEscaped($translatable_storage_setting);
}
/**
* Tests the translation of a boolean field settings.
*/
public function testBooleanFieldConfigTranslation(): void {
// Add a test boolean field.
$field_name = $this->randomMachineName();
FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'type' => 'boolean',
])->save();
$bundle = $this->randomMachineName();
entity_test_create_bundle($bundle);
$field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'entity_test',
'bundle' => $bundle,
]);
$on_label = 'On label (with <em>HTML</em> & things)';
$field->setSetting('on_label', $on_label);
$off_label = 'Off label (with <em>HTML</em> & things)';
$field->setSetting('off_label', $off_label);
$field->save();
$this->drupalLogin($this->translatorUser);
$this->drupalGet("/entity_test/structure/$bundle/fields/entity_test.$bundle.$field_name/translate");
$this->clickLink('Add');
// Checks the text of details summary element that surrounds the translation
// options.
$this->assertSession()->responseContains(Html::escape(strip_tags($on_label)) . ' Boolean settings');
// Checks that the correct on and off labels appear on the form.
$this->assertSession()->assertEscaped($on_label);
$this->assertSession()->assertEscaped($off_label);
}
/**
* Tests text_format translation.
*/
public function testTextFormatTranslation(): void {
$this->drupalLogin($this->adminUser);
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = $this->container->get('config.factory');
$expected = [
'value' => '<p><strong>Hello World</strong></p>',
'format' => 'plain_text',
];
$actual = $config_factory
->get('config_translation_test.content')
->getOriginal('content', FALSE);
$this->assertEquals($expected, $actual);
$translation_base_url = 'admin/config/media/file-system/translate';
$this->drupalGet($translation_base_url);
// 'Add' link should be present for French translation.
$translation_page_url = "$translation_base_url/fr/add";
$this->assertSession()->linkByHrefExists($translation_page_url);
$this->drupalGet($translation_page_url);
// Assert that changing the text format is not possible, even for an
// administrator.
$this->assertSession()->fieldNotExists('translation[config_names][config_translation_test.content][content][format]');
// Update translatable fields.
$edit = [
'translation[config_names][config_translation_test.content][content][value]' => '<p><strong>Hello World</strong> - FR</p>',
];
// Save language specific version of form.
$this->drupalGet($translation_page_url);
$this->submitForm($edit, 'Save translation');
// Get translation and check we've got the right value.
$expected = [
'value' => '<p><strong>Hello World</strong> - FR</p>',
'format' => 'plain_text',
];
$this->container->get('language.config_factory_override')
->setLanguage(new Language(['id' => 'fr']));
$actual = $config_factory
->get('config_translation_test.content')
->get('content');
$this->assertEquals($expected, $actual);
// Change the text format of the source configuration and verify that the
// text format of the translation does not change because that could lead to
// security vulnerabilities.
$config_factory
->getEditable('config_translation_test.content')
->set('content.format', 'full_html')
->save();
$actual = $config_factory
->get('config_translation_test.content')
->get('content');
// The translation should not have changed, so re-use $expected.
$this->assertEquals($expected, $actual);
// Because the text is now in a text format that the translator does not
// have access to, the translator should not be able to translate it.
$translation_page_url = "$translation_base_url/fr/edit";
$this->drupalLogin($this->translatorUser);
$this->drupalGet($translation_page_url);
$this->assertDisabledTextarea('edit-translation-config-names-config-translation-testcontent-content-value');
$this->submitForm([], 'Save translation');
// Check that submitting the form did not update the text format of the
// translation.
$actual = $config_factory
->get('config_translation_test.content')
->get('content');
$this->assertEquals($expected, $actual);
// The administrator must explicitly change the text format.
$this->drupalLogin($this->adminUser);
$edit = [
'translation[config_names][config_translation_test.content][content][format]' => 'full_html',
];
$this->drupalGet($translation_page_url);
$this->submitForm($edit, 'Save translation');
$expected = [
'value' => '<p><strong>Hello World</strong> - FR</p>',
'format' => 'full_html',
];
$actual = $config_factory
->get('config_translation_test.content')
->get('content');
$this->assertEquals($expected, $actual);
}
/**
* Tests field translation for node fields.
*/
public function testNodeFieldTranslation(): void {
NodeType::create(['type' => 'article', 'name' => 'Article'])->save();
$field_name = 'translatable_field';
$field_storage = FieldStorageConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'type' => 'text',
]);
$field_storage->setSetting('translatable_storage_setting', 'translatable_storage_setting');
$field_storage->save();
$field = FieldConfig::create([
'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => 'article',
]);
$field->save();
$this->drupalLogin($this->translatorUser);
$this->drupalGet("/entity_test/structure/article/fields/node.article.$field_name/translate");
$this->clickLink('Add');
$form_values = [
'translation[config_names][field.field.node.article.translatable_field][description]' => 'FR Help text.',
'translation[config_names][field.field.node.article.translatable_field][label]' => 'FR label',
];
$this->submitForm($form_values, 'Save translation');
$this->assertSession()->pageTextContains('Successfully saved French translation.');
// Check that the translations are saved.
$this->clickLink('Edit');
$this->assertSession()->responseContains('FR label');
}
/**
* Test translation save confirmation message.
*/
public function testMenuTranslationWithoutChange(): void {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/structure/menu/manage/main/translate/tyv/add');
$this->submitForm([], 'Save translation');
$this->assertSession()->pageTextContains('Tuvan translation was not added. To add a translation, you must modify the configuration.');
$this->drupalGet('admin/structure/menu/manage/main/translate/tyv/add');
$edit = [
'translation[config_names][system.menu.main][label]' => 'Main navigation Translation',
'translation[config_names][system.menu.main][description]' => 'Site section links Translation',
];
$this->submitForm($edit, 'Save translation');
$this->assertSession()->pageTextContains('Successfully saved Tuvan translation.');
}
}

View File

@@ -0,0 +1,199 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
// cspell:ignore libellé
/**
* Translate site information to various languages.
*
* @group config_translation
*/
class ConfigTranslationUiSiteInformationTest extends ConfigTranslationUiTestBase {
/**
* Tests the site information translation interface.
*/
public function testSiteInformationTranslationUi(): void {
$this->drupalLogin($this->adminUser);
$site_name = 'Name of the site for testing configuration translation';
$site_slogan = 'Site slogan for testing configuration translation';
$site_name_label = 'Site name';
$fr_site_name = 'Nom du site pour tester la configuration traduction';
$fr_site_slogan = 'Slogan du site pour tester la traduction de configuration';
$fr_site_name_label = 'Libellé du champ "Nom du site"';
$translation_base_url = 'admin/config/system/site-information/translate';
// Set site name and slogan for default language.
$this->setSiteInformation($site_name, $site_slogan);
$this->drupalGet('admin/config/system/site-information');
// Check translation tab exist.
$this->assertSession()->linkByHrefExists($translation_base_url);
$this->drupalGet($translation_base_url);
// Check that the 'Edit' link in the source language links back to the
// original form.
$this->clickLink('Edit');
// Also check that saving the form leads back to the translation overview.
$this->submitForm([], 'Save configuration');
$this->assertSession()->addressEquals($translation_base_url);
// Check 'Add' link of French to visit add page.
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/add");
$this->clickLink('Add');
// Make sure original text is present on this page.
$this->assertSession()->pageTextContains($site_name);
$this->assertSession()->pageTextContains($site_slogan);
// Update site name and slogan for French.
$edit = [
'translation[config_names][system.site][name]' => $fr_site_name,
'translation[config_names][system.site][slogan]' => $fr_site_slogan,
];
$this->drupalGet("{$translation_base_url}/fr/add");
$this->submitForm($edit, 'Save translation');
$this->assertSession()->pageTextContains('Successfully saved French translation.');
// Check for edit, delete links (and no 'add' link) for French language.
$this->assertSession()->linkByHrefNotExists("$translation_base_url/fr/add");
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/edit");
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/delete");
// Check translation saved proper.
$this->drupalGet("$translation_base_url/fr/edit");
$this->assertSession()->fieldValueEquals('translation[config_names][system.site][name]', $fr_site_name);
$this->assertSession()->fieldValueEquals('translation[config_names][system.site][slogan]', $fr_site_slogan);
// Place branding block with site name and slogan into header region.
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
// Check French translation of site name and slogan are in place.
$this->drupalGet('fr');
$this->assertSession()->pageTextContains($fr_site_name);
$this->assertSession()->pageTextContains($fr_site_slogan);
// Visit French site to ensure base language string present as source.
$this->drupalGet("fr/$translation_base_url/fr/edit");
$this->assertSession()->pageTextContains($site_name);
$this->assertSession()->pageTextContains($site_slogan);
// Translate 'Site name' label in French.
$search = [
'string' => $site_name_label,
'langcode' => 'fr',
'translation' => 'untranslated',
];
$this->drupalGet('admin/config/regional/translate');
$this->submitForm($search, 'Filter');
$textarea = $this->assertSession()->elementExists('xpath', '//textarea');
$lid = $textarea->getAttribute('name');
$edit = [
$lid => $fr_site_name_label,
];
$this->drupalGet('admin/config/regional/translate');
$this->submitForm($edit, 'Save translations');
// Ensure that the label is in French (and not in English).
$this->drupalGet("fr/$translation_base_url/fr/edit");
$this->assertSession()->pageTextContains($fr_site_name_label);
$this->assertSession()->pageTextNotContains($site_name_label);
// Ensure that the label is also in French (and not in English)
// when editing another language with the interface in French.
$this->drupalGet("fr/$translation_base_url/ta/edit");
$this->assertSession()->pageTextContains($fr_site_name_label);
$this->assertSession()->pageTextNotContains($site_name_label);
// Ensure that the label is not translated when the interface is in English.
$this->drupalGet("$translation_base_url/fr/edit");
$this->assertSession()->pageTextContains($site_name_label);
$this->assertSession()->pageTextNotContains($fr_site_name_label);
}
/**
* Tests the site information translation interface.
*/
public function testSourceValueDuplicateSave(): void {
$this->drupalLogin($this->adminUser);
$site_name = 'Site name for testing configuration translation';
$site_slogan = 'Site slogan for testing configuration translation';
$translation_base_url = 'admin/config/system/site-information/translate';
$this->setSiteInformation($site_name, $site_slogan);
$this->drupalGet($translation_base_url);
// Case 1: Update new value for site slogan and site name.
$edit = [
'translation[config_names][system.site][name]' => 'FR ' . $site_name,
'translation[config_names][system.site][slogan]' => 'FR ' . $site_slogan,
];
// First time, no overrides, so just Add link.
$this->drupalGet("{$translation_base_url}/fr/add");
$this->submitForm($edit, 'Save translation');
// Read overridden file from active config.
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
// Expect both name and slogan in language specific file.
$expected = [
'name' => 'FR ' . $site_name,
'slogan' => 'FR ' . $site_slogan,
];
$this->assertEquals($expected, $override->get());
// Case 2: Update new value for site slogan and default value for site name.
$this->drupalGet("$translation_base_url/fr/edit");
// Assert that the language configuration does not leak outside of the
// translation form into the actual site name and slogan.
$this->assertSession()->pageTextNotContains('FR ' . $site_name);
$this->assertSession()->pageTextNotContains('FR ' . $site_slogan);
$edit = [
'translation[config_names][system.site][name]' => $site_name,
'translation[config_names][system.site][slogan]' => 'FR ' . $site_slogan,
];
$this->submitForm($edit, 'Save translation');
$this->assertSession()->pageTextContains('Successfully updated French translation.');
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
// Expect only slogan in language specific file.
$expected = 'FR ' . $site_slogan;
$this->assertEquals($expected, $override->get('slogan'));
// Case 3: Keep default value for site name and slogan.
$this->drupalGet("$translation_base_url/fr/edit");
$this->assertSession()->pageTextNotContains('FR ' . $site_slogan);
$edit = [
'translation[config_names][system.site][name]' => $site_name,
'translation[config_names][system.site][slogan]' => $site_slogan,
];
$this->submitForm($edit, 'Save translation');
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
// Expect no language specific file.
$this->assertTrue($override->isNew());
// Check configuration page with translator user. Should have no access.
$this->drupalLogout();
$this->drupalLogin($this->translatorUser);
$this->drupalGet('admin/config/system/site-information');
$this->assertSession()->statusCodeEquals(403);
// While translator can access the translation page, the edit link is not
// present due to lack of permissions.
$this->drupalGet($translation_base_url);
$this->assertSession()->linkNotExists('Edit');
// Check 'Add' link for French.
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/add");
}
}

View File

@@ -0,0 +1,336 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
// cspell:ignore anonyme viewsviewfiles
/**
* Translate settings and entities to various languages.
*
* @group config_translation
* @group #slow
*/
class ConfigTranslationUiTest extends ConfigTranslationUiTestBase {
/**
* Tests the account settings translation interface.
*
* This is the only special case so far where we have multiple configuration
* names involved building up one configuration translation form. Test that
* the translations are saved for all configuration names properly.
*/
public function testAccountSettingsConfigurationTranslation(): void {
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/config/people/accounts');
$this->assertSession()->linkExists('Translate account settings');
$this->drupalGet('admin/config/people/accounts/translate');
$this->assertSession()->linkExists('Translate account settings');
$this->assertSession()->linkByHrefExists('admin/config/people/accounts/translate/fr/add');
// Update account settings fields for French.
$edit = [
'translation[config_names][user.settings][anonymous]' => 'Anonyme',
'translation[config_names][user.mail][status_blocked][subject]' => 'Testing, your account is blocked.',
'translation[config_names][user.mail][status_blocked][body]' => 'Testing account blocked body.',
];
$this->drupalGet('admin/config/people/accounts/translate/fr/add');
$this->submitForm($edit, 'Save translation');
// Make sure the changes are saved and loaded back properly.
$this->drupalGet('admin/config/people/accounts/translate/fr/edit');
foreach ($edit as $key => $value) {
// Check the translations appear in the right field type as well.
$this->assertSession()->fieldValueEquals($key, $value);
}
// Check that labels for email settings appear.
$this->assertSession()->pageTextContains('Account cancellation confirmation');
$this->assertSession()->pageTextContains('Password recovery');
}
/**
* Tests source and target language edge cases.
*/
public function testSourceAndTargetLanguage(): void {
$this->drupalLogin($this->adminUser);
// Loading translation page for not-specified language (und)
// should return 403.
$this->drupalGet('admin/config/system/site-information/translate/und/add');
$this->assertSession()->statusCodeEquals(403);
// Check the source language doesn't have 'Add' or 'Delete' link and
// make sure source language edit goes to original configuration page
// not the translation specific edit page.
$this->drupalGet('admin/config/system/site-information/translate');
$this->assertSession()->linkByHrefNotExists('admin/config/system/site-information/translate/en/edit');
$this->assertSession()->linkByHrefNotExists('admin/config/system/site-information/translate/en/add');
$this->assertSession()->linkByHrefNotExists('admin/config/system/site-information/translate/en/delete');
$this->assertSession()->linkByHrefExists('admin/config/system/site-information');
// Translation addition to source language should return 403.
$this->drupalGet('admin/config/system/site-information/translate/en/add');
$this->assertSession()->statusCodeEquals(403);
// Translation editing in source language should return 403.
$this->drupalGet('admin/config/system/site-information/translate/en/edit');
$this->assertSession()->statusCodeEquals(403);
// Translation deletion in source language should return 403.
$this->drupalGet('admin/config/system/site-information/translate/en/delete');
$this->assertSession()->statusCodeEquals(403);
// Set default language of site information to not-specified language (und).
$this->config('system.site')
->set('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED)
->save();
// Make sure translation tab does not exist on the configuration page.
$this->drupalGet('admin/config/system/site-information');
$this->assertSession()->linkByHrefNotExists('admin/config/system/site-information/translate');
// If source language is not specified, translation page should be 403.
$this->drupalGet('admin/config/system/site-information/translate');
$this->assertSession()->statusCodeEquals(403);
}
/**
* Tests plural source elements in configuration translation forms.
*/
public function testPluralConfigStringsSourceElements(): void {
$this->drupalLogin($this->adminUser);
// Languages to test, with various number of plural forms.
$languages = [
'vi' => ['plurals' => 1, 'expected' => [TRUE, FALSE, FALSE, FALSE]],
'fr' => ['plurals' => 2, 'expected' => [TRUE, TRUE, FALSE, FALSE]],
'sl' => ['plurals' => 4, 'expected' => [TRUE, TRUE, TRUE, TRUE]],
];
foreach ($languages as $langcode => $data) {
// Import a .po file to add a new language with a given number of plural forms
$name = \Drupal::service('file_system')->tempnam('temporary://', $langcode . '_') . '.po';
file_put_contents($name, $this->getPoFile($data['plurals']));
$this->drupalGet('admin/config/regional/translate/import');
$this->submitForm([
'langcode' => $langcode,
'files[file]' => $name,
], 'Import');
// Change the config langcode of the 'files' view.
$config = \Drupal::service('config.factory')->getEditable('views.view.files');
$config->set('langcode', $langcode);
$config->save();
// Go to the translation page of the 'files' view.
$translation_url = 'admin/structure/views/view/files/translate/en/add';
$this->drupalGet($translation_url);
// Check if the expected number of source elements are present.
foreach ($data['expected'] as $index => $expected) {
if ($expected) {
$this->assertSession()->responseContains('edit-source-config-names-viewsviewfiles-display-default-display-options-fields-count-format-plural-string-' . $index);
}
else {
$this->assertSession()->responseNotContains('edit-source-config-names-viewsviewfiles-display-default-display-options-fields-count-format-plural-string-' . $index);
}
}
}
}
/**
* Tests translation of plural strings with multiple plural forms in config.
*/
public function testPluralConfigStrings(): void {
$this->drupalLogin($this->adminUser);
// First import a .po file with multiple plural forms.
// This will also automatically add the 'sl' language.
$name = \Drupal::service('file_system')->tempnam('temporary://', "sl_") . '.po';
file_put_contents($name, $this->getPoFile(4));
$this->drupalGet('admin/config/regional/translate/import');
$this->submitForm([
'langcode' => 'sl',
'files[file]' => $name,
], 'Import');
// Translate the files view, as this one uses numeric formatters.
$description = 'Singular form';
$field_value = '1 place';
$field_value_plural = '@count places';
$translation_url = 'admin/structure/views/view/files/translate/sl/add';
$this->drupalGet($translation_url);
// Make sure original text is present on this page, in addition to 2 new
// empty fields.
$this->assertSession()->pageTextContains($description);
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]', $field_value);
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]', $field_value_plural);
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]', '');
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]', '');
// Then make sure it also works.
$edit = [
'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]' => $field_value . ' SL',
'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]' => $field_value_plural . ' 1 SL',
'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]' => $field_value_plural . ' 2 SL',
'translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]' => $field_value_plural . ' 3 SL',
];
$this->drupalGet($translation_url);
$this->submitForm($edit, 'Save translation');
// Make sure the values have changed.
$this->drupalGet($translation_url);
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][0]', "$field_value SL");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][1]', "$field_value_plural 1 SL");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][2]', "$field_value_plural 2 SL");
$this->assertSession()->fieldValueEquals('translation[config_names][views.view.files][display][default][display_options][fields][count][format_plural_string][3]', "$field_value_plural 3 SL");
}
/**
* Tests translation storage in locale storage.
*/
public function testLocaleDBStorage(): void {
// Enable import of translations. By default this is disabled for automated
// tests.
$this->config('locale.settings')
->set('translation.import_enabled', TRUE)
->set('translation.use_source', LOCALE_TRANSLATION_USE_SOURCE_LOCAL)
->save();
$this->drupalLogin($this->adminUser);
$langcode = 'xx';
$name = $this->randomMachineName(16);
$edit = [
'predefined_langcode' => 'custom',
'langcode' => $langcode,
'label' => $name,
'direction' => Language::DIRECTION_LTR,
];
$this->drupalGet('admin/config/regional/language/add');
$this->submitForm($edit, 'Add custom language');
// Make sure there is no translation stored in locale storage before edit.
$translation = $this->getTranslation('user.settings', 'anonymous', 'fr');
$this->assertEmpty($translation);
// Add custom translation.
$edit = [
'translation[config_names][user.settings][anonymous]' => 'Anonyme',
];
$this->drupalGet('admin/config/people/accounts/translate/fr/add');
$this->submitForm($edit, 'Save translation');
// Make sure translation stored in locale storage after saved language
// specific configuration translation.
$translation = $this->getTranslation('user.settings', 'anonymous', 'fr');
$this->assertEquals('Anonyme', $translation->getString());
// revert custom translations to base translation.
$edit = [
'translation[config_names][user.settings][anonymous]' => 'Anonymous',
];
$this->drupalGet('admin/config/people/accounts/translate/fr/edit');
$this->submitForm($edit, 'Save translation');
// Make sure there is no translation stored in locale storage after revert.
$translation = $this->getTranslation('user.settings', 'anonymous', 'fr');
$this->assertEquals('Anonymous', $translation->getString());
}
/**
* Tests the single language existing.
*/
public function testSingleLanguageUI(): void {
$this->drupalLogin($this->adminUser);
// Delete French language
$this->drupalGet('admin/config/regional/language/delete/fr');
$this->submitForm([], 'Delete');
$this->assertSession()->pageTextContains('The French (fr) language has been removed.');
// Change default language to Tamil.
$edit = [
'site_default_language' => 'ta',
];
$this->drupalGet('admin/config/regional/language');
$this->submitForm($edit, 'Save configuration');
$this->assertSession()->pageTextContains('Configuration saved.');
// Delete English language
$this->drupalGet('admin/config/regional/language/delete/en');
$this->submitForm([], 'Delete');
$this->assertSession()->pageTextContains('The English (en) language has been removed.');
// Visit account setting translation page, this should not
// throw any notices.
$this->drupalGet('admin/config/people/accounts/translate');
$this->assertSession()->statusCodeEquals(200);
}
/**
* Tests the config_translation_info_alter() hook.
*/
public function testAlterInfo(): void {
$this->drupalLogin($this->adminUser);
$this->container->get('state')->set('config_translation_test_config_translation_info_alter', TRUE);
$this->container->get('plugin.manager.config_translation.mapper')->clearCachedDefinitions();
// Check if the translation page does not have the altered out settings.
$this->drupalGet('admin/config/people/accounts/translate/fr/add');
$this->assertSession()->pageTextContains('Name');
$this->assertSession()->pageTextNotContains('Account cancellation confirmation');
$this->assertSession()->pageTextNotContains('Password recovery');
}
/**
* Tests the sequence data type translation.
*/
public function testSequenceTranslation(): void {
$this->drupalLogin($this->adminUser);
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = $this->container->get('config.factory');
$expected = [
'kitten',
'llama',
'elephant',
];
$actual = $config_factory
->getEditable('config_translation_test.content')
->get('animals');
$this->assertEquals($expected, $actual);
$edit = [
'translation[config_names][config_translation_test.content][content][value]' => '<p><strong>Hello World</strong> - FR</p>',
'translation[config_names][config_translation_test.content][animals][0]' => 'kitten - FR',
'translation[config_names][config_translation_test.content][animals][1]' => 'llama - FR',
'translation[config_names][config_translation_test.content][animals][2]' => 'elephant - FR',
];
$this->drupalGet('admin/config/media/file-system/translate/fr/add');
$this->submitForm($edit, 'Save translation');
$this->container->get('language.config_factory_override')
->setLanguage(new Language(['id' => 'fr']));
$expected = [
'kitten - FR',
'llama - FR',
'elephant - FR',
];
$actual = $config_factory
->get('config_translation_test.content')
->get('animals');
$this->assertEquals($expected, $actual);
}
}

View File

@@ -0,0 +1,234 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\Core\Test\AssertMailTrait;
use Drupal\filter\Entity\FilterFormat;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Translate settings and entities to various languages.
*/
abstract class ConfigTranslationUiTestBase extends BrowserTestBase {
use AssertMailTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Languages to enable.
*
* @var array
*/
protected $langcodes = ['fr', 'ta', 'tyv'];
/**
* Administrator user for tests.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* Translator user for tests.
*
* @var \Drupal\user\UserInterface
*/
protected $translatorUser;
/**
* String translation storage object.
*
* @var \Drupal\locale\StringStorageInterface
*/
protected $localeStorage;
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'block',
'config_translation',
'config_translation_test',
'contact',
'contact_test',
'contextual',
'entity_test',
'field_test',
'field_ui',
'filter',
'filter_test',
'node',
'views',
'views_ui',
'menu_ui',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$translator_permissions = [
'translate configuration',
];
/** @var \Drupal\filter\FilterFormatInterface $filter_test_format */
$filter_test_format = FilterFormat::load('filter_test');
/** @var \Drupal\filter\FilterFormatInterface $filtered_html_format */
$filtered_html_format = FilterFormat::load('filtered_html');
/** @var \Drupal\filter\FilterFormatInterface $full_html_format */
$full_html_format = FilterFormat::load('full_html');
$admin_permissions = array_merge(
$translator_permissions,
[
'administer languages',
'administer site configuration',
'link to any page',
'administer contact forms',
'administer filters',
$filtered_html_format->getPermissionName(),
$full_html_format->getPermissionName(),
$filter_test_format->getPermissionName(),
'access site-wide contact form',
'access contextual links',
'administer views',
'administer account settings',
'administer themes',
'bypass node access',
'administer content types',
'translate interface',
]
);
// Create and log in user.
$this->translatorUser = $this->drupalCreateUser($translator_permissions);
$this->adminUser = $this->drupalCreateUser($admin_permissions);
// Add languages.
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
$this->localeStorage = $this->container->get('locale.storage');
$this->drupalPlaceBlock('local_tasks_block');
$this->drupalPlaceBlock('page_title_block');
}
/**
* Gets translation from locale storage.
*
* @param $config_name
* Configuration object.
* @param $key
* Translation configuration field key.
* @param $langcode
* String language code to load translation.
*
* @return bool|mixed
* Returns translation if exists, FALSE otherwise.
*/
protected function getTranslation($config_name, $key, $langcode) {
$settings_locations = $this->localeStorage->getLocations(['type' => 'configuration', 'name' => $config_name]);
$this->assertNotEmpty($settings_locations, "$config_name should have configuration locations.");
if ($settings_locations) {
$source = $this->container->get('config.factory')->get($config_name)->get($key);
$source_string = $this->localeStorage->findString(['source' => $source, 'type' => 'configuration']);
$this->assertNotEmpty($source_string, "$config_name.$key should have a source string.");
if ($source_string) {
$conditions = [
'lid' => $source_string->lid,
'language' => $langcode,
];
$translations = $this->localeStorage->getTranslations($conditions + ['translated' => TRUE]);
return reset($translations);
}
}
return FALSE;
}
/**
* Sets site name and slogan for default language, helps in tests.
*
* @param string $site_name
* The site name.
* @param string $site_slogan
* The site slogan.
*/
protected function setSiteInformation($site_name, $site_slogan) {
$edit = [
'site_name' => $site_name,
'site_slogan' => $site_slogan,
];
$this->drupalGet('admin/config/system/site-information');
$this->submitForm($edit, 'Save configuration');
$this->assertSession()->pageTextContains('The configuration options have been saved.');
}
/**
* Asserts that a textarea with a given ID has been disabled from editing.
*
* @param string $id
* The HTML ID of the textarea.
*
* @internal
*/
protected function assertDisabledTextarea(string $id): void {
$textarea = $this->assertSession()->fieldDisabled($id);
$this->assertSame('textarea', $textarea->getTagName());
$this->assertSame('This field has been disabled because you do not have sufficient permissions to edit it.', $textarea->getText());
// Make sure the text format select is not shown.
$select_id = str_replace('value', 'format--2', $id);
$xpath = $this->assertSession()->buildXPathQuery('//select[@id=:id]', [':id' => $select_id]);
$this->assertSession()->elementNotExists('xpath', $xpath);
}
/**
* Helper function that returns a .po file with a given number of plural forms.
*/
public function getPoFile($plurals) {
$po_file = [];
$po_file[1] = <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 8\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=1; plural=0;\\n"
EOF;
$po_file[2] = <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 8\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=2; plural=(n>1);\\n"
EOF;
$po_file[4] = <<< EOF
msgid ""
msgstr ""
"Project-Id-Version: Drupal 8\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
"Content-Transfer-Encoding: 8bit\\n"
"Plural-Forms: nplurals=4; plural=(((n%100)==1)?(0):(((n%100)==2)?(1):((((n%100)==3)||((n%100)==4))?(2):3)));\\n"
EOF;
return $po_file[$plurals];
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
/**
* Verifies theme configuration translation settings.
*
* @group config_translation
*/
class ConfigTranslationUiThemeTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'config_translation',
'config_translation_test',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Languages to enable.
*
* @var array
*/
protected $langcodes = ['fr', 'ta'];
/**
* Administrator user for tests.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$admin_permissions = [
'administer themes',
'administer languages',
'administer site configuration',
'translate configuration',
];
// Create and log in user.
$this->adminUser = $this->drupalCreateUser($admin_permissions);
// Add languages.
foreach ($this->langcodes as $langcode) {
ConfigurableLanguage::createFromLangcode($langcode)->save();
}
}
/**
* Tests that theme provided *.config_translation.yml files are found.
*/
public function testThemeDiscovery(): void {
// Install the test theme and rebuild routes.
$theme = 'config_translation_test_theme';
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/appearance');
$element = $this->assertSession()->elementExists('xpath', "//a[normalize-space()='Install and set as default' and contains(@href, '{$theme}')]");
$this->drupalGet($this->getAbsoluteUrl($element->getAttribute('href')), ['external' => TRUE]);
$translation_base_url = 'admin/config/development/performance/translate';
$this->drupalGet($translation_base_url);
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->linkByHrefExists("$translation_base_url/fr/add");
}
}

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\views_ui\Functional\UITestBase;
use Drupal\views\Views;
// cspell:ignore später
/**
* Visit view list and test if translate is available.
*
* @group config_translation
*/
class ConfigTranslationViewListUiTest extends UITestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['node', 'test_view'];
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['config_translation', 'views_ui'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE, $modules = ['views_test_config']): void {
parent::setUp($import_test_views, $modules);
$permissions = [
'administer views',
'translate configuration',
'access content overview',
'administer languages',
];
// Create and log in user.
$this->drupalLogin($this->drupalCreateUser($permissions));
}
/**
* Tests views_ui list to see if translate link is added to operations.
*/
public function testTranslateOperationInViewListUi(): void {
// Views UI List 'admin/structure/views'.
$this->drupalGet('admin/structure/views');
$translate_link = 'admin/structure/views/view/test_view/translate';
// Test if the link to translate the test_view is on the page.
$this->assertSession()->linkByHrefExists($translate_link);
// Test if the link to translate actually goes to the translate page.
$this->drupalGet($translate_link);
$this->assertSession()->responseContains('<th>Language</th>');
// Test that the 'Edit' tab appears.
$this->assertSession()->linkByHrefExists('admin/structure/views/view/test_view');
}
/**
* Test to ensure that TimestampFormatter translation works.
*/
public function testTimestampFormatterTranslation(): void {
ConfigurableLanguage::createFromLangcode('de')->save();
$this->drupalCreateContentType(['type' => 'article']);
$node = $this->drupalCreateNode(['type' => 'article', 'title' => $this->randomMachineName()]);
// Update the view to set the field formatter.
$view = Views::getView('content');
$display = &$view->storage->getDisplay('default');
$display['display_options']['fields']['changed']['type'] = 'timestamp_ago';
$display['display_options']['fields']['changed']['settings'] = [
'future_format' => '@interval hence',
'past_format' => '@interval ago',
'granularity' => 1,
];
$view->save();
// Add a translation to the views configuration for the past and future
// formats.
$this->drupalGet('admin/structure/views/view/content/translate/de/edit');
$edit = [
'translation[config_names][views.view.content][display][default][display_options][fields][changed][settings][future_format]' => '@interval später',
'translation[config_names][views.view.content][display][default][display_options][fields][changed][settings][past_format]' => 'vor @interval',
];
$this->submitForm($edit, 'Save translation');
// Create a timestamp just over an hour in the past and set the nodes update
// time to this.
$past_timestamp = \Drupal::time()->getCurrentTime() - 3700;
$node->setChangedTime($past_timestamp);
$node->save();
$this->drupalGet('/de/admin/content');
// Not all normal string translations are available, so 'hour' is still in
// English.
$this->assertSession()->pageTextContains('vor 1 hour');
// Create a timestamp just over an hour in the future and set the nodes
// update time to this.
$past_timestamp = \Drupal::time()->getCurrentTime() + 3700;
$node->setChangedTime($past_timestamp);
$node->save();
$this->drupalGet('/de/admin/content');
// Not all normal string translations are available, so 'hour' is still in
// English.
$this->assertSession()->pageTextContains('1 hour später');
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Functional;
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
/**
* Generic module test for config_translation.
*
* @group config_translation
*/
class GenericTest extends GenericModuleTestBase {}

View File

@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Translate settings and entities to various languages.
*
* @group config_translation
*/
class ConfigTranslationUiTest extends WebDriverTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_translation',
'contextual',
'node',
'views',
'views_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests that contextual link related to views.
*/
public function testViewContextualLink(): void {
$user = $this->drupalCreateUser([
'translate configuration',
'access contextual links',
]);
$this->drupalLogin($user);
$this->drupalGet('node');
$contextualLinks = $this->assertSession()->waitForElement('css', '.contextual-links');
$link = $contextualLinks->findLink('Translate view');
$this->assertNotNull($link, 'Translate view contextual link added.');
}
/**
* Tests that the add, edit and delete operations open in a modal.
*/
public function testConfigTranslationDialog(): void {
$page = $this->getSession()->getPage();
ConfigurableLanguage::createFromLangcode('fi')->save();
$user = $this->drupalCreateUser([
'translate configuration',
]);
$this->drupalLogin($user);
$this->drupalGet('admin/structure/views/view/content/translate');
$this->clickLink('Add');
$this->assertEquals('Add Finnish translation for Content view', $this->assertSession()->waitForElement('css', '.ui-dialog-title')->getText());
$this->assertSession()->fieldExists('translation[config_names][views.view.content][label]')->setValue('Content FI');
$page->find('css', '.ui-dialog-buttonset')->pressButton('Save translation');
$this->assertSession()->pageTextContains('Successfully saved Finnish translation.');
$this->clickLink('Edit');
$this->assertEquals('Edit Finnish translation for Content view', $this->assertSession()->waitForElement('css', '.ui-dialog-title')->getText());
$this->getSession()->getPage()->find('css', '.ui-dialog-buttonset')->pressButton('Save translation');
$this->assertSession()->pageTextContains('Successfully updated Finnish translation.');
$page->find('css', '.dropbutton-toggle button')->click();
$this->clickLink('Delete');
$this->assertEquals('Are you sure you want to delete the Finnish translation of Content view?', $this->assertSession()->waitForElement('css', '.ui-dialog-title')->getText());
$page->find('css', '.ui-dialog-buttonset')->pressButton('Delete');
$this->assertSession()->pageTextContains('Finnish translation of Content view was deleted');
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel;
use Drupal\Core\Routing\RouteMatch;
use Drupal\KernelTests\KernelTestBase;
use Symfony\Component\Routing\Route;
/**
* Tests config mapper.
*
* @group config_translation
*/
class ConfigMapperTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_translation',
'config_translation_test',
'language',
'locale',
'system',
];
/**
* Tests adding config names to mapper.
*/
public function testAddingConfigNames(): void {
// Get a config names mapper.
$mappers = \Drupal::service('plugin.manager.config_translation.mapper')->getMappers();
$mapper = $mappers['system.site_information_settings'];
// Test that it doesn't contain a config name from config_translation_test.
$config_names = $mapper->getConfigNames();
$this->assertNotContains('config_translation_test.content', $config_names);
// Call populateFromRouteMatch() to dispatch the "config mapper populate"
// event.
$mapper->populateFromRouteMatch(new RouteMatch('test', new Route('/')));
// Test that it contains the new config name from config_translation_test.
$config_names = $mapper->getConfigNames();
$this->assertContains('config_translation_test.content', $config_names);
}
}

View File

@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel;
use Drupal\config_translation\Form\ConfigTranslationAddForm;
use Drupal\config_translation\Form\ConfigTranslationEditForm;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests for altering configuration translation forms.
*
* @group config_translation
*/
class ConfigTranslationFormTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_translation',
'config_translation_test',
'language',
'locale',
];
/**
* Tests altering of the configuration translation forms.
*/
public function testConfigTranslationFormAlter(): void {
$this->installConfig(['config_translation_test']);
$definitions = $this->container->get('plugin.manager.config_translation.mapper')->getDefinitions();
$plugin_id = key($definitions);
$langcode = 'xx';
ConfigurableLanguage::create(['id' => $langcode, 'label' => 'XX'])->save();
$this->container->get('state')->set('config_translation_test_alter_form_alter', TRUE);
$form_builder = $this->container->get('form_builder');
$route_match = $this->container->get('current_route_match');
$add_form = $form_builder->getForm(ConfigTranslationAddForm::class, $route_match, $plugin_id, $langcode);
$edit_form = $form_builder->getForm(ConfigTranslationEditForm::class, $route_match, $plugin_id, $langcode);
// Test that hook_form_BASE_FORM_ID_alter() was called for the base form ID
// 'config_translation_form'.
$this->assertTrue($add_form['#base_altered']);
$this->assertTrue($edit_form['#base_altered']);
// Test that hook_form_FORM_ID_alter() was called for the form IDs
// 'config_translation_add_form' and 'config_translation_edit_form'.
$this->assertTrue($add_form['#altered']);
$this->assertTrue($edit_form['#altered']);
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade i18n maintenance variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemMaintenanceTranslationTest extends MigrateDrupal6TestBase {
protected static $modules = [
'language',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigrations([
'language',
'system_maintenance',
'd6_system_maintenance_translation',
]);
}
/**
* Tests migration of system variables to system.maintenance.yml.
*/
public function testSystemMaintenance(): void {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'system.maintenance');
$this->assertSame('fr - Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.', $config->get('message'));
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade i18n_strings site variables to system.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateSystemSiteTranslationTest extends MigrateDrupal6TestBase {
protected static $modules = [
'language',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigrations([
'language',
'system_site',
'd6_system_site_translation',
]);
}
/**
* Tests migration of system (site) variables to system.site.yml.
*/
public function testSystemSite(): void {
$config_translation = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'system.site');
$this->assertSame('fr site name', $config_translation->get('name'));
$this->assertSame('fr_site_mail@example.com', $config_translation->get('mail'));
$this->assertSame('fr Migrate rocks', $config_translation->get('slogan'));
$this->assertSame('/fr-user', $config_translation->get('page.403'));
$this->assertSame('/fr-page-not-found', $config_translation->get('page.404'));
$this->assertSame('/node', $config_translation->get('page.front'));
$this->assertNull($config_translation->get('admin_compact_mode'));
$config_translation = \Drupal::service('language_manager')->getLanguageConfigOverride('zu', 'system.site');
$this->assertSame('zu - site_name', $config_translation->get('name'));
$this->assertSame('site_mail@example.com', $config_translation->get('mail'));
$this->assertSame('Migrate rocks', $config_translation->get('slogan'));
$this->assertSame('/zu-user', $config_translation->get('page.403'));
$this->assertSame('/zu-page-not-found', $config_translation->get('page.404'));
$this->assertSame('/node', $config_translation->get('page.front'));
$this->assertNull($config_translation->get('admin_compact_mode'));
}
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Upgrade i18n variables to user.*.yml.
*
* @group migrate_drupal_6
*/
class MigrateUserConfigsTranslationTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
protected static $modules = [
'language',
'locale',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installSchema('locale',
['locales_source', 'locales_target', 'locales_location']);
$this->executeMigrations([
'language',
'd6_user_mail',
'd6_user_settings',
'd6_user_mail_translation',
'd6_user_settings_translation',
]);
}
/**
* Tests migration of i18n user variables to user.mail.yml.
*/
public function testUserMail(): void {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'user.mail');
$this->assertSame('fr - Account details for [user:name] at [site:name] (approved)', $config->get('status_activated.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nYour account at [site:name] has been activated.\r\n\r\nYou may now log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\nOnce you have set your own password, you will be able to log in to [site:login-url] in the future using:\r\n\r\nusername: [user:name]\r\n", $config->get('status_activated.body'));
$this->assertSame('fr - Replacement login information for [user:name] at [site:name]', $config->get('password_reset.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nA request to reset the password for your account has been made at [site:name].\r\n\r\nYou may now log in to [site:url-brief] by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.", $config->get('password_reset.body'));
$this->assertSame('fr - Account details for [user:name] at [site:name] (deleted)', $config->get('cancel_confirm.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nYour account on [site:name] has been deleted.", $config->get('cancel_confirm.body'));
$this->assertSame('fr - An administrator created an account for you at [site:name]', $config->get('register_admin_created.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\r\n\r\nusername: [user:name]\r\npassword: \r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\n\r\n-- [site:name] team", $config->get('register_admin_created.body'));
$this->assertSame('fr - Account details for [user:name] at [site:name]', $config->get('register_no_approval_required.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nThank you for registering at [site:name]. You may now log in to [site:login-url] using the following username and password:\r\n\r\nusername: [user:name]\r\npassword: \r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\n\r\n-- [site:name] team", $config->get('register_no_approval_required.body'));
$this->assertSame('fr - Account details for [user:name] at [site:name] (pending admin approval)', $config->get('register_pending_approval.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\r\n\r\n\r\n-- [site:name] team", $config->get('register_pending_approval.body'));
$this->assertSame('fr - Account details for [user:name] at [site:name] (blocked)', $config->get('status_blocked.subject'));
$this->assertSame("fr - [user:name],\r\n\r\nYour account on [site:name] has been blocked.", $config->get('status_blocked.body'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'user.mail', $config->get());
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('zu', 'user.mail');
$this->assertSame('zu - An administrator created an account for you at [site:name]', $config->get('register_admin_created.subject'));
$this->assertSame("zu - [user:name],\r\n\r\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\r\n\r\nusername: [user:name]\r\npassword: \r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n[user:one-time-login-url]\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\r\n\r\n\r\n-- [site:name] team", $config->get('register_admin_created.body'));
// Tests migration of i18n user variables to user.settings.yml.
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('fr', 'user.settings');
$this->assertSame(1, $config->get('notify.status_blocked'));
$this->assertSame(0, $config->get('notify.status_activated'));
$this->assertSame(0, $config->get('verify_mail'));
$this->assertSame('admin_only', $config->get('register'));
$this->assertSame('fr Guest', $config->get('anonymous'));
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('zu', 'user.settings');
$this->assertSame(1, $config->get('notify.status_blocked'));
$this->assertSame(0, $config->get('notify.status_activated'));
$this->assertSame(0, $config->get('verify_mail'));
$this->assertSame('admin_only', $config->get('register'));
$this->assertSame('Guest', $config->get('anonymous'));
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests the user profile field instance migration.
*
* @group migrate_drupal_6
*/
class MigrateUserProfileFieldInstanceTranslationTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_translation',
'locale',
'language',
'field',
];
/**
* Tests migration of translated user profile fields.
*/
public function testUserProfileFields(): void {
$this->executeMigrations([
'language',
'user_profile_field',
'user_profile_field_instance',
'd6_user_profile_field_instance_translation',
]);
$language_manager = $this->container->get('language_manager');
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_really_really_love_mig');
$this->assertSame("J'aime les migrations", $config_translation->get('label'));
$this->assertSame("Si vous cochez cette case, vous aimez les migrations.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_color');
$this->assertSame('fr - Favorite color', $config_translation->get('label'));
$this->assertSame('Inscrivez votre couleur préférée', $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_biography');
$this->assertSame('fr - Biography', $config_translation->get('label'));
$this->assertSame('fr - Tell people a little bit about yourself', $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_sell_address');
$this->assertSame('fr - Sell your email address?', $config_translation->get('label'));
$this->assertSame("fr - If you check this box, we'll sell your address to spammers to help line the pockets of our shareholders. Thanks!", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_sold_to');
$this->assertSame('fr - Sales Category', $config_translation->get('label'));
$this->assertSame("fr - Select the sales categories to which this user's address was sold.", $config_translation->get('description'));
$this->assertSame('fr - Pill spammers Fitness spammers Back\slash Forward/slash Dot.in.the.middle', $config_translation->get('options'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_bands');
$this->assertSame('Mes groupes préférés', $config_translation->get('label'));
$this->assertSame("fr - Enter your favorite bands. When you've saved your profile, you'll be able to find other people with the same favorites.", $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_birthdate');
$this->assertSame('fr - Birthdate', $config_translation->get('label'));
$this->assertSame('fr - Enter your birth date and we\'ll send you a coupon.', $config_translation->get('description'));
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'field.field.user.user.profile_blog');
$this->assertSame('fr - Blog', $config_translation->get('label'));
$this->assertSame('fr - Paste the full URL, including http://, of your personal blog.', $config_translation->get('description'));
}
}

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d6;
use Drupal\migrate\MigrateExecutable;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
* Tests rollback of user profile translations.
*
* @group migrate_drupal_6
*/
class MigrateUserProfileTranslationRollbackTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_translation',
'locale',
'language',
'field',
];
/**
* Tests rollback of the complete node migration.
*/
public function testRollback(): void {
$migration_ids = [
'user_profile_field',
'd6_profile_field_option_translation',
'user_profile_field_instance',
'd6_user_profile_field_instance_translation',
'language',
];
/** @var \Drupal\migrate\Plugin\MigrationPluginManager $migration_plugin_manager */
$migration_plugin_manager = \Drupal::service('plugin.manager.migration');
$migrations = [];
foreach ($migration_ids as $migration_id) {
$migrations[$migration_id] = $migration_plugin_manager->createInstance($migration_id, []);
}
$migrations = $migration_plugin_manager->buildDependencyMigration($migrations, []);
// Execute the import.
$ids = array_keys($migrations);
$this->executeMigrations($ids);
// Execute the rollback.
$ids = array_reverse($ids);
try {
foreach ($ids as $id) {
// Language rollback tries to rollback the default language so skip it.
if ($id == 'language') {
continue;
}
$migration = $migrations[$id];
(new MigrateExecutable($migration, $this))->rollback();
}
}
catch (\Exception $e) {
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migrations of i18n maintenance variable.
*
* @group migrate_drupal_7
*/
class MigrateSystemMaintenanceTranslationTest extends MigrateDrupal7TestBase {
protected static $modules = [
'language',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigrations([
'language',
'system_maintenance',
'd7_system_maintenance_translation',
]);
}
/**
* Tests migrations of i18n maintenance variable.
*/
public function testSystemMaintenance(): void {
$config = \Drupal::service('language_manager')->getLanguageConfigOverride('is', 'system.maintenance');
$this->assertSame('is - This is a custom maintenance mode message.', $config->get('message'));
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d7;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Migrate multilingual site variables.
*
* @group migrate_drupal_7
*/
class MigrateSystemSiteTranslationTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'language',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigrations([
'language',
'system_site',
'd7_system_site_translation',
]);
}
/**
* Tests migration of system (site) variables to system.site.yml.
*/
public function testSystemSite(): void {
$language_manager = \Drupal::service('language_manager');
$config_translation = $language_manager->getLanguageConfigOverride('fr', 'system.site');
$this->assertSame('The Site Name', $config_translation->get('name'));
$this->assertSame('fr - The Slogan', $config_translation->get('slogan'));
$this->assertNull($config_translation->get('page.403'));
$this->assertNull($config_translation->get('page.404'));
$this->assertNull($config_translation->get('page.front'));
$this->assertNull($config_translation->get('admin_compact_mode'));
$config_translation = $language_manager->getLanguageConfigOverride('is', 'system.site');
$this->assertSame('is - The Site Name', $config_translation->get('name'));
$this->assertSame('is - The Slogan', $config_translation->get('slogan'));
$this->assertNull($config_translation->get('page.403'));
$this->assertNull($config_translation->get('page.404'));
$this->assertNull($config_translation->get('page.front'));
$this->assertNull($config_translation->get('admin_compact_mode'));
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Migrate\d7;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Test migration of i18n user variables.
*
* @group migrate_drupal_7
*/
class MigrateUserConfigsTranslationTest extends MigrateDrupal7TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'language',
'locale',
'config_translation',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installSchema('locale', [
'locales_source',
'locales_target',
'locales_location',
]);
$this->executeMigrations([
'language',
'd7_user_mail',
'd7_user_settings',
'd7_user_mail_translation',
'd7_user_settings_translation',
]);
}
/**
* Tests migration of i18n user variables to user.mail and user.settings.
*/
public function testUserConfig(): void {
// Tests migration of i18n user variables to user.mail.yml.
$language_manager = \Drupal::service('language_manager');
$config = $language_manager->getLanguageConfigOverride('is', 'user.mail');
$this->assertSame('is - Are you sure?', $config->get('cancel_confirm.subject'));
$this->assertSame('is - A little birdie said you wanted to cancel your account.', $config->get('cancel_confirm.body'));
$this->assertSame('is - Fix your password', $config->get('password_reset.subject'));
$this->assertSame("is - Nope! You're locked out forever.", $config->get('password_reset.body'));
$this->assertSame('is - Gawd made you an account', $config->get('register_admin_created.subject'));
$this->assertSame("is - ...and it could be taken away.\r\n[site:name], [site:url]", $config->get('register_admin_created.body'));
$this->assertSame('is - Welcome!', $config->get('register_no_approval_required.subject'));
$this->assertSame('is - You can now log in if you can figure out how to use Drupal!', $config->get('register_no_approval_required.body'));
$this->assertSame('is - Soon...', $config->get('register_pending_approval.subject'));
$this->assertSame('is - ...you will join our Circle. Let the Drupal flow through you.', $config->get('register_pending_approval.body'));
$this->assertSame('is - Your account is approved!', $config->get('status_activated.subject'));
$this->assertSame('is - Your account was activated, and there was much rejoicing.', $config->get('status_activated.body'));
$this->assertSame('is - BEGONE!', $config->get('status_blocked.subject'));
$this->assertSame('is - You no longer please the robot overlords. Go to your room and chill out.', $config->get('status_blocked.body'));
$this->assertSame('is - So long, bub', $config->get('status_canceled.subject'));
$this->assertSame('is - The gates of Drupal are closed to you. Now you will work in the salt mines.', $config->get('status_canceled.body'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'user.mail', $config->get());
// Tests migration of i18n user variables to user.settings.yml.
$config = $language_manager->getLanguageConfigOverride('is', 'user.settings');
$this->assertSame('is - anonymous', $config->get('anonymous'));
}
}

View File

@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Kernel\Plugin\migrate\source\d6;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
// cspell:ignore objectid
/**
* Tests the i18nProfileField source plugin.
*
* @covers \Drupal\config_translation\Plugin\migrate\source\d6\ProfileFieldTranslation
* @group migrate_drupal
*/
class ProfileFieldTranslationTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['config_translation', 'migrate_drupal', 'user'];
/**
* {@inheritdoc}
*/
public static function providerSource() {
$test = [];
$test[0]['source_data'] = [
'profile_fields' => [
[
'fid' => 2,
'title' => 'Test',
'name' => 'profile_test',
],
[
'fid' => 42,
'title' => 'I love migrations',
'name' => 'profile_love_migrations',
],
],
'i18n_strings' => [
[
'lid' => 1,
'objectid' => 'profile_test',
'type' => 'field',
'property' => 'explanation',
],
[
'lid' => 10,
'objectid' => 'profile_love_migrations',
'type' => 'field',
'property' => 'title',
],
[
'lid' => 11,
'objectid' => 'profile_love_migrations',
'type' => 'field',
'property' => 'explanation',
],
],
'locales_target' => [
[
'lid' => 10,
'translation' => "fr - I love migration.",
'language' => 'fr',
],
[
'lid' => 11,
'translation' => 'fr - If you check this box, you like migrations.',
'language' => 'fr',
],
],
];
$test[0]['expected_data'] = [
[
'property' => 'title',
'translation' => "fr - I love migration.",
'language' => 'fr',
'fid' => '42',
'name' => 'profile_love_migrations',
],
[
'property' => 'explanation',
'translation' => 'fr - If you check this box, you like migrations.',
'language' => 'fr',
'fid' => '42',
'name' => 'profile_love_migrations',
],
];
return $test;
}
}

View File

@@ -0,0 +1,235 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Unit;
use Drupal\config_translation\ConfigEntityMapper;
use Drupal\Core\Url;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Routing\Route;
/**
* Tests the functionality provided by the configuration entity mapper.
*
* @group config_translation
*/
class ConfigEntityMapperTest extends UnitTestCase {
/**
* The configuration entity mapper to test.
*
* @var \Drupal\config_translation\ConfigEntityMapper
*/
protected $configEntityMapper;
/**
* The entity type manager used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $entityTypeManager;
/**
* The entity instance used for testing.
*
* @var \Drupal\Core\Entity\EntityInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $entity;
/**
* The route provider used for testing.
*
* @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $routeProvider;
/**
* The mocked language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $languageManager;
/**
* The mocked event dispatcher.
*
* @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $eventDispatcher;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->entityTypeManager = $this->createMock('Drupal\Core\Entity\EntityTypeManagerInterface');
$this->entity = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityInterface');
$this->routeProvider = $this->createMock('Drupal\Core\Routing\RouteProviderInterface');
$this->routeProvider
->expects($this->any())
->method('getRouteByName')
->with('entity.configurable_language.edit_form')
->willReturn(new Route('/admin/config/regional/language/edit/{configurable_language}'));
$definition = [
'class' => '\Drupal\config_translation\ConfigEntityMapper',
'base_route_name' => 'entity.configurable_language.edit_form',
'title' => '@label language',
'names' => [],
'entity_type' => 'configurable_language',
'route_name' => 'config_translation.item.overview.entity.configurable_language.edit_form',
];
$typed_config_manager = $this->createMock('Drupal\Core\Config\TypedConfigManagerInterface');
$locale_config_manager = $this->getMockBuilder('Drupal\locale\LocaleConfigManager')
->disableOriginalConstructor()
->getMock();
$this->languageManager = $this->createMock('Drupal\Core\Language\LanguageManagerInterface');
$this->eventDispatcher = $this->createMock('Symfony\Contracts\EventDispatcher\EventDispatcherInterface');
$this->configEntityMapper = new ConfigEntityMapper(
'configurable_language',
$definition,
$this->getConfigFactoryStub(),
$typed_config_manager,
$locale_config_manager,
$this->createMock('Drupal\config_translation\ConfigMapperManagerInterface'),
$this->routeProvider,
$this->getStringTranslationStub(),
$this->entityTypeManager,
$this->languageManager,
$this->eventDispatcher
);
}
/**
* Tests ConfigEntityMapper::setEntity() and ConfigEntityMapper::getEntity().
*/
public function testEntityGetterAndSetter(): void {
$this->entity
->expects($this->once())
->method('id')
->with()
->willReturn('entity_id');
$entity_type = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$entity_type
->expects($this->any())
->method('getConfigPrefix')
->willReturn('config_prefix');
$this->entityTypeManager
->expects($this->once())
->method('getDefinition')
->with('configurable_language')
->willReturn($entity_type);
// No entity is set.
$this->assertNull($this->configEntityMapper->getEntity());
$result = $this->configEntityMapper->setEntity($this->entity);
$this->assertTrue($result);
// Ensure that the getter provides the entity.
$this->assertEquals($this->entity, $this->configEntityMapper->getEntity());
// Ensure that the configuration name was added to the mapper.
$plugin_definition = $this->configEntityMapper->getPluginDefinition();
$this->assertContains('config_prefix.entity_id', $plugin_definition['names']);
// Make sure setEntity() returns FALSE when called a second time.
$result = $this->configEntityMapper->setEntity($this->entity);
$this->assertFalse($result);
}
/**
* Tests ConfigEntityMapper::getOverviewRouteParameters().
*/
public function testGetOverviewRouteParameters(): void {
$entity_type = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$this->entityTypeManager
->expects($this->once())
->method('getDefinition')
->with('configurable_language')
->willReturn($entity_type);
$this->configEntityMapper->setEntity($this->entity);
$this->entity
->expects($this->once())
->method('id')
->with()
->willReturn('entity_id');
$result = $this->configEntityMapper->getOverviewRouteParameters();
$this->assertSame(['configurable_language' => 'entity_id'], $result);
}
/**
* Tests ConfigEntityMapper::getType().
*/
public function testGetType(): void {
$result = $this->configEntityMapper->getType();
$this->assertSame('configurable_language', $result);
}
/**
* Tests ConfigEntityMapper::getTypeName().
*/
public function testGetTypeName(): void {
$entity_type = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$entity_type->expects($this->once())
->method('getLabel')
->willReturn('test');
$this->entityTypeManager
->expects($this->once())
->method('getDefinition')
->with('configurable_language')
->willReturn($entity_type);
$result = $this->configEntityMapper->getTypeName();
$this->assertSame('test', $result);
}
/**
* Tests ConfigEntityMapper::getTypeLabel().
*/
public function testGetTypeLabel(): void {
$entity_type = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$entity_type->expects($this->once())
->method('getLabel')
->willReturn('test');
$this->entityTypeManager
->expects($this->once())
->method('getDefinition')
->with('configurable_language')
->willReturn($entity_type);
$result = $this->configEntityMapper->getTypeLabel();
$this->assertSame('test', $result);
}
/**
* Tests ConfigEntityMapper::getOperations().
*/
public function testGetOperations(): void {
$result = $this->configEntityMapper->getOperations();
$expected = [
'list' => [
'title' => 'List',
'url' => Url::fromRoute('config_translation.entity_list', ['mapper_id' => 'configurable_language']),
],
];
$this->assertEquals($expected, $result);
}
}

View File

@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Unit;
use Drupal\config_translation\ConfigFieldMapper;
use Drupal\Tests\UnitTestCase;
/**
* Tests the functionality provided by the configuration field mapper.
*
* @group config_translation
*
* @coversDefaultClass \Drupal\config_translation\ConfigFieldMapper
*/
class ConfigFieldMapperTest extends UnitTestCase {
/**
* The configuration field mapper to test.
*
* @var \Drupal\config_translation\ConfigFieldMapper
*/
protected $configFieldMapper;
/**
* The field config instance used for testing.
*
* @var \Drupal\field\FieldConfigInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $entity;
/**
* The entity type manager used for testing.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $entityTypeManager;
/**
* The mocked event dispatcher.
*
* @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $eventDispatcher;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->entityTypeManager = $this->createMock('Drupal\Core\Entity\EntityTypeManagerInterface');
$this->entity = $this->createMock('Drupal\field\FieldConfigInterface');
$definition = [
'class' => '\Drupal\config_translation\ConfigFieldMapper',
'base_route_name' => 'entity.field_config.node_field_edit_form',
'title' => '@label field',
'names' => [],
'entity_type' => 'field_config',
];
$locale_config_manager = $this->getMockBuilder('Drupal\locale\LocaleConfigManager')
->disableOriginalConstructor()
->getMock();
$this->eventDispatcher = $this->createMock('Symfony\Contracts\EventDispatcher\EventDispatcherInterface');
$this->configFieldMapper = new ConfigFieldMapper(
'node_fields',
$definition,
$this->getConfigFactoryStub(),
$this->createMock('Drupal\Core\Config\TypedConfigManagerInterface'),
$locale_config_manager,
$this->createMock('Drupal\config_translation\ConfigMapperManagerInterface'),
$this->createMock('Drupal\Core\Routing\RouteProviderInterface'),
$this->getStringTranslationStub(),
$this->entityTypeManager,
$this->createMock('Drupal\Core\Language\LanguageManagerInterface'),
$this->eventDispatcher
);
}
/**
* Tests ConfigFieldMapper::setEntity().
*
* @covers ::setEntity
*/
public function testSetEntity(): void {
$entity_type = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityTypeInterface');
$entity_type
->expects($this->any())
->method('getConfigPrefix')
->willReturn('config_prefix');
$this->entityTypeManager
->expects($this->any())
->method('getDefinition')
->willReturn($entity_type);
$field_storage = $this->createMock('Drupal\field\FieldStorageConfigInterface');
$field_storage
->expects($this->any())
->method('id')
->willReturn('field_storage_id');
$this->entity
->expects($this->any())
->method('getFieldStorageDefinition')
->willReturn($field_storage);
$result = $this->configFieldMapper->setEntity($this->entity);
$this->assertTrue($result);
// Ensure that the configuration name was added to the mapper.
$plugin_definition = $this->configFieldMapper->getPluginDefinition();
$this->assertContains('config_prefix.field_storage_id', $plugin_definition['names']);
// Make sure setEntity() returns FALSE when called a second time.
$result = $this->configFieldMapper->setEntity($this->entity);
$this->assertFalse($result);
}
}

View File

@@ -0,0 +1,186 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Unit;
use Drupal\config_translation\ConfigMapperManager;
use Drupal\Core\Config\Schema\Mapping;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\Core\TypedData\DataDefinition;
use Prophecy\Prophet;
/**
* Tests the functionality provided by configuration translation mapper manager.
*
* @group config_translation
*/
class ConfigMapperManagerTest extends UnitTestCase {
/**
* The configuration mapper manager to test.
*
* @var \Drupal\config_translation\ConfigMapperManager
*/
protected $configMapperManager;
/**
* The typed configuration manager used for testing.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $typedConfigManager;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$language = new Language(['id' => 'en']);
$language_manager = $this->createMock('Drupal\Core\Language\LanguageManagerInterface');
$language_manager->expects($this->once())
->method('getCurrentLanguage')
->with(LanguageInterface::TYPE_INTERFACE)
->willReturn($language);
$this->typedConfigManager = $this->getMockBuilder('Drupal\Core\Config\TypedConfigManagerInterface')
->getMock();
$module_handler = $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface');
$theme_handler = $this->createMock('Drupal\Core\Extension\ThemeHandlerInterface');
$this->configMapperManager = new ConfigMapperManager(
$this->createMock('Drupal\Core\Cache\CacheBackendInterface'),
$language_manager,
$module_handler,
$this->typedConfigManager,
$theme_handler
);
}
/**
* Tests ConfigMapperManager::hasTranslatable().
*
* @param \Drupal\Core\TypedData\TypedDataInterface $element
* The schema element to test.
* @param bool $expected
* The expected return value of ConfigMapperManager::hasTranslatable().
*
* @dataProvider providerTestHasTranslatable
*/
public function testHasTranslatable(TypedDataInterface $element, $expected): void {
$this->typedConfigManager
->expects($this->once())
->method('get')
->with('test')
->willReturn($element);
$result = $this->configMapperManager->hasTranslatable('test');
$this->assertSame($expected, $result);
}
/**
* Provides data for ConfigMapperManager::testHasTranslatable()
*
* @return array
* An array of arrays, where each inner array contains the schema element
* to test as the first key and the expected result of
* ConfigMapperManager::hasTranslatable() as the second key.
*/
public static function providerTestHasTranslatable() {
return [
[static::getElement([]), FALSE],
[static::getElement(['aaa' => 'bbb']), FALSE],
[static::getElement(['translatable' => FALSE]), FALSE],
[static::getElement(['translatable' => TRUE]), TRUE],
[static::getNestedElement([static::getElement([])]), FALSE],
[static::getNestedElement([static::getElement(['translatable' => TRUE])]), TRUE],
[
static::getNestedElement([
static::getElement(['aaa' => 'bbb']),
static::getElement(['ccc' => 'ddd']),
static::getElement(['eee' => 'fff']),
]),
FALSE,
],
[
static::getNestedElement([
static::getElement(['aaa' => 'bbb']),
static::getElement(['ccc' => 'ddd']),
static::getElement(['translatable' => TRUE]),
]),
TRUE,
],
[
static::getNestedElement([
static::getElement(['aaa' => 'bbb']),
static::getNestedElement([
static::getElement(['ccc' => 'ddd']),
static::getElement(['eee' => 'fff']),
]),
static::getNestedElement([
static::getElement(['ggg' => 'hhh']),
static::getElement(['iii' => 'jjj']),
]),
]),
FALSE,
],
[
static::getNestedElement([
static::getElement(['aaa' => 'bbb']),
static::getNestedElement([
static::getElement(['ccc' => 'ddd']),
static::getElement(['eee' => 'fff']),
]),
static::getNestedElement([
static::getElement(['ggg' => 'hhh']),
static::getElement(['translatable' => TRUE]),
]),
]),
TRUE,
],
];
}
/**
* Returns a mocked schema element.
*
* @param array $definition
* The definition of the schema element.
*
* @return \Drupal\Core\Config\Schema\Element
* The mocked schema element.
*/
protected static function getElement(array $definition) {
$data_definition = new DataDefinition($definition);
$element = (new Prophet())->prophesize(TypedDataInterface::class);
$element->getDataDefinition()->willReturn($data_definition);
return $element->reveal();
}
/**
* Returns a mocked nested schema element.
*
* @param array $elements
* An array of simple schema elements.
*
* @return \Drupal\Core\Config\Schema\Mapping
* A nested schema element, containing the passed-in elements.
*/
protected static function getNestedElement(array $elements) {
// ConfigMapperManager::findTranslatable() checks for
// \Drupal\Core\TypedData\TraversableTypedDataInterface, but mocking that
// directly does not work, because we need to implement \IteratorAggregate
// in order for getIterator() to be called. Therefore we need to mock
// \Drupal\Core\Config\Schema\ArrayElement, but that is abstract, so we
// need to mock one of the subclasses of it.
$nested_element = (new Prophet())->prophesize(Mapping::class);
$nested_element->getIterator()->shouldBeCalledTimes(1)->willReturn(new \ArrayIterator($elements));
return $nested_element->reveal();
}
}

View File

@@ -0,0 +1,696 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_translation\Unit;
use Drupal\config_translation\ConfigNamesMapper;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Language\Language;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\Url;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\Routing\Route;
/**
* Tests the functionality provided by the configuration names mapper.
*
* @group config_translation
*/
class ConfigNamesMapperTest extends UnitTestCase {
/**
* The plugin definition of the test mapper.
*
* @var array
*/
protected $pluginDefinition;
/**
* The configuration names mapper to test.
*
* @see \Drupal\config_translation\ConfigNamesMapper
*
* @var \Drupal\Tests\config_translation\Unit\TestConfigNamesMapper
*/
protected $configNamesMapper;
/**
* The locale configuration manager.
*
* @var \Drupal\locale\LocaleConfigManager|\PHPUnit\Framework\MockObject\MockObject
*/
protected $localeConfigManager;
/**
* The typed configuration manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $typedConfigManager;
/**
* The configuration mapper manager.
*
* @var \Drupal\config_translation\ConfigMapperManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $configMapperManager;
/**
* The base route used for testing.
*
* @var \Symfony\Component\Routing\Route
*/
protected $baseRoute;
/**
* The route provider used for testing.
*
* @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $routeProvider;
/**
* The mocked URL generator.
*
* @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $urlGenerator;
/**
* The mocked language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $languageManager;
/**
* The mocked event dispatcher.
*
* @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject
*/
protected $eventDispatcher;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->routeProvider = $this->createMock('Drupal\Core\Routing\RouteProviderInterface');
$this->pluginDefinition = [
'class' => '\Drupal\config_translation\ConfigNamesMapper',
'base_route_name' => 'system.site_information_settings',
'title' => 'System information',
'names' => ['system.site'],
'weight' => 42,
];
$this->typedConfigManager = $this->createMock('Drupal\Core\Config\TypedConfigManagerInterface');
$this->localeConfigManager = $this->getMockBuilder('Drupal\locale\LocaleConfigManager')
->disableOriginalConstructor()
->getMock();
$this->configMapperManager = $this->createMock('Drupal\config_translation\ConfigMapperManagerInterface');
$this->urlGenerator = $this->createMock('Drupal\Core\Routing\UrlGeneratorInterface');
$container = new ContainerBuilder();
$container->set('url_generator', $this->urlGenerator);
\Drupal::setContainer($container);
$this->baseRoute = new Route('/admin/config/system/site-information');
$this->routeProvider
->expects($this->any())
->method('getRouteByName')
->with('system.site_information_settings')
->willReturn($this->baseRoute);
$this->languageManager = $this->createMock('Drupal\Core\Language\LanguageManagerInterface');
$this->eventDispatcher = $this->createMock('Symfony\Contracts\EventDispatcher\EventDispatcherInterface');
$this->configNamesMapper = new TestConfigNamesMapper(
'system.site_information_settings',
$this->pluginDefinition,
$this->getConfigFactoryStub(),
$this->typedConfigManager,
$this->localeConfigManager,
$this->configMapperManager,
$this->routeProvider,
$this->getStringTranslationStub(),
$this->languageManager,
$this->eventDispatcher
);
}
/**
* Tests ConfigNamesMapper::getTitle().
*/
public function testGetTitle(): void {
$result = $this->configNamesMapper->getTitle();
$this->assertSame($this->pluginDefinition['title'], (string) $result);
}
/**
* Tests ConfigNamesMapper::getBaseRouteName().
*/
public function testGetBaseRouteName(): void {
$result = $this->configNamesMapper->getBaseRouteName();
$this->assertSame($this->pluginDefinition['base_route_name'], $result);
}
/**
* Tests ConfigNamesMapper::getBaseRouteParameters().
*/
public function testGetBaseRouteParameters(): void {
$result = $this->configNamesMapper->getBaseRouteParameters();
$this->assertSame([], $result);
}
/**
* Tests ConfigNamesMapper::getBaseRoute().
*/
public function testGetBaseRoute(): void {
$result = $this->configNamesMapper->getBaseRoute();
$this->assertSame($this->baseRoute, $result);
}
/**
* Tests ConfigNamesMapper::getBasePath().
*/
public function testGetBasePath(): void {
$this->urlGenerator->expects($this->once())
->method('getPathFromRoute')
->with('system.site_information_settings', [])
->willReturn('/admin/config/system/site-information');
$result = $this->configNamesMapper->getBasePath();
$this->assertSame('/admin/config/system/site-information', $result);
}
/**
* Tests ConfigNamesMapper::getOverviewRouteName().
*/
public function testGetOverviewRouteName(): void {
$result = $this->configNamesMapper->getOverviewRouteName();
$expected = 'config_translation.item.overview.' . $this->pluginDefinition['base_route_name'];
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getOverviewRouteParameters().
*/
public function testGetOverviewRouteParameters(): void {
$result = $this->configNamesMapper->getOverviewRouteParameters();
$this->assertSame([], $result);
}
/**
* Tests ConfigNamesMapper::getOverviewRoute().
*/
public function testGetOverviewRoute(): void {
$expected = new Route('/admin/config/system/site-information/translate',
[
'_controller' => '\Drupal\config_translation\Controller\ConfigTranslationController::itemPage',
'plugin_id' => 'system.site_information_settings',
],
[
'_config_translation_overview_access' => 'TRUE',
]
);
$result = $this->configNamesMapper->getOverviewRoute();
$this->assertSame(serialize($expected), serialize($result));
}
/**
* Tests ConfigNamesMapper::getOverviewPath().
*/
public function testGetOverviewPath(): void {
$this->urlGenerator->expects($this->once())
->method('getPathFromRoute')
->with('config_translation.item.overview.system.site_information_settings', [])
->willReturn('/admin/config/system/site-information/translate');
$result = $this->configNamesMapper->getOverviewPath();
$this->assertSame('/admin/config/system/site-information/translate', $result);
}
/**
* Tests ConfigNamesMapper::getAddRouteName().
*/
public function testGetAddRouteName(): void {
$result = $this->configNamesMapper->getAddRouteName();
$expected = 'config_translation.item.add.' . $this->pluginDefinition['base_route_name'];
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getAddRouteParameters().
*/
public function testGetAddRouteParameters(): void {
$route_match = new RouteMatch('example', new Route('/test/{langcode}'), ['langcode' => 'xx']);
$this->configNamesMapper->populateFromRouteMatch($route_match);
$expected = ['langcode' => 'xx'];
$result = $this->configNamesMapper->getAddRouteParameters();
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getAddRoute().
*/
public function testGetAddRoute(): void {
$expected = new Route('/admin/config/system/site-information/translate/{langcode}/add',
[
'_form' => '\Drupal\config_translation\Form\ConfigTranslationAddForm',
'plugin_id' => 'system.site_information_settings',
],
[
'_config_translation_form_access' => 'TRUE',
]
);
$result = $this->configNamesMapper->getAddRoute();
$this->assertSame(serialize($expected), serialize($result));
}
/**
* Tests ConfigNamesMapper::getEditRouteName().
*/
public function testGetEditRouteName(): void {
$result = $this->configNamesMapper->getEditRouteName();
$expected = 'config_translation.item.edit.' . $this->pluginDefinition['base_route_name'];
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getEditRouteParameters().
*/
public function testGetEditRouteParameters(): void {
$route_match = new RouteMatch('example', new Route('/test/{langcode}'), ['langcode' => 'xx']);
$this->configNamesMapper->populateFromRouteMatch($route_match);
$expected = ['langcode' => 'xx'];
$result = $this->configNamesMapper->getEditRouteParameters();
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getEditRoute().
*/
public function testGetEditRoute(): void {
$expected = new Route('/admin/config/system/site-information/translate/{langcode}/edit',
[
'_form' => '\Drupal\config_translation\Form\ConfigTranslationEditForm',
'plugin_id' => 'system.site_information_settings',
],
[
'_config_translation_form_access' => 'TRUE',
]
);
$result = $this->configNamesMapper->getEditRoute();
$this->assertSame(serialize($expected), serialize($result));
}
/**
* Tests ConfigNamesMapper::getDeleteRouteName().
*/
public function testGetDeleteRouteName(): void {
$result = $this->configNamesMapper->getDeleteRouteName();
$expected = 'config_translation.item.delete.' . $this->pluginDefinition['base_route_name'];
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getDeleteRouteParameters().
*/
public function testGetDeleteRouteParameters(): void {
$route_match = new RouteMatch('example', new Route('/test/{langcode}'), ['langcode' => 'xx']);
$this->configNamesMapper->populateFromRouteMatch($route_match);
$expected = ['langcode' => 'xx'];
$result = $this->configNamesMapper->getDeleteRouteParameters();
$this->assertSame($expected, $result);
}
/**
* Tests ConfigNamesMapper::getRoute().
*/
public function testGetDeleteRoute(): void {
$expected = new Route('/admin/config/system/site-information/translate/{langcode}/delete',
[
'_form' => '\Drupal\config_translation\Form\ConfigTranslationDeleteForm',
'plugin_id' => 'system.site_information_settings',
],
[
'_config_translation_form_access' => 'TRUE',
]
);
$result = $this->configNamesMapper->getDeleteRoute();
$this->assertSame(serialize($expected), serialize($result));
}
/**
* Tests ConfigNamesMapper::getConfigNames().
*/
public function testGetConfigNames(): void {
$result = $this->configNamesMapper->getConfigNames();
$this->assertSame($this->pluginDefinition['names'], $result);
}
/**
* Tests ConfigNamesMapper::addConfigName().
*/
public function testAddConfigName(): void {
$names = $this->configNamesMapper->getConfigNames();
$this->configNamesMapper->addConfigName('test');
$names[] = 'test';
$result = $this->configNamesMapper->getConfigNames();
$this->assertSame($names, $result);
}
/**
* Tests ConfigNamesMapper::getWeight().
*/
public function testGetWeight(): void {
$result = $this->configNamesMapper->getWeight();
$this->assertSame($this->pluginDefinition['weight'], $result);
}
/**
* Tests ConfigNamesMapper::populateFromRouteMatch().
*/
public function testPopulateFromRouteMatch(): void {
// Make sure the language code is not set initially.
$this->assertNull($this->configNamesMapper->getInternalLangcode());
// Test that an empty request does not set the language code.
$route_match = new RouteMatch('example', new Route('/test/{langcode}'));
$this->configNamesMapper->populateFromRouteMatch($route_match);
$this->assertNull($this->configNamesMapper->getInternalLangcode());
// Test that a request with a 'langcode' attribute sets the language code.
$route_match = new RouteMatch('example', new Route('/test/{langcode}'), ['langcode' => 'xx']);
$this->configNamesMapper->populateFromRouteMatch($route_match);
$this->assertSame('xx', $this->configNamesMapper->getInternalLangcode());
// Test that the language code gets unset with the wrong request.
$route_match = new RouteMatch('example', new Route('/test/{langcode}'));
$this->configNamesMapper->populateFromRouteMatch($route_match);
$this->assertNull($this->configNamesMapper->getInternalLangcode());
}
/**
* Tests ConfigNamesMapper::getTypeLabel().
*/
public function testGetTypeLabel(): void {
$result = $this->configNamesMapper->getTypeLabel();
$this->assertSame($this->pluginDefinition['title'], (string) $result);
}
/**
* Tests ConfigNamesMapper::getLangcode().
*/
public function testGetLangcode(): void {
// Test that the getLangcode() falls back to 'en', if no explicit language
// code is provided.
$config_factory = $this->getConfigFactoryStub([
'system.site' => ['key' => 'value'],
]);
$this->configNamesMapper->setConfigFactory($config_factory);
$result = $this->configNamesMapper->getLangcode();
$this->assertSame('en', $result);
// Test that getLangcode picks up the language code provided by the
// configuration.
$config_factory = $this->getConfigFactoryStub([
'system.site' => ['langcode' => 'xx'],
]);
$this->configNamesMapper->setConfigFactory($config_factory);
$result = $this->configNamesMapper->getLangcode();
$this->assertSame('xx', $result);
// Test that getLangcode() works for multiple configuration names.
$this->configNamesMapper->addConfigName('system.maintenance');
$config_factory = $this->getConfigFactoryStub([
'system.site' => ['langcode' => 'xx'],
'system.maintenance' => ['langcode' => 'xx'],
]);
$this->configNamesMapper->setConfigFactory($config_factory);
$result = $this->configNamesMapper->getLangcode();
$this->assertSame('xx', $result);
// Test that getLangcode() throws an exception when different language codes
// are given.
$config_factory = $this->getConfigFactoryStub([
'system.site' => ['langcode' => 'xx'],
'system.maintenance' => ['langcode' => 'yy'],
]);
$this->configNamesMapper->setConfigFactory($config_factory);
try {
$this->configNamesMapper->getLangcode();
$this->fail();
}
catch (\RuntimeException $e) {
}
}
/**
* Tests ConfigNamesMapper::getConfigData().
*/
public function testGetConfigData(): void {
$configs = [
'system.site' => [
'name' => 'Drupal',
'slogan' => 'Come for the software, stay for the community!',
],
'system.maintenance' => [
'enabled' => FALSE,
'message' => '@site is currently under maintenance.',
],
'system.rss' => [
'items' => [
'view_mode' => 'rss',
],
],
];
$this->configNamesMapper->setConfigNames(array_keys($configs));
$config_factory = $this->getConfigFactoryStub($configs);
$this->configNamesMapper->setConfigFactory($config_factory);
$result = $this->configNamesMapper->getConfigData();
$this->assertSame($configs, $result);
}
/**
* Tests ConfigNamesMapper::hasSchema().
*
* @param array $mock_return_values
* An array of values that the mocked locale configuration manager should
* return for hasConfigSchema().
* @param bool $expected
* The expected return value of ConfigNamesMapper::hasSchema().
*
* @dataProvider providerTestHasSchema
*/
public function testHasSchema(array $mock_return_values, $expected): void {
// As the configuration names are arbitrary, simply use integers.
$config_names = range(1, count($mock_return_values));
$this->configNamesMapper->setConfigNames($config_names);
$map = [];
foreach ($config_names as $i => $config_name) {
$map[] = [$config_name, $mock_return_values[$i]];
}
$this->typedConfigManager
->expects($this->any())
->method('hasConfigSchema')
->willReturnMap($map);
$result = $this->configNamesMapper->hasSchema();
$this->assertSame($expected, $result);
}
/**
* Provides data for ConfigMapperTest::testHasSchema().
*
* @return array
* An array of arrays, where each inner array has an array of values that
* the mocked locale configuration manager should return for
* hasConfigSchema() as the first value and the expected return value of
* ConfigNamesMapper::hasSchema() as the second value.
*/
public static function providerTestHasSchema() {
return [
[[TRUE], TRUE],
[[FALSE], FALSE],
[[TRUE, TRUE, TRUE], TRUE],
[[TRUE, FALSE, TRUE], FALSE],
];
}
/**
* Tests ConfigNamesMapper::hasTranslatable().
*
* @param array $mock_return_values
* An array of values that the mocked configuration mapper manager should
* return for hasTranslatable().
* @param bool $expected
* The expected return value of ConfigNamesMapper::hasTranslatable().
*
* @dataProvider providerTestHasTranslatable
*/
public function testHasTranslatable(array $mock_return_values, $expected): void {
// As the configuration names are arbitrary, simply use integers.
$config_names = range(1, count($mock_return_values));
$this->configNamesMapper->setConfigNames($config_names);
$map = [];
foreach ($config_names as $i => $config_name) {
$map[] = isset($mock_return_values[$i]) ? [$config_name, $mock_return_values[$i]] : [];
}
$this->configMapperManager
->expects($this->any())
->method('hasTranslatable')
->willReturnMap($map);
$result = $this->configNamesMapper->hasTranslatable();
$this->assertSame($expected, $result);
}
/**
* Provides data for ConfigNamesMapperTest::testHasTranslatable().
*
* @return array
* An array of arrays, where each inner array has an array of values that
* the mocked configuration mapper manager should return for
* hasTranslatable() as the first value and the expected return value of
* ConfigNamesMapper::hasTranslatable() as the second value.
*/
public static function providerTestHasTranslatable() {
return [
[[], FALSE],
[[TRUE], TRUE],
[[FALSE], FALSE],
[[TRUE, TRUE, TRUE], TRUE],
[[FALSE, FALSE, FALSE], FALSE],
[[TRUE, FALSE, TRUE], TRUE],
];
}
/**
* Tests ConfigNamesMapper::hasTranslation().
*
* @param array $mock_return_values
* An array of values that the mocked configuration mapper manager should
* return for hasTranslation().
* @param bool $expected
* The expected return value of ConfigNamesMapper::hasTranslation().
*
* @dataProvider providerTestHasTranslation
*/
public function testHasTranslation(array $mock_return_values, $expected): void {
$language = new Language();
// As the configuration names are arbitrary, simply use integers.
$config_names = range(1, count($mock_return_values));
$this->configNamesMapper->setConfigNames($config_names);
$map = [];
foreach ($config_names as $i => $config_name) {
$map[] = [$config_name, $language->getId(), $mock_return_values[$i]];
}
$this->localeConfigManager
->expects($this->any())
->method('hasTranslation')
->willReturnMap($map);
$result = $this->configNamesMapper->hasTranslation($language);
$this->assertSame($expected, $result);
}
/**
* Provides data for ConfigNamesMapperTest::testHasTranslation().
*
* @return array
* An array of arrays, where each inner array has an array of values that
* the mocked configuration mapper manager should return for
* hasTranslation() as the first value and the expected return value of
* ConfigNamesMapper::hasTranslation() as the second value.
*/
public static function providerTestHasTranslation() {
return [
[[TRUE], TRUE],
[[FALSE], FALSE],
[[TRUE, TRUE, TRUE], TRUE],
[[FALSE, FALSE, TRUE], TRUE],
[[FALSE, FALSE, FALSE], FALSE],
];
}
/**
* Tests ConfigNamesMapper::getTypeName().
*/
public function testGetTypeName(): void {
$result = $this->configNamesMapper->getTypeName();
$this->assertSame('Settings', (string) $result);
}
/**
* Tests ConfigNamesMapper::hasTranslation().
*/
public function testGetOperations(): void {
$expected = [
'translate' => [
'title' => 'Translate',
'url' => Url::fromRoute('config_translation.item.overview.system.site_information_settings'),
],
];
$result = $this->configNamesMapper->getOperations();
$this->assertEquals($expected, $result);
}
}
/**
* Defines a test mapper class.
*/
class TestConfigNamesMapper extends ConfigNamesMapper {
/**
* Gets the internal language code of this mapper, if any.
*
* This method is not to be confused with
* ConfigMapperInterface::getLangcode().
*
* @return string|null
* The language code of this mapper if it is set; NULL otherwise.
*/
public function getInternalLangcode() {
return $this->langcode ?? NULL;
}
/**
* Sets the list of configuration names.
*
* @param array $config_names
* The configuration names.
*/
public function setConfigNames(array $config_names) {
$this->pluginDefinition['names'] = $config_names;
}
/**
* Sets the configuration factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory to set.
*/
public function setConfigFactory(ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
}
}

View File

@@ -0,0 +1,6 @@
# Attach to performance settings for testing. The base route does not matter.
system.performance_settings:
title: 'Theme translation test'
base_route_name: system.performance_settings
names:
- system.site

View File

@@ -0,0 +1,10 @@
name: 'Configuration Translation Test Theme'
type: theme
base theme: stable9
description: 'Theme for testing the configuration translation mapper system'
# version: VERSION
# Information added by Drupal.org packaging script on 2024-07-04
version: '10.3.1'
project: 'drupal'
datestamp: 1720094222