View source
<?php
namespace Drupal\Tests\config\Functional;
use Drupal\Component\Utility\Html;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Config\InstallStorage;
use Drupal\Tests\BrowserTestBase;
class ConfigImportUITest extends BrowserTestBase {
public static $modules = [
'config',
'config_test',
'config_import_test',
'text',
'options',
];
protected $defaultTheme = 'classy';
protected $webUser;
protected function setUp() {
parent::setUp();
$this->webUser = $this
->drupalCreateUser([
'synchronize configuration',
]);
$this
->drupalLogin($this->webUser);
$this
->copyConfig($this->container
->get('config.storage'), $this->container
->get('config.storage.sync'));
}
public function testImport() {
$name = 'system.site';
$dynamic_name = 'config_test.dynamic.new';
$sync = $this->container
->get('config.storage.sync');
$this
->drupalGet('admin/config/development/configuration');
$this
->assertText('There are no configuration changes to import.');
$this
->assertNoFieldById('edit-submit', t('Import all'));
$new_site_name = 'Config import test ' . $this
->randomString();
$this
->prepareSiteNameUpdate($new_site_name);
$this
->assertIdentical($sync
->exists($name), TRUE, $name . ' found.');
$original_dynamic_data = [
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
'langcode' => \Drupal::languageManager()
->getDefaultLanguage()
->getId(),
'status' => TRUE,
'dependencies' => [],
'id' => 'new',
'label' => 'New',
'weight' => 0,
'style' => '',
'size' => '',
'size_value' => '',
'protected_property' => '',
];
$sync
->write($dynamic_name, $original_dynamic_data);
$this
->assertIdentical($sync
->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
$core_extension = $this
->config('core.extension')
->get();
$core_extension['module']['automated_cron'] = 0;
$core_extension['module']['ban'] = 0;
$core_extension['module'] = module_config_sort($core_extension['module']);
$core_extension['theme']['classy'] = 0;
$core_extension['theme']['bartik'] = 0;
$sync
->write('core.extension', $core_extension);
$install_storage = new InstallStorage();
$system_theme = $this
->config('system.theme')
->get();
$system_theme['default'] = 'bartik';
$sync
->write('system.theme', $system_theme);
$settings = $install_storage
->read('automated_cron.settings');
$settings['interval'] = 10000;
$sync
->write('automated_cron.settings', $settings);
\Drupal::service('module_installer')
->uninstall([
'text',
'options',
]);
\Drupal::state()
->set('ConfigImportUITest.core.extension.modules_installed', []);
\Drupal::state()
->set('ConfigImportUITest.core.extension.modules_uninstalled', []);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertRaw('<td>' . $name);
$this
->assertRaw('<td>' . $dynamic_name);
$this
->assertRaw('<td>core.extension');
$this
->assertRaw('<td>system.theme');
$this
->assertRaw('<td>automated_cron.settings');
$this
->assertFieldById('edit-submit', t('Import all'));
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertNoRaw('<td>' . $name);
$this
->assertNoRaw('<td>' . $dynamic_name);
$this
->assertNoRaw('<td>core.extension');
$this
->assertNoRaw('<td>system.theme');
$this
->assertNoRaw('<td>automated_cron.settings');
$this
->assertNoFieldById('edit-submit', t('Import all'));
$this
->assertText(t('There are no configuration changes to import.'));
$this
->rebuildContainer();
$this
->assertIdentical($new_site_name, $this
->config('system.site')
->get('name'));
$this
->assertIdentical($original_dynamic_data, $this
->config($dynamic_name)
->get());
$this
->assertTrue(isset($GLOBALS['hook_cache_flush']));
$this
->rebuildContainer();
$this
->assertTrue(\Drupal::moduleHandler()
->moduleExists('ban'), 'Ban module installed during import.');
$this
->assertTrue(\Drupal::database()
->schema()
->tableExists('ban_ip'), 'The database table ban_ip exists.');
$this
->assertTrue(\Drupal::moduleHandler()
->moduleExists('automated_cron'), 'Automated Cron module installed during import.');
$this
->assertTrue(\Drupal::moduleHandler()
->moduleExists('options'), 'Options module installed during import.');
$this
->assertTrue(\Drupal::moduleHandler()
->moduleExists('text'), 'Text module installed during import.');
$this
->assertTrue(\Drupal::service('theme_handler')
->themeExists('bartik'), 'Bartik theme installed during import.');
$installed = \Drupal::state()
->get('ConfigImportUITest.core.extension.modules_installed', []);
$uninstalled = \Drupal::state()
->get('ConfigImportUITest.core.extension.modules_uninstalled', []);
$expected = [
'automated_cron',
'ban',
'text',
'options',
];
$this
->assertIdentical($expected, $installed, 'Automated Cron, Ban, Text and Options modules installed in the correct order.');
$this
->assertTrue(empty($uninstalled), 'No modules uninstalled during import');
$interval_values = \Drupal::state()
->get('ConfigImportUITest.automated_cron.settings.interval', []);
$this
->assertIdentical($interval_values, [
10000,
]);
$core_extension = $this
->config('core.extension')
->get();
unset($core_extension['module']['automated_cron']);
unset($core_extension['module']['ban']);
unset($core_extension['module']['options']);
unset($core_extension['module']['text']);
unset($core_extension['theme']['bartik']);
$sync
->write('core.extension', $core_extension);
$sync
->delete('automated_cron.settings');
$sync
->delete('text.settings');
$system_theme = $this
->config('system.theme')
->get();
$system_theme['default'] = 'stark';
$system_theme['admin'] = 'stark';
$sync
->write('system.theme', $system_theme);
\Drupal::state()
->set('ConfigImportUITest.core.extension.modules_installed', []);
\Drupal::state()
->set('ConfigImportUITest.core.extension.modules_uninstalled', []);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertRaw('<td>core.extension');
$this
->assertRaw('<td>system.theme');
$this
->assertRaw('<td>automated_cron.settings');
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertNoRaw('<td>core.extension');
$this
->assertNoRaw('<td>system.theme');
$this
->assertNoRaw('<td>automated_cron.settings');
$this
->rebuildContainer();
$this
->assertFalse(\Drupal::moduleHandler()
->moduleExists('ban'), 'Ban module uninstalled during import.');
$this
->assertFalse(\Drupal::database()
->schema()
->tableExists('ban_ip'), 'The database table ban_ip does not exist.');
$this
->assertFalse(\Drupal::moduleHandler()
->moduleExists('automated_cron'), 'Automated cron module uninstalled during import.');
$this
->assertFalse(\Drupal::moduleHandler()
->moduleExists('options'), 'Options module uninstalled during import.');
$this
->assertFalse(\Drupal::moduleHandler()
->moduleExists('text'), 'Text module uninstalled during import.');
$installed = \Drupal::state()
->get('ConfigImportUITest.core.extension.modules_installed', []);
$uninstalled = \Drupal::state()
->get('ConfigImportUITest.core.extension.modules_uninstalled', []);
$expected = [
'options',
'text',
'ban',
'automated_cron',
];
$this
->assertIdentical($expected, $uninstalled, 'Options, Text, Ban and Automated Cron modules uninstalled in the correct order.');
$this
->assertTrue(empty($installed), 'No modules installed during import');
$theme_info = \Drupal::service('theme_handler')
->listInfo();
$this
->assertFalse(isset($theme_info['bartik']), 'Bartik theme uninstalled during import.');
$delete_called = \Drupal::state()
->get('ConfigImportUITest.automated_cron.settings.delete', 0);
$this
->assertIdentical($delete_called, 1, "The automated_cron.settings configuration was deleted once during configuration import.");
}
public function testImportLock() {
$new_site_name = 'Config import test ' . $this
->randomString();
$this
->prepareSiteNameUpdate($new_site_name);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertNoText(t('There are no configuration changes to import.'));
$config_importer = $this
->configImporter();
$this->container
->get('lock.persistent')
->acquire($config_importer::LOCK_NAME);
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertText(t('Another request may be synchronizing configuration already.'));
$this->container
->get('lock.persistent')
->release($config_importer::LOCK_NAME);
$this
->assertNotEqual($new_site_name, $this
->config('system.site')
->get('name'));
}
public function testImportSiteUuidValidation() {
$sync = \Drupal::service('config.storage.sync');
$config_data = $this
->config('system.site')
->get();
$config_data['uuid'] = \Drupal::service('uuid')
->generate();
$sync
->write('system.site', $config_data);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertText(t('The staged configuration cannot be imported, because it originates from a different site than this site. You can only synchronize configuration between cloned instances of this site.'));
$this
->assertNoFieldById('edit-submit', t('Import all'));
}
public function testImportDiff() {
$sync = $this->container
->get('config.storage.sync');
$config_name = 'config_test.system';
$change_key = 'foo';
$remove_key = '404';
$add_key = 'biff';
$add_data = '<em>bangpow</em>';
$change_data = '<p><em>foobar</em></p>';
$original_data = [
'foo' => '<p>foobar</p>',
'baz' => '<strong>no change</strong>',
'404' => '<em>herp</em>',
];
$this
->config($config_name)
->setData($original_data)
->save();
$sync_data = $original_data;
$sync_data[$change_key] = $change_data;
$sync_data[$add_key] = $add_data;
unset($sync_data[$remove_key]);
$sync
->write($config_name, $sync_data);
$this
->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
$this
->assertNoRaw('&nbsp;');
$this
->assertTitle("View changes of {$config_name} | Drupal");
$this
->assertText(Html::escape("foo: '<p><em>foobar</em></p>'"));
$this
->assertText(Html::escape("foo: '<p>foobar</p>'"));
$this
->assertText(Html::escape("baz: '<strong>no change</strong>'"));
$this
->assertText(Html::escape("biff: '<em>bangpow</em>'"));
$this
->assertText(Html::escape("404: '<em>herp</em>'"));
$result = $this
->xpath('//table[contains(@class, :class)]', [
':class' => 'diff',
]);
$this
->assertCount(1, $result, "Diff UI is displaying colors.");
$sync_data = $original_data;
unset($sync_data[$remove_key]);
$sync
->write($config_name, $sync_data);
$this
->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
$this
->assertText(Html::escape("foo: '<p>foobar</p>'"));
$this
->assertText(Html::escape("baz: '<strong>no change</strong>'"));
$this
->assertText(Html::escape("404: '<em>herp</em>'"));
$sync_data = $original_data;
$sync_data[$add_key] = $add_data;
$sync
->write($config_name, $sync_data);
$this
->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
$this
->assertText(Html::escape("baz: '<strong>no change</strong>'"));
$this
->assertText(Html::escape("404: '<em>herp</em>'"));
$this
->assertText(Html::escape("biff: '<em>bangpow</em>'"));
}
public function testImportValidation() {
\Drupal::state()
->set('config_import_test.config_import_validate_fail', TRUE);
$new_site_name = 'Config import test ' . $this
->randomString();
$this
->prepareSiteNameUpdate($new_site_name);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertNoText(t('There are no configuration changes to import.'));
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertText('The configuration cannot be imported because it failed validation for the following reasons:');
$this
->assertText('Config import validate error 1.');
$this
->assertText('Config import validate error 2.');
$this
->assertNotEqual($new_site_name, $this
->config('system.site')
->get('name'));
}
public function testConfigUninstallConfigException() {
$sync = $this->container
->get('config.storage.sync');
$core_extension = $this
->config('core.extension')
->get();
unset($core_extension['module']['config']);
$sync
->write('core.extension', $core_extension);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertText('core.extension');
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertText('Can not uninstall the Configuration module as part of a configuration synchronization through the user interface.');
}
public function prepareSiteNameUpdate($new_site_name) {
$sync = $this->container
->get('config.storage.sync');
$config_data = $this
->config('system.site')
->get();
$config_data['name'] = $new_site_name;
$sync
->write('system.site', $config_data);
}
public function testImportErrorLog() {
$name_primary = 'config_test.dynamic.primary';
$name_secondary = 'config_test.dynamic.secondary';
$sync = $this->container
->get('config.storage.sync');
$uuid = $this->container
->get('uuid');
$values_primary = [
'uuid' => $uuid
->generate(),
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [],
'id' => 'primary',
'label' => 'Primary',
'weight' => 0,
'style' => NULL,
'size' => NULL,
'size_value' => NULL,
'protected_property' => NULL,
];
$sync
->write($name_primary, $values_primary);
$values_secondary = [
'uuid' => $uuid
->generate(),
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [
'config' => [
$name_primary,
],
],
'id' => 'secondary',
'label' => 'Secondary Sync',
'weight' => 0,
'style' => NULL,
'size' => NULL,
'size_value' => NULL,
'protected_property' => NULL,
];
$sync
->write($name_secondary, $values_secondary);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertNoText(t('There are no configuration changes to import.'));
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertText(new FormattableMarkup('Deleted and replaced configuration entity "@name"', [
'@name' => $name_secondary,
]));
$this
->assertText(t('The configuration was imported with errors.'));
$this
->assertNoText(t('The configuration was imported successfully.'));
$this
->assertText(t('There are no configuration changes to import.'));
}
public function testEntityBundleDelete() {
\Drupal::service('module_installer')
->install([
'node',
]);
$this
->copyConfig($this->container
->get('config.storage'), $this->container
->get('config.storage.sync'));
$node_type = $this
->drupalCreateContentType();
$node = $this
->drupalCreateNode([
'type' => $node_type
->id(),
]);
$this
->drupalGet('admin/config/development/configuration');
$this
->assertText(new FormattableMarkup('node.type.@type', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('field.field.node.@type.body', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('core.entity_view_display.node.@type.teaser', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('core.entity_view_display.node.@type.default', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('core.entity_form_display.node.@type.default', [
'@type' => $node_type
->id(),
]));
$this
->drupalPostForm(NULL, [], t('Import all'));
$validation_message = t('Entities exist of type %entity_type and %bundle_label %bundle. These entities need to be deleted before importing.', [
'%entity_type' => $node
->getEntityType()
->getLabel(),
'%bundle_label' => $node
->getEntityType()
->getBundleLabel(),
'%bundle' => $node_type
->label(),
]);
$this
->assertRaw($validation_message);
$this
->assertText(new FormattableMarkup('node.type.@type', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('field.field.node.@type.body', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('core.entity_view_display.node.@type.teaser', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('core.entity_view_display.node.@type.default', [
'@type' => $node_type
->id(),
]));
$this
->assertText(new FormattableMarkup('core.entity_form_display.node.@type.default', [
'@type' => $node_type
->id(),
]));
$node
->delete();
$this
->drupalPostForm(NULL, [], t('Import all'));
$this
->assertNoRaw($validation_message);
$this
->assertText(t('There are no configuration changes to import.'));
$this
->assertNoText(new FormattableMarkup('node.type.@type', [
'@type' => $node_type
->id(),
]));
$this
->assertNoText(new FormattableMarkup('field.field.node.@type.body', [
'@type' => $node_type
->id(),
]));
$this
->assertNoText(new FormattableMarkup('core.entity_view_display.node.@type.teaser', [
'@type' => $node_type
->id(),
]));
$this
->assertNoText(new FormattableMarkup('core.entity_view_display.node.@type.default', [
'@type' => $node_type
->id(),
]));
$this
->assertNoText(new FormattableMarkup('core.entity_form_display.node.@type.default', [
'@type' => $node_type
->id(),
]));
}
public function testExtensionValidation() {
\Drupal::service('module_installer')
->install([
'node',
]);
\Drupal::service('theme_installer')
->install([
'bartik',
]);
$this
->rebuildContainer();
$sync = $this->container
->get('config.storage.sync');
$this
->copyConfig($this->container
->get('config.storage'), $sync);
$core = $sync
->read('core.extension');
unset($core['module']['text']);
$module_data = $this->container
->get('extension.list.module')
->getList();
$this
->assertTrue(isset($module_data['node']->requires['text']), 'The Node module depends on the Text module.');
unset($core['theme']['classy']);
$theme_data = \Drupal::service('theme_handler')
->rebuildThemeData();
$this
->assertTrue(isset($theme_data['bartik']->requires['classy']), 'The Bartik theme depends on the Classy theme.');
$core['module']['does_not_exist'] = 0;
$core['theme']['does_not_exist'] = 0;
$sync
->write('core.extension', $core);
$this
->drupalPostForm('admin/config/development/configuration', [], t('Import all'));
$this
->assertText('The configuration cannot be imported because it failed validation for the following reasons:');
$this
->assertText('Unable to uninstall the Text module since the Node module is installed.');
$this
->assertText('Unable to uninstall the Classy theme since the Bartik theme is installed.');
$this
->assertText('Unable to install the does_not_exist module since it does not exist.');
$this
->assertText('Unable to install the does_not_exist theme since it does not exist.');
}
public function testBatchErrors() {
$new_site_name = 'Config import test ' . $this
->randomString();
$this
->prepareSiteNameUpdate($new_site_name);
\Drupal::state()
->set('config_import_steps_alter.error', TRUE);
$this
->drupalPostForm('admin/config/development/configuration', [], t('Import all'));
$this
->assertSession()
->responseContains('_config_import_test_config_import_steps_alter batch error');
$this
->assertSession()
->responseContains('_config_import_test_config_import_steps_alter ConfigImporter error');
$this
->assertSession()
->responseContains('The configuration was imported with errors.');
}
}