Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -6,4 +6,4 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_collection_install_test
|
||||
- drupal:config_collection_install_test
|
||||
|
|
|
@ -1 +1 @@
|
|||
collection: another_collection
|
||||
collection: another_collection
|
||||
|
|
|
@ -1 +1 @@
|
|||
collection: collection.test1
|
||||
collection: collection.test1
|
||||
|
|
|
@ -1 +1 @@
|
|||
collection: collection.test2
|
||||
collection: collection.test2
|
||||
|
|
|
@ -18,7 +18,7 @@ class ConfigOverrider implements ConfigFactoryOverrideInterface {
|
|||
return [
|
||||
'config_test.dynamic.test_1' => [
|
||||
'label' => 'Overridden label',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Drupal\config_events_test;
|
||||
|
||||
|
||||
use Drupal\Core\Config\ConfigCrudEvent;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\Core\State\StateInterface;
|
||||
|
@ -41,7 +40,7 @@ class EventSubscriber implements EventSubscriberInterface {
|
|||
'event_name' => $name,
|
||||
'current_config_data' => $config->get(),
|
||||
'original_config_data' => $config->getOriginal(),
|
||||
'raw_config_data' => $config->getRawData()
|
||||
'raw_config_data' => $config->getRawData(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* Provides configuration import test helpers.
|
||||
*/
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
|
||||
/**
|
||||
* Implements hook_config_import_steps_alter().
|
||||
*/
|
||||
|
@ -17,9 +19,15 @@ function config_import_test_config_import_steps_alter(&$sync_steps) {
|
|||
*
|
||||
* @param array $context
|
||||
* The batch context.
|
||||
* @param \Drupal\Core\Config\ConfigImporter $config_importer
|
||||
* The configuration importer.
|
||||
*/
|
||||
function _config_import_test_config_import_steps_alter(&$context) {
|
||||
function _config_import_test_config_import_steps_alter(&$context, ConfigImporter $config_importer) {
|
||||
$GLOBALS['hook_config_test']['config_import_steps_alter'] = TRUE;
|
||||
if (\Drupal::state()->get('config_import_steps_alter.error', FALSE)) {
|
||||
$context['results']['errors'][] = '_config_import_test_config_import_steps_alter batch error';
|
||||
$config_importer->logError('_config_import_test_config_import_steps_alter ConfigImporter error');
|
||||
}
|
||||
$context['finished'] = 1;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
id: dependency_for_unmet2
|
||||
label: 'Other module test to test optional config installation'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: true
|
||||
langcode: en
|
||||
protected_property: Default
|
|
@ -4,4 +4,4 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_test
|
||||
- drupal:config_test
|
||||
|
|
|
@ -4,4 +4,4 @@ package: 'Testing'
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_test
|
||||
- drupal:config_test
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
id: other_module_test_optional_entity_unmet2
|
||||
label: 'Other module test to test optional config installation'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: true
|
||||
langcode: en
|
||||
protected_property: Default
|
||||
dependencies:
|
||||
enforced:
|
||||
config:
|
||||
- config_test.dynamic.dependency_for_unmet2
|
|
@ -5,5 +5,5 @@ version: VERSION
|
|||
core: 8.x
|
||||
|
||||
dependencies:
|
||||
- block
|
||||
- block_test
|
||||
- drupal:block
|
||||
- drupal:block_test
|
||||
|
|
|
@ -5,5 +5,5 @@ version: VERSION
|
|||
core: 8.x
|
||||
|
||||
dependencies:
|
||||
- block
|
||||
- block_content
|
||||
- drupal:block
|
||||
- drupal:block_content
|
||||
|
|
|
@ -18,13 +18,13 @@ class ConfigOverriderLowPriority implements ConfigFactoryOverrideInterface {
|
|||
$overrides = [];
|
||||
if (!empty($GLOBALS['config_test_run_module_overrides'])) {
|
||||
if (in_array('system.site', $names)) {
|
||||
$overrides = ['system.site' =>
|
||||
[
|
||||
$overrides = [
|
||||
'system.site' => [
|
||||
'name' => 'Should not apply because of higher priority listener',
|
||||
// This override should apply because it is not overridden by the
|
||||
// higher priority listener.
|
||||
'slogan' => 'Yay for overrides!',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,17 +61,4 @@ class PirateDayCacheabilityMetadataConfigOverride implements ConfigFactoryOverri
|
|||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not our overrides are potentially applicable.
|
||||
*
|
||||
* @param string $name
|
||||
* The name of the config object that is being constructed.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the merchant ship will be boarded. FALSE if we drink rum instead.
|
||||
*/
|
||||
protected function isCacheabilityMetadataApplicable($name) {
|
||||
return in_array($name, ['system.theme', 'block.block.call_to_action']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -297,3 +297,41 @@ test.double_brackets.breed:
|
|||
mapping:
|
||||
breed:
|
||||
type: string
|
||||
|
||||
config_schema_test.schema_sequence_sort:
|
||||
type: config_object
|
||||
mapping:
|
||||
keyed_sort:
|
||||
type: sequence
|
||||
orderby: key
|
||||
sequence:
|
||||
- type: string
|
||||
value_sort:
|
||||
type: sequence
|
||||
orderby: value
|
||||
sequence:
|
||||
- type: string
|
||||
no_sort:
|
||||
type: sequence
|
||||
sequence:
|
||||
- type: string
|
||||
complex_sort_value:
|
||||
type: sequence
|
||||
orderby: value
|
||||
sequence:
|
||||
- type: mapping
|
||||
mapping:
|
||||
foo:
|
||||
type: string
|
||||
bar:
|
||||
type: string
|
||||
complex_sort_key:
|
||||
type: sequence
|
||||
orderby: key
|
||||
sequence:
|
||||
- type: mapping
|
||||
mapping:
|
||||
foo:
|
||||
type: string
|
||||
bar:
|
||||
type: string
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
llama: llama
|
||||
cat:
|
||||
type: kitten
|
||||
count: 2
|
||||
giraffe:
|
||||
hum1: hum1
|
||||
hum2: hum2
|
||||
uuid: '7C30C50E-641A-4E34-A7F1-46BCFB9BE5A3'
|
|
@ -135,8 +135,7 @@ config_test.new:
|
|||
type: string
|
||||
label: 'Test setting'
|
||||
uuid:
|
||||
type: string
|
||||
label: 'UUID'
|
||||
type: uuid
|
||||
|
||||
config_test.old:
|
||||
type: config_test.new
|
||||
|
@ -158,3 +157,49 @@ config_test.foo:
|
|||
|
||||
config_test.bar:
|
||||
type: config_test.foo
|
||||
|
||||
system.action.*.third_party.config_test:
|
||||
type: mapping
|
||||
label: 'Third party setting for action entity'
|
||||
mapping:
|
||||
integer:
|
||||
type: integer
|
||||
label: 'Integer'
|
||||
|
||||
config_test.validation:
|
||||
type: config_object
|
||||
label: 'Configuration type'
|
||||
constraints:
|
||||
Callback:
|
||||
callback: [\Drupal\config_test\ConfigValidation, validateMapping]
|
||||
mapping:
|
||||
llama:
|
||||
type: string
|
||||
constraints:
|
||||
Callback:
|
||||
callback: [\Drupal\config_test\ConfigValidation, validateLlama]
|
||||
cat:
|
||||
type: mapping
|
||||
mapping:
|
||||
type:
|
||||
type: string
|
||||
constraints:
|
||||
Callback:
|
||||
callback: [\Drupal\config_test\ConfigValidation, validateCats]
|
||||
count:
|
||||
type: integer
|
||||
constraints:
|
||||
Callback:
|
||||
callback: [\Drupal\config_test\ConfigValidation, validateCatCount]
|
||||
giraffe:
|
||||
type: sequence
|
||||
constraints:
|
||||
Callback:
|
||||
callback: [\Drupal\config_test\ConfigValidation, validateSequence]
|
||||
sequence:
|
||||
type: string
|
||||
constraints:
|
||||
Callback:
|
||||
callback: [\Drupal\config_test\ConfigValidation, validateGiraffes]
|
||||
uuid:
|
||||
type: uuid
|
||||
|
|
|
@ -8,6 +8,8 @@ use Drupal\Core\Language\LanguageInterface;
|
|||
|
||||
/**
|
||||
* Form controller for the test config edit forms.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ConfigTestForm extends EntityForm {
|
||||
|
||||
|
@ -136,10 +138,10 @@ class ConfigTestForm extends EntityForm {
|
|||
$status = $entity->save();
|
||||
|
||||
if ($status === SAVED_UPDATED) {
|
||||
drupal_set_message(format_string('%label configuration has been updated.', ['%label' => $entity->label()]));
|
||||
$this->messenger()->addStatus(format_string('%label configuration has been updated.', ['%label' => $entity->label()]));
|
||||
}
|
||||
else {
|
||||
drupal_set_message(format_string('%label configuration has been created.', ['%label' => $entity->label()]));
|
||||
$this->messenger()->addStatus(format_string('%label configuration has been created.', ['%label' => $entity->label()]));
|
||||
}
|
||||
|
||||
$form_state->setRedirectUrl($this->entity->urlInfo('collection'));
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\config_test;
|
||||
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* Provides a collection of validation callbacks for testing purposes.
|
||||
*/
|
||||
class ConfigValidation {
|
||||
|
||||
/**
|
||||
* Validates a llama.
|
||||
*
|
||||
* @param string $string
|
||||
* The string to validate.
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The validation execution context.
|
||||
*/
|
||||
public static function validateLlama($string, ExecutionContextInterface $context) {
|
||||
if (!in_array($string, ['llama', 'alpaca', 'guanaco', 'vicuña'], TRUE)) {
|
||||
$context->addViolation('no valid llama');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates cats.
|
||||
*
|
||||
* @param string $string
|
||||
* The string to validate.
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The validation execution context.
|
||||
*/
|
||||
public static function validateCats($string, ExecutionContextInterface $context) {
|
||||
if (!in_array($string, ['kitten', 'cats', 'nyans'])) {
|
||||
$context->addViolation('no valid cat');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a number.
|
||||
*
|
||||
* @param int $count
|
||||
* The integer to validate.
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The validation execution context.
|
||||
*/
|
||||
public static function validateCatCount($count, ExecutionContextInterface $context) {
|
||||
if ($count <= 1) {
|
||||
$context->addViolation('no enough cats');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates giraffes.
|
||||
*
|
||||
* @param string $string
|
||||
* The string to validate.
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The validation execution context.
|
||||
*/
|
||||
public static function validateGiraffes($string, ExecutionContextInterface $context) {
|
||||
if (strpos($string, 'hum') !== 0) {
|
||||
$context->addViolation('Giraffes just hum');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a mapping.
|
||||
*
|
||||
* @param array $mapping
|
||||
* The data to validate.
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The validation execution context.
|
||||
*/
|
||||
public static function validateMapping($mapping, ExecutionContextInterface $context) {
|
||||
// Ensure we are validating the entire mapping by diffing against all the
|
||||
// keys.
|
||||
$mapping_schema = \Drupal::service('config.typed')->get('config_test.validation')->getValue();
|
||||
if ($diff = array_diff_key($mapping, $mapping_schema)) {
|
||||
$context->addViolation('Unexpected keys: ' . implode(', ', array_keys($diff)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a sequence.
|
||||
*
|
||||
* @param array $sequence
|
||||
* The data to validate.
|
||||
* @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
|
||||
* The validation execution context.
|
||||
*/
|
||||
public static function validateSequence($sequence, ExecutionContextInterface $context) {
|
||||
if (isset($sequence['invalid-key'])) {
|
||||
$context->addViolation('Invalid giraffe key.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -113,13 +113,27 @@ class ConfigTest extends ConfigEntityBase implements ConfigTestInterface {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
parent::calculateDependencies();
|
||||
if ($module = \Drupal::state()->get('config_test_new_dependency', FALSE)) {
|
||||
$this->addDependency('module', $module);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function onDependencyRemoval(array $dependencies) {
|
||||
// Record which entities have this method called on.
|
||||
// Record which entities have this method called on and what dependencies
|
||||
// are passed.
|
||||
$called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
|
||||
$called[] = $this->id();
|
||||
$called[$this->id()] = $dependencies;
|
||||
$called[$this->id()]['config'] = array_keys($called[$this->id()]['config']);
|
||||
$called[$this->id()]['content'] = array_keys($called[$this->id()]['content']);
|
||||
\Drupal::state()->set('config_test.on_dependency_removal_called', $called);
|
||||
|
||||
$changed = parent::onDependencyRemoval($dependencies);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\config_test\Functional\Rest\ConfigTestResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ConfigTestHalJsonAnonTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\config_test\Functional\Rest\ConfigTestResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ConfigTestHalJsonBasicAuthTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal', 'basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Hal;
|
||||
|
||||
use Drupal\Tests\config_test\Functional\Rest\ConfigTestResourceTestBase;
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group hal
|
||||
*/
|
||||
class ConfigTestHalJsonCookieTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['hal'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'hal_json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/hal+json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestJsonAnonTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestJsonBasicAuthTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestJsonCookieTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'application/json';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\config_test\Entity\ConfigTest;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
|
||||
|
||||
abstract class ConfigTestResourceTestBase extends EntityResourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['config_test', 'config_test_rest'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $entityTypeId = 'config_test';
|
||||
|
||||
/**
|
||||
* @var \Drupal\config_test\ConfigTestInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUpAuthorization($method) {
|
||||
$this->grantPermissionsToTestedRole(['view config_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function createEntity() {
|
||||
$config_test = ConfigTest::create([
|
||||
'id' => 'llama',
|
||||
'label' => 'Llama',
|
||||
]);
|
||||
$config_test->save();
|
||||
|
||||
return $config_test;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedNormalizedEntity() {
|
||||
$normalization = [
|
||||
'uuid' => $this->entity->uuid(),
|
||||
'id' => 'llama',
|
||||
'weight' => 0,
|
||||
'langcode' => 'en',
|
||||
'status' => TRUE,
|
||||
'dependencies' => [],
|
||||
'label' => 'Llama',
|
||||
'style' => NULL,
|
||||
'size' => NULL,
|
||||
'size_value' => NULL,
|
||||
'protected_property' => NULL,
|
||||
];
|
||||
|
||||
return $normalization;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getNormalizedPostEntity() {
|
||||
// @todo Update in https://www.drupal.org/node/2300677.
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestXmlAnonTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use AnonResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestXmlBasicAuthTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use BasicAuthResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['basic_auth'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'basic_auth';
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config_test\Functional\Rest;
|
||||
|
||||
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
|
||||
use Drupal\Tests\rest\Functional\EntityResource\XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* @group rest
|
||||
*/
|
||||
class ConfigTestXmlCookieTest extends ConfigTestResourceTestBase {
|
||||
|
||||
use CookieResourceTestTrait;
|
||||
use XmlEntityNormalizationQuirksTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $format = 'xml';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $mimeType = 'text/xml; charset=UTF-8';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $auth = 'cookie';
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
id: does_not_match
|
||||
label: Default
|
||||
weight: 0
|
||||
protected_property: Default
|
||||
langcode: en
|
|
@ -0,0 +1,7 @@
|
|||
name: 'Configuration test ID mismatch'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:config_test
|
|
@ -4,4 +4,4 @@ package: Testing
|
|||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- config_test
|
||||
- drupal:config_test
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests if configuration overrides correctly affect cacheability metadata.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class CacheabilityMetadataConfigOverrideIntegrationTest extends BrowserTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'block_test',
|
||||
'config_override_integration_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// @todo If our block does not contain any content then the cache context
|
||||
// is not bubbling up and the test fails. Remove this line once the cache
|
||||
// contexts are properly set. See https://www.drupal.org/node/2529980.
|
||||
\Drupal::state()->set('block_test.content', 'Needs to have some content');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if config overrides correctly set cacheability metadata.
|
||||
*/
|
||||
public function testConfigOverride() {
|
||||
// Check the default (disabled) state of the cache context. The block label
|
||||
// should not be overridden.
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertNoText('Overridden block label');
|
||||
|
||||
// Both the cache context and tag should be present.
|
||||
$this->assertCacheContext('config_override_integration_test');
|
||||
$this->assertCacheTag('config_override_integration_test_tag');
|
||||
|
||||
// Flip the state of the cache context. The block label should now be
|
||||
// overridden.
|
||||
\Drupal::state()->set('config_override_integration_test.enabled', TRUE);
|
||||
$this->drupalGet('<front>');
|
||||
$this->assertText('Overridden block label');
|
||||
|
||||
// Both the cache context and tag should still be present.
|
||||
$this->assertCacheContext('config_override_integration_test');
|
||||
$this->assertCacheTag('config_override_integration_test_tag');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests that config overrides do not bleed through in entity forms and lists.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityFormOverrideTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['config_test'];
|
||||
|
||||
/**
|
||||
* Tests that overrides do not affect forms or listing screens.
|
||||
*/
|
||||
public function testFormsWithOverrides() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$original_label = 'Default';
|
||||
$overridden_label = 'Overridden label';
|
||||
$edited_label = 'Edited label';
|
||||
|
||||
$config_test_storage = $this->container->get('entity.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->assertEqual($config_test_storage->load('dotted.default')->label(), $overridden_label);
|
||||
|
||||
// Test that the original label on the listing page is intact.
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
$this->assertText($original_label);
|
||||
$this->assertNoText($overridden_label);
|
||||
|
||||
// Test that the original label on the editing page is intact.
|
||||
$this->drupalGet('admin/structure/config_test/manage/dotted.default');
|
||||
$elements = $this->xpath('//input[@name="label"]');
|
||||
$this->assertIdentical($elements[0]->getValue(), $original_label);
|
||||
$this->assertNoText($overridden_label);
|
||||
|
||||
// Change to a new label and test that the listing now has the edited label.
|
||||
$edit = [
|
||||
'label' => $edited_label,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
$this->assertNoText($overridden_label);
|
||||
$this->assertText($edited_label);
|
||||
|
||||
// Test that the editing page now has the edited label.
|
||||
$this->drupalGet('admin/structure/config_test/manage/dotted.default');
|
||||
$elements = $this->xpath('//input[@name="label"]');
|
||||
$this->assertIdentical($elements[0]->getValue(), $edited_label);
|
||||
|
||||
// Test that the overridden label is still loaded with the entity.
|
||||
$this->assertEqual($config_test_storage->load('dotted.default')->label(), $overridden_label);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Core\Routing\RedirectDestinationTrait;
|
||||
use Drupal\config_test\Entity\ConfigTest;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the listing of configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityListTest extends BrowserTestBase {
|
||||
|
||||
use RedirectDestinationTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['block', 'config_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Delete the override config_test entity since it is not required by this
|
||||
// test.
|
||||
\Drupal::entityManager()->getStorage('config_test')->load('override')->delete();
|
||||
$this->drupalPlaceBlock('local_actions_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity list builder methods.
|
||||
*/
|
||||
public function testList() {
|
||||
$controller = \Drupal::entityManager()->getListBuilder('config_test');
|
||||
|
||||
// Test getStorage() method.
|
||||
$this->assertTrue($controller->getStorage() instanceof EntityStorageInterface, 'EntityStorage instance in storage.');
|
||||
|
||||
// Get a list of ConfigTest entities and confirm that it contains the
|
||||
// ConfigTest entity provided by the config_test module.
|
||||
// @see config_test.dynamic.dotted.default.yml
|
||||
$list = $controller->load();
|
||||
$this->assertEqual(count($list), 1, '1 ConfigTest entity found.');
|
||||
$entity = $list['dotted.default'];
|
||||
$this->assertTrue(!empty($entity), '"Default" ConfigTest entity ID found.');
|
||||
$this->assertTrue($entity instanceof ConfigTest, '"Default" ConfigTest entity is an instance of ConfigTest.');
|
||||
|
||||
// Test getOperations() method.
|
||||
$expected_operations = [
|
||||
'edit' => [
|
||||
'title' => t('Edit'),
|
||||
'weight' => 10,
|
||||
'url' => $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()),
|
||||
],
|
||||
'disable' => [
|
||||
'title' => t('Disable'),
|
||||
'weight' => 40,
|
||||
'url' => $entity->toUrl('disable')->setOption('query', $this->getRedirectDestination()->getAsArray()),
|
||||
],
|
||||
'delete' => [
|
||||
'title' => t('Delete'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()),
|
||||
],
|
||||
];
|
||||
|
||||
$actual_operations = $controller->getOperations($entity);
|
||||
// Sort the operations to normalize link order.
|
||||
uasort($actual_operations, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
|
||||
$this->assertEqual($expected_operations, $actual_operations, 'The operations are identical.');
|
||||
|
||||
// Test buildHeader() method.
|
||||
$expected_items = [
|
||||
'label' => 'Label',
|
||||
'id' => 'Machine name',
|
||||
'operations' => 'Operations',
|
||||
];
|
||||
$actual_items = $controller->buildHeader();
|
||||
$this->assertEqual($expected_items, $actual_items, 'Return value from buildHeader matches expected.');
|
||||
|
||||
// Test buildRow() method.
|
||||
$build_operations = $controller->buildOperations($entity);
|
||||
$expected_items = [
|
||||
'label' => 'Default',
|
||||
'id' => 'dotted.default',
|
||||
'operations' => [
|
||||
'data' => $build_operations,
|
||||
],
|
||||
];
|
||||
$actual_items = $controller->buildRow($entity);
|
||||
$this->assertEqual($expected_items, $actual_items, 'Return value from buildRow matches expected.');
|
||||
// Test sorting.
|
||||
$storage = $controller->getStorage();
|
||||
$entity = $storage->create([
|
||||
'id' => 'alpha',
|
||||
'label' => 'Alpha',
|
||||
'weight' => 1,
|
||||
]);
|
||||
$entity->save();
|
||||
$entity = $storage->create([
|
||||
'id' => 'omega',
|
||||
'label' => 'Omega',
|
||||
'weight' => 1,
|
||||
]);
|
||||
$entity->save();
|
||||
$entity = $storage->create([
|
||||
'id' => 'beta',
|
||||
'label' => 'Beta',
|
||||
'weight' => 0,
|
||||
]);
|
||||
$entity->save();
|
||||
$list = $controller->load();
|
||||
$this->assertIdentical(array_keys($list), ['beta', 'dotted.default', 'alpha', 'omega']);
|
||||
|
||||
// Test that config entities that do not support status, do not have
|
||||
// enable/disable operations.
|
||||
$controller = $this->container->get('entity.manager')
|
||||
->getListBuilder('config_test_no_status');
|
||||
|
||||
$list = $controller->load();
|
||||
$entity = $list['default'];
|
||||
|
||||
// Test getOperations() method.
|
||||
$expected_operations = [
|
||||
'edit' => [
|
||||
'title' => t('Edit'),
|
||||
'weight' => 10,
|
||||
'url' => $entity->toUrl()->setOption('query', $this->getRedirectDestination()->getAsArray()),
|
||||
],
|
||||
'delete' => [
|
||||
'title' => t('Delete'),
|
||||
'weight' => 100,
|
||||
'url' => $entity->toUrl('delete-form')->setOption('query', $this->getRedirectDestination()->getAsArray()),
|
||||
],
|
||||
];
|
||||
|
||||
$actual_operations = $controller->getOperations($entity);
|
||||
// Sort the operations to normalize link order.
|
||||
uasort($actual_operations, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
|
||||
$this->assertEqual($expected_operations, $actual_operations, 'The operations are identical.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the listing UI.
|
||||
*/
|
||||
public function testListUI() {
|
||||
// Log in as an administrative user to access the full menu trail.
|
||||
$this->drupalLogin($this->drupalCreateUser(['access administration pages', 'administer site configuration']));
|
||||
|
||||
// Get the list callback page.
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
|
||||
// Test for the page title.
|
||||
$this->assertTitle('Test configuration | Drupal');
|
||||
|
||||
// Test for the table.
|
||||
$element = $this->xpath('//div[@class="layout-content"]//table');
|
||||
$this->assertTrue($element, 'Configuration entity list table found.');
|
||||
|
||||
// Test the table header.
|
||||
$elements = $this->xpath('//div[@class="layout-content"]//table/thead/tr/th');
|
||||
$this->assertEqual(count($elements), 3, 'Correct number of table header cells found.');
|
||||
|
||||
// Test the contents of each th cell.
|
||||
$expected_items = ['Label', 'Machine name', 'Operations'];
|
||||
foreach ($elements as $key => $element) {
|
||||
$this->assertIdentical($element->getText(), $expected_items[$key]);
|
||||
}
|
||||
|
||||
// Check the number of table row cells.
|
||||
$elements = $this->xpath('//div[@class="layout-content"]//table/tbody/tr[@class="odd"]/td');
|
||||
$this->assertEqual(count($elements), 3, 'Correct number of table row cells found.');
|
||||
|
||||
// Check the contents of each row cell. The first cell contains the label,
|
||||
// the second contains the machine name, and the third contains the
|
||||
// operations list.
|
||||
$this->assertIdentical($elements[0]->getText(), 'Default');
|
||||
$this->assertIdentical($elements[1]->getText(), 'dotted.default');
|
||||
$this->assertNotEmpty($elements[2]->find('xpath', '//ul'), 'Operations list found.');
|
||||
|
||||
// Add a new entity using the operations link.
|
||||
$this->assertLink('Add test configuration');
|
||||
$this->clickLink('Add test configuration');
|
||||
$this->assertResponse(200);
|
||||
$edit = [
|
||||
'label' => 'Antelope',
|
||||
'id' => 'antelope',
|
||||
'weight' => 1,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Ensure that the entity's sort method was called.
|
||||
$this->assertTrue(\Drupal::state()->get('config_entity_sort'), 'ConfigTest::sort() was called.');
|
||||
|
||||
// Confirm that the user is returned to the listing, and verify that the
|
||||
// text of the label and machine name appears in the list (versus elsewhere
|
||||
// on the page).
|
||||
$this->assertFieldByXpath('//td', 'Antelope', "Label found for added 'Antelope' entity.");
|
||||
$this->assertFieldByXpath('//td', 'antelope', "Machine name found for added 'Antelope' entity.");
|
||||
|
||||
// Edit the entity using the operations link.
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/antelope');
|
||||
$this->clickLink('Edit', 1);
|
||||
$this->assertResponse(200);
|
||||
$this->assertTitle('Edit Antelope | Drupal');
|
||||
$edit = ['label' => 'Albatross', 'id' => 'albatross'];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
|
||||
// Confirm that the user is returned to the listing, and verify that the
|
||||
// text of the label and machine name appears in the list (versus elsewhere
|
||||
// on the page).
|
||||
$this->assertFieldByXpath('//td', 'Albatross', "Label found for updated 'Albatross' entity.");
|
||||
$this->assertFieldByXpath('//td', 'albatross', "Machine name found for updated 'Albatross' entity.");
|
||||
|
||||
// Delete the added entity using the operations link.
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/albatross/delete');
|
||||
$this->clickLink('Delete', 1);
|
||||
$this->assertResponse(200);
|
||||
$this->assertTitle('Are you sure you want to delete the test configuration Albatross? | Drupal');
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
|
||||
// Verify that the text of the label and machine name does not appear in
|
||||
// the list (though it may appear elsewhere on the page).
|
||||
$this->assertNoFieldByXpath('//td', 'Albatross', "No label found for deleted 'Albatross' entity.");
|
||||
$this->assertNoFieldByXpath('//td', 'albatross', "No machine name found for deleted 'Albatross' entity.");
|
||||
|
||||
// Delete the original entity using the operations link.
|
||||
$this->clickLink('Delete');
|
||||
$this->assertResponse(200);
|
||||
$this->assertTitle('Are you sure you want to delete the test configuration Default? | Drupal');
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
|
||||
// Verify that the text of the label and machine name does not appear in
|
||||
// the list (though it may appear elsewhere on the page).
|
||||
$this->assertNoFieldByXpath('//td', 'Default', "No label found for deleted 'Default' entity.");
|
||||
$this->assertNoFieldByXpath('//td', 'dotted.default', "No machine name found for deleted 'Default' entity.");
|
||||
|
||||
// Confirm that the empty text is displayed.
|
||||
$this->assertText('There are no test configuration entities yet.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test paging.
|
||||
*/
|
||||
public function testPager() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$storage = \Drupal::entityManager()->getListBuilder('config_test')->getStorage();
|
||||
|
||||
// Create 51 test entities.
|
||||
for ($i = 1; $i < 52; $i++) {
|
||||
$storage->create([
|
||||
'id' => str_pad($i, 2, '0', STR_PAD_LEFT),
|
||||
'label' => 'Test config entity ' . $i,
|
||||
'weight' => $i,
|
||||
'protected_property' => $i,
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Load the listing page.
|
||||
$this->drupalGet('admin/structure/config_test');
|
||||
|
||||
// Item 51 should not be present.
|
||||
$this->assertRaw('Test config entity 50', 'Config entity 50 is shown.');
|
||||
$this->assertNoRaw('Test config entity 51', 'Config entity 51 is on the next page.');
|
||||
|
||||
// Browse to the next page.
|
||||
$this->clickLink(t('Page 2'));
|
||||
$this->assertNoRaw('Test config entity 50', 'Test config entity 50 is on the previous page.');
|
||||
$this->assertRaw('dotted.default', 'Default config entity appears on page 2.');
|
||||
$this->assertRaw('Test config entity 51', 'Test config entity 51 is on page 2.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Component\Render\FormattableMarkup;
|
||||
use Drupal\Core\Entity\EntityMalformedException;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
||||
use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The maximum length for the entity storage used in this test.
|
||||
*/
|
||||
const MAX_ID_LENGTH = ConfigEntityStorage::MAX_ID_LENGTH;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config_test'];
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
public function testCRUD() {
|
||||
$default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
// Verify default properties on a newly created empty entity.
|
||||
$storage = \Drupal::entityTypeManager()->getStorage('config_test');
|
||||
$empty = $storage->create();
|
||||
$this->assertTrue($empty->uuid());
|
||||
$this->assertIdentical($empty->label, NULL);
|
||||
$this->assertIdentical($empty->style, NULL);
|
||||
$this->assertIdentical($empty->language()->getId(), $default_langcode);
|
||||
|
||||
// Verify ConfigEntity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->isNew(), TRUE);
|
||||
$this->assertIdentical($empty->getOriginalId(), NULL);
|
||||
$this->assertIdentical($empty->bundle(), 'config_test');
|
||||
$this->assertIdentical($empty->id(), NULL);
|
||||
$this->assertTrue($empty->uuid());
|
||||
$this->assertIdentical($empty->label(), NULL);
|
||||
|
||||
$this->assertIdentical($empty->get('id'), NULL);
|
||||
$this->assertTrue($empty->get('uuid'));
|
||||
$this->assertIdentical($empty->get('label'), NULL);
|
||||
$this->assertIdentical($empty->get('style'), NULL);
|
||||
$this->assertIdentical($empty->language()->getId(), $default_langcode);
|
||||
|
||||
// Verify Entity properties/methods on the newly created empty entity.
|
||||
$this->assertIdentical($empty->getEntityTypeId(), 'config_test');
|
||||
// The URI can only be checked after saving.
|
||||
try {
|
||||
$empty->urlInfo();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify that an empty entity cannot be saved.
|
||||
try {
|
||||
$empty->save();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify that an entity with an empty ID string is considered empty, too.
|
||||
$empty_id = $storage->create([
|
||||
'id' => '',
|
||||
]);
|
||||
$this->assertIdentical($empty_id->isNew(), TRUE);
|
||||
try {
|
||||
$empty_id->save();
|
||||
$this->fail('EntityMalformedException was thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->pass('EntityMalformedException was thrown.');
|
||||
}
|
||||
|
||||
// Verify properties on a newly created entity.
|
||||
$config_test = $storage->create($expected = [
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
'style' => $this->randomMachineName(),
|
||||
]);
|
||||
$this->assertTrue($config_test->uuid());
|
||||
$this->assertNotEqual($config_test->uuid(), $empty->uuid());
|
||||
$this->assertIdentical($config_test->label, $expected['label']);
|
||||
$this->assertIdentical($config_test->style, $expected['style']);
|
||||
$this->assertIdentical($config_test->language()->getId(), $default_langcode);
|
||||
|
||||
// Verify methods on the newly created entity.
|
||||
$this->assertIdentical($config_test->isNew(), TRUE);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $expected['id']);
|
||||
$this->assertIdentical($config_test->id(), $expected['id']);
|
||||
$this->assertTrue($config_test->uuid());
|
||||
$expected['uuid'] = $config_test->uuid();
|
||||
$this->assertIdentical($config_test->label(), $expected['label']);
|
||||
|
||||
// Verify that the entity can be saved.
|
||||
try {
|
||||
$status = $config_test->save();
|
||||
$this->pass('EntityMalformedException was not thrown.');
|
||||
}
|
||||
catch (EntityMalformedException $e) {
|
||||
$this->fail('EntityMalformedException was not thrown.');
|
||||
}
|
||||
|
||||
// The entity path can only be checked after saving.
|
||||
$this->assertIdentical($config_test->url(), Url::fromRoute('entity.config_test.edit_form', ['config_test' => $expected['id']])->toString());
|
||||
|
||||
// Verify that the correct status is returned and properties did not change.
|
||||
$this->assertIdentical($status, SAVED_NEW);
|
||||
$this->assertIdentical($config_test->id(), $expected['id']);
|
||||
$this->assertIdentical($config_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($config_test->label(), $expected['label']);
|
||||
$this->assertIdentical($config_test->isNew(), FALSE);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $expected['id']);
|
||||
|
||||
// Save again, and verify correct status and properties again.
|
||||
$status = $config_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($config_test->id(), $expected['id']);
|
||||
$this->assertIdentical($config_test->uuid(), $expected['uuid']);
|
||||
$this->assertIdentical($config_test->label(), $expected['label']);
|
||||
$this->assertIdentical($config_test->isNew(), FALSE);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $expected['id']);
|
||||
|
||||
// Verify that a configuration entity can be saved with an ID of the
|
||||
// maximum allowed length, but not longer.
|
||||
|
||||
// Test with a short ID.
|
||||
$id_length_config_test = $storage->create([
|
||||
'id' => $this->randomMachineName(8),
|
||||
]);
|
||||
try {
|
||||
$id_length_config_test->save();
|
||||
$this->pass(new FormattableMarkup("config_test entity with ID length @length was saved.", [
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
]));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
|
||||
// Test with an ID of the maximum allowed length.
|
||||
$id_length_config_test = $storage->create([
|
||||
'id' => $this->randomMachineName(static::MAX_ID_LENGTH),
|
||||
]);
|
||||
try {
|
||||
$id_length_config_test->save();
|
||||
$this->pass(new FormattableMarkup("config_test entity with ID length @length was saved.", [
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
]));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
|
||||
// Test with an ID exceeding the maximum allowed length.
|
||||
$id_length_config_test = $storage->create([
|
||||
'id' => $this->randomMachineName(static::MAX_ID_LENGTH + 1),
|
||||
]);
|
||||
try {
|
||||
$status = $id_length_config_test->save();
|
||||
$this->fail(new FormattableMarkup("config_test entity with ID length @length exceeding the maximum allowed length of @max saved successfully", [
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
'@max' => static::MAX_ID_LENGTH,
|
||||
]));
|
||||
}
|
||||
catch (ConfigEntityIdLengthException $e) {
|
||||
$this->pass(new FormattableMarkup("config_test entity with ID length @length exceeding the maximum allowed length of @max failed to save", [
|
||||
'@length' => strlen($id_length_config_test->id()),
|
||||
'@max' => static::MAX_ID_LENGTH,
|
||||
]));
|
||||
}
|
||||
|
||||
// Ensure that creating an entity with the same id as an existing one is not
|
||||
// possible.
|
||||
$same_id = $storage->create([
|
||||
'id' => $config_test->id(),
|
||||
]);
|
||||
$this->assertIdentical($same_id->isNew(), TRUE);
|
||||
try {
|
||||
$same_id->save();
|
||||
$this->fail('Not possible to overwrite an entity entity.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass('Not possible to overwrite an entity entity.');
|
||||
}
|
||||
|
||||
// Verify that renaming the ID returns correct status and properties.
|
||||
$ids = [$expected['id'], 'second_' . $this->randomMachineName(4), 'third_' . $this->randomMachineName(4)];
|
||||
for ($i = 1; $i < 3; $i++) {
|
||||
$old_id = $ids[$i - 1];
|
||||
$new_id = $ids[$i];
|
||||
// Before renaming, everything should point to the current ID.
|
||||
$this->assertIdentical($config_test->id(), $old_id);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $old_id);
|
||||
|
||||
// Rename.
|
||||
$config_test->set('id', $new_id);
|
||||
$this->assertIdentical($config_test->id(), $new_id);
|
||||
$status = $config_test->save();
|
||||
$this->assertIdentical($status, SAVED_UPDATED);
|
||||
$this->assertIdentical($config_test->isNew(), FALSE);
|
||||
|
||||
// Verify that originalID points to new ID directly after renaming.
|
||||
$this->assertIdentical($config_test->id(), $new_id);
|
||||
$this->assertIdentical($config_test->getOriginalId(), $new_id);
|
||||
}
|
||||
|
||||
// Test config entity prepopulation.
|
||||
\Drupal::state()->set('config_test.prepopulate', TRUE);
|
||||
$config_test = $storage->create(['foo' => 'bar']);
|
||||
$this->assertEqual($config_test->get('foo'), 'baz', 'Initial value correctly populated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests CRUD operations through the UI.
|
||||
*/
|
||||
public function testCRUDUI() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$id = strtolower($this->randomMachineName());
|
||||
$label1 = $this->randomMachineName();
|
||||
$label2 = $this->randomMachineName();
|
||||
$label3 = $this->randomMachineName();
|
||||
$message_insert = format_string('%label configuration has been created.', ['%label' => $label1]);
|
||||
$message_update = format_string('%label configuration has been updated.', ['%label' => $label2]);
|
||||
$message_delete = format_string('The test configuration %label has been deleted.', ['%label' => $label2]);
|
||||
|
||||
// Create a configuration entity.
|
||||
$edit = [
|
||||
'id' => $id,
|
||||
'label' => $label1,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertRaw($message_insert);
|
||||
$this->assertNoRaw($message_update);
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Update the configuration entity.
|
||||
$edit = [
|
||||
'label' => $label2,
|
||||
];
|
||||
$this->drupalPostForm("admin/structure/config_test/manage/$id", $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw($message_insert);
|
||||
$this->assertRaw($message_update);
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id/delete");
|
||||
|
||||
// Delete the configuration entity.
|
||||
$this->drupalGet("admin/structure/config_test/manage/$id");
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertUrl("admin/structure/config_test/manage/$id/delete");
|
||||
$this->drupalPostForm(NULL, [], 'Delete');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw($message_update);
|
||||
$this->assertRaw($message_delete);
|
||||
$this->assertNoText($label1);
|
||||
$this->assertNoLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Re-create a configuration entity.
|
||||
$edit = [
|
||||
'id' => $id,
|
||||
'label' => $label1,
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($label1);
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Rename the configuration entity's ID/machine name.
|
||||
$edit = [
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
'label' => $label3,
|
||||
];
|
||||
$this->drupalPostForm("admin/structure/config_test/manage/$id", $edit, 'Save');
|
||||
$this->assertUrl('admin/structure/config_test');
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoText($label1);
|
||||
$this->assertNoText($label2);
|
||||
$this->assertText($label3);
|
||||
$this->assertNoLinkByHref("admin/structure/config_test/manage/$id");
|
||||
$id = $edit['id'];
|
||||
$this->assertLinkByHref("admin/structure/config_test/manage/$id");
|
||||
|
||||
// Create a configuration entity with '0' machine name.
|
||||
$edit = [
|
||||
'id' => '0',
|
||||
'label' => '0',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/config_test/add', $edit, 'Save');
|
||||
$this->assertResponse(200);
|
||||
$message_insert = format_string('%label configuration has been created.', ['%label' => $edit['label']]);
|
||||
$this->assertRaw($message_insert);
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/0');
|
||||
$this->assertLinkByHref('admin/structure/config_test/manage/0/delete');
|
||||
$this->drupalPostForm('admin/structure/config_test/manage/0/delete', [], 'Delete');
|
||||
$this->assertFalse(entity_load('config_test', '0'), 'Test entity deleted');
|
||||
|
||||
// Create a configuration entity with a property that uses AJAX to show
|
||||
// extra form elements. Test this scenario in a non-JS case by using a
|
||||
// 'js-hidden' submit button.
|
||||
// @see \Drupal\Tests\config\FunctionalJavascript\ConfigEntityTest::testAjaxOnAddPage()
|
||||
$this->drupalGet('admin/structure/config_test/add');
|
||||
|
||||
$id = strtolower($this->randomMachineName());
|
||||
$edit = [
|
||||
'id' => $id,
|
||||
'label' => $this->randomString(),
|
||||
'size' => 'custom',
|
||||
];
|
||||
|
||||
$this->assertFieldByName('size');
|
||||
$this->assertNoFieldByName('size_value');
|
||||
|
||||
$this->drupalPostForm(NULL, $edit, 'Change size');
|
||||
$this->assertFieldByName('size');
|
||||
$this->assertFieldByName('size_value');
|
||||
|
||||
// Submit the form with the regular 'Save' button and check that the entity
|
||||
// values are correct.
|
||||
$edit += ['size_value' => 'medium'];
|
||||
$this->drupalPostForm(NULL, $edit, 'Save');
|
||||
|
||||
$entity = entity_load('config_test', $id);
|
||||
$this->assertEqual($entity->get('size'), 'custom');
|
||||
$this->assertEqual($entity->get('size_value'), 'medium');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Core\Archiver\ArchiveTar;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing/exporting configuration.
|
||||
*
|
||||
* Each testX method does a complete rebuild of a Drupal site, so values being
|
||||
* tested need to be stored in protected properties in order to survive until
|
||||
* the next rebuild.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigExportImportUITest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The contents of the config export tarball, held between test methods.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tarball;
|
||||
|
||||
/**
|
||||
* Holds the original 'site slogan' before testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $originalSlogan;
|
||||
|
||||
/**
|
||||
* Holds a randomly generated new 'site slogan' for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $newSlogan;
|
||||
|
||||
|
||||
/**
|
||||
* Holds a content type.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $contentType;
|
||||
|
||||
/**
|
||||
* Holds the randomly-generated name of a field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* Holds the field storage entity for $fieldName.
|
||||
*
|
||||
* @var \Drupal\field\FieldStorageConfigInterface
|
||||
*/
|
||||
protected $fieldStorage;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config', 'node', 'field'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// The initial import must be done with uid 1 because if separately named
|
||||
// roles are created then the role is lost after import. If the roles
|
||||
// created have the same name then the sync will fail because they will
|
||||
// have different UUIDs.
|
||||
$this->drupalLogin($this->rootUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a simple site export import case.
|
||||
*/
|
||||
public function testExportImport() {
|
||||
// After installation there is no snapshot and nothing to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
$this->originalSlogan = $this->config('system.site')->get('slogan');
|
||||
$this->newSlogan = $this->randomString(16);
|
||||
$this->assertNotEqual($this->newSlogan, $this->originalSlogan);
|
||||
$this->config('system.site')
|
||||
->set('slogan', $this->newSlogan)
|
||||
->save();
|
||||
$this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan);
|
||||
|
||||
// Create a content type.
|
||||
$this->contentType = $this->drupalCreateContentType();
|
||||
|
||||
// Create a field.
|
||||
$this->fieldName = mb_strtolower($this->randomMachineName());
|
||||
$this->fieldStorage = FieldStorageConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
]);
|
||||
$this->fieldStorage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $this->fieldStorage,
|
||||
'bundle' => $this->contentType->id(),
|
||||
])->save();
|
||||
// Update the displays so that configuration does not change unexpectedly on
|
||||
// import.
|
||||
entity_get_form_display('node', $this->contentType->id(), 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'text_textfield',
|
||||
])
|
||||
->save();
|
||||
entity_get_display('node', $this->contentType->id(), 'full')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
entity_get_display('node', $this->contentType->id(), 'default')
|
||||
->setComponent($this->fieldName)
|
||||
->save();
|
||||
entity_get_display('node', $this->contentType->id(), 'teaser')
|
||||
->removeComponent($this->fieldName)
|
||||
->save();
|
||||
|
||||
$this->drupalGet('node/add/' . $this->contentType->id());
|
||||
$this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed');
|
||||
|
||||
// Export the configuration.
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/export', [], 'Export');
|
||||
$this->tarball = $this->getSession()->getPage()->getContent();
|
||||
|
||||
$this->config('system.site')
|
||||
->set('slogan', $this->originalSlogan)
|
||||
->save();
|
||||
$this->assertEqual($this->config('system.site')->get('slogan'), $this->originalSlogan);
|
||||
|
||||
// Delete the custom field.
|
||||
$fields = FieldConfig::loadMultiple();
|
||||
foreach ($fields as $field) {
|
||||
if ($field->getName() == $this->fieldName) {
|
||||
$field->delete();
|
||||
}
|
||||
}
|
||||
$field_storages = FieldStorageConfig::loadMultiple();
|
||||
foreach ($field_storages as $field_storage) {
|
||||
if ($field_storage->getName() == $this->fieldName) {
|
||||
$field_storage->delete();
|
||||
}
|
||||
}
|
||||
$this->drupalGet('node/add/' . $this->contentType->id());
|
||||
$this->assertNoFieldByName("{$this->fieldName}[0][value]", '', 'Widget is not displayed');
|
||||
|
||||
// Import the configuration.
|
||||
$filename = 'temporary://' . $this->randomMachineName();
|
||||
file_put_contents($filename, $this->tarball);
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/import', ['files[import_tarball]' => $filename], 'Upload');
|
||||
// There is no snapshot yet because an import has never run.
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
$this->assertText($this->contentType->label());
|
||||
|
||||
$this->drupalPostForm(NULL, [], 'Import all');
|
||||
// After importing the snapshot has been updated an there are no warnings.
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
$this->assertEqual($this->config('system.site')->get('slogan'), $this->newSlogan);
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertFieldByName("{$this->fieldName}[0][value]", '', 'Widget is displayed');
|
||||
|
||||
$this->config('system.site')
|
||||
->set('slogan', $this->originalSlogan)
|
||||
->save();
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText(t('Warning message'));
|
||||
$this->assertText('The following items in your active configuration have changes since the last import that may be lost on the next import.');
|
||||
// Ensure the item is displayed as part of a list (to avoid false matches
|
||||
// on the rest of the page) and that the list markup is not escaped.
|
||||
$this->assertRaw('<li>system.site</li>');
|
||||
// Remove everything from sync. The warning about differences between the
|
||||
// active and snapshot should no longer exist.
|
||||
\Drupal::service('config.storage.sync')->deleteAll();
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('Warning message'));
|
||||
$this->assertNoText('The following items in your active configuration have changes since the last import that may be lost on the next import.');
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
// Write a file to sync. The warning about differences between the active
|
||||
// and snapshot should now exist.
|
||||
/** @var \Drupal\Core\Config\StorageInterface $sync */
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$data = $this->config('system.site')->get();
|
||||
$data['slogan'] = 'in the face';
|
||||
$this->copyConfig($this->container->get('config.storage'), $sync);
|
||||
$sync->write('system.site', $data);
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertText(t('Warning message'));
|
||||
$this->assertText('The following items in your active configuration have changes since the last import that may be lost on the next import.');
|
||||
// Ensure the item is displayed as part of a list (to avoid false matches
|
||||
// on the rest of the page) and that the list markup is not escaped.
|
||||
$this->assertRaw('<li>system.site</li>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an export and import of collections.
|
||||
*/
|
||||
public function testExportImportCollections() {
|
||||
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$test1_storage = $active_storage->createCollection('collection.test1');
|
||||
$test1_storage->write('config_test.create', ['foo' => 'bar']);
|
||||
$test1_storage->write('config_test.update', ['foo' => 'bar']);
|
||||
$test2_storage = $active_storage->createCollection('collection.test2');
|
||||
$test2_storage->write('config_test.another_create', ['foo' => 'bar']);
|
||||
$test2_storage->write('config_test.another_update', ['foo' => 'bar']);
|
||||
|
||||
// Export the configuration.
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/export', [], 'Export');
|
||||
$this->tarball = $this->getSession()->getPage()->getContent();
|
||||
$filename = file_directory_temp() . '/' . $this->randomMachineName();
|
||||
file_put_contents($filename, $this->tarball);
|
||||
|
||||
// Set up the active storage collections to test import.
|
||||
$test1_storage->delete('config_test.create');
|
||||
$test1_storage->write('config_test.update', ['foo' => 'baz']);
|
||||
$test1_storage->write('config_test.delete', ['foo' => 'bar']);
|
||||
$test2_storage->delete('config_test.another_create');
|
||||
$test2_storage->write('config_test.another_update', ['foo' => 'baz']);
|
||||
$test2_storage->write('config_test.another_delete', ['foo' => 'bar']);
|
||||
|
||||
// Create a snapshot.
|
||||
$snapshot_storage = \Drupal::service('config.storage.snapshot');
|
||||
\Drupal::service('config.manager')->createSnapshot($active_storage, $snapshot_storage);
|
||||
|
||||
// Ensure that the snapshot has the expected collection data before import.
|
||||
$test1_snapshot = $snapshot_storage->createCollection('collection.test1');
|
||||
$data = $test1_snapshot->read('config_test.delete');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.delete in collection.test1 exists in the snapshot storage.');
|
||||
$data = $test1_snapshot->read('config_test.update');
|
||||
$this->assertEqual($data, ['foo' => 'baz'], 'The config_test.update in collection.test1 exists in the snapshot storage.');
|
||||
$this->assertFalse($test1_snapshot->read('config_test.create'), 'The config_test.create in collection.test1 does not exist in the snapshot storage.');
|
||||
$test2_snapshot = $snapshot_storage->createCollection('collection.test2');
|
||||
$data = $test2_snapshot->read('config_test.another_delete');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.another_delete in collection.test2 exists in the snapshot storage.');
|
||||
$data = $test2_snapshot->read('config_test.another_update');
|
||||
$this->assertEqual($data, ['foo' => 'baz'], 'The config_test.another_update in collection.test2 exists in the snapshot storage.');
|
||||
$this->assertFalse($test2_snapshot->read('config_test.another_create'), 'The config_test.another_create in collection.test2 does not exist in the snapshot storage.');
|
||||
|
||||
// Create the tar that contains the expected content for the collections.
|
||||
$tar = new ArchiveTar($filename, 'gz');
|
||||
$content_list = $tar->listContent();
|
||||
// Convert the list of files into something easy to search.
|
||||
$files = [];
|
||||
foreach ($content_list as $file) {
|
||||
$files[] = $file['filename'];
|
||||
}
|
||||
$this->assertTrue(in_array('collection/test1/config_test.create.yml', $files), 'Config export contains collection/test1/config_test.create.yml.');
|
||||
$this->assertTrue(in_array('collection/test2/config_test.another_create.yml', $files), 'Config export contains collection/test2/config_test.another_create.yml.');
|
||||
$this->assertTrue(in_array('collection/test1/config_test.update.yml', $files), 'Config export contains collection/test1/config_test.update.yml.');
|
||||
$this->assertTrue(in_array('collection/test2/config_test.another_update.yml', $files), 'Config export contains collection/test2/config_test.another_update.yml.');
|
||||
$this->assertFalse(in_array('collection/test1/config_test.delete.yml', $files), 'Config export does not contain collection/test1/config_test.delete.yml.');
|
||||
$this->assertFalse(in_array('collection/test2/config_test.another_delete.yml', $files), 'Config export does not contain collection/test2/config_test.another_delete.yml.');
|
||||
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/import', ['files[import_tarball]' => $filename], 'Upload');
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
$this->assertText(t('@collection configuration collection', ['@collection' => 'collection.test1']));
|
||||
$this->assertText(t('@collection configuration collection', ['@collection' => 'collection.test2']));
|
||||
$this->assertText('config_test.create');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.create');
|
||||
$this->assertText('config_test.update');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.update');
|
||||
$this->assertText('config_test.delete');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.delete');
|
||||
$this->assertText('config_test.another_create');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_create');
|
||||
$this->assertText('config_test.another_update');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_update');
|
||||
$this->assertText('config_test.another_delete');
|
||||
$this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_delete');
|
||||
|
||||
$this->drupalPostForm(NULL, [], 'Import all');
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Test data in collections.
|
||||
$data = $test1_storage->read('config_test.create');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.create in collection.test1 has been created.');
|
||||
$data = $test1_storage->read('config_test.update');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.update in collection.test1 has been updated.');
|
||||
$this->assertFalse($test1_storage->read('config_test.delete'), 'The config_test.delete in collection.test1 has been deleted.');
|
||||
|
||||
$data = $test2_storage->read('config_test.another_create');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.another_create in collection.test2 has been created.');
|
||||
$data = $test2_storage->read('config_test.another_update');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.another_update in collection.test2 has been updated.');
|
||||
$this->assertFalse($test2_storage->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 has been deleted.');
|
||||
|
||||
// Ensure that the snapshot has been updated with the collection data.
|
||||
$snapshot_storage = \Drupal::service('config.storage.snapshot');
|
||||
$test1_snapshot = $snapshot_storage->createCollection('collection.test1');
|
||||
$data = $test1_snapshot->read('config_test.create');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.create in collection.test1 has been created in the snapshot storage.');
|
||||
$data = $test1_snapshot->read('config_test.update');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.update in collection.test1 has been updated in the snapshot storage.');
|
||||
$this->assertFalse($test1_snapshot->read('config_test.delete'), 'The config_test.delete in collection.test1 does not exist in the snapshot storage.');
|
||||
$test2_snapshot = $snapshot_storage->createCollection('collection.test2');
|
||||
$data = $test2_snapshot->read('config_test.another_create');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.another_create in collection.test2 has been created in the snapshot storage.');
|
||||
$data = $test2_snapshot->read('config_test.another_update');
|
||||
$this->assertEqual($data, ['foo' => 'bar'], 'The config_test.another_update in collection.test2 has been updated in the snapshot storage.');
|
||||
$this->assertFalse($test2_snapshot->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 does not exist in the snapshot storage.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Core\Archiver\Tar;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for exporting configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigExportUITest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config', 'config_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Set up an override.
|
||||
$settings['config']['system.maintenance']['message'] = (object) [
|
||||
'value' => 'Foo',
|
||||
'required' => TRUE,
|
||||
];
|
||||
$this->writeSettings($settings);
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(['export configuration']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests export of configuration.
|
||||
*/
|
||||
public function testExport() {
|
||||
// Verify the export page with export submit button is available.
|
||||
$this->drupalGet('admin/config/development/configuration/full/export');
|
||||
$this->assertFieldById('edit-submit', t('Export'));
|
||||
|
||||
// Submit the export form and verify response. This will create a file in
|
||||
// temporary directory with the default name config.tar.gz.
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/export', [], t('Export'));
|
||||
$this->assertResponse(200, 'User can access the download callback.');
|
||||
|
||||
// Test if header contains file name with hostname and timestamp.
|
||||
$request = \Drupal::request();
|
||||
$hostname = str_replace('.', '-', $request->getHttpHost());
|
||||
$header_content_disposition = $this->drupalGetHeader('content-disposition');
|
||||
$header_match = (boolean) preg_match('/attachment; filename="config-' . preg_quote($hostname) . '-\d{4}-\d{2}-\d{2}-\d{2}-\d{2}\.tar\.gz"/', $header_content_disposition);
|
||||
$this->assertTrue($header_match, "Header with filename matches the expected format.");
|
||||
|
||||
// Extract the archive and verify it's not empty.
|
||||
$file_path = file_directory_temp() . '/' . 'config.tar.gz';
|
||||
$archiver = new Tar($file_path);
|
||||
$archive_contents = $archiver->listContents();
|
||||
$this->assert(!empty($archive_contents), 'Downloaded archive file is not empty.');
|
||||
|
||||
// Prepare the list of config files from active storage, see
|
||||
// \Drupal\config\Controller\ConfigController::downloadExport().
|
||||
$storage_active = $this->container->get('config.storage');
|
||||
$config_files = [];
|
||||
foreach ($storage_active->listAll() as $config_name) {
|
||||
$config_files[] = $config_name . '.yml';
|
||||
}
|
||||
// Assert that the downloaded archive file contents are the same as the test
|
||||
// site active store.
|
||||
$this->assertIdentical($archive_contents, $config_files);
|
||||
|
||||
// Ensure the test configuration override is in effect but was not exported.
|
||||
$this->assertIdentical(\Drupal::config('system.maintenance')->get('message'), 'Foo');
|
||||
$archiver->extract(file_directory_temp(), ['system.maintenance.yml']);
|
||||
$file_contents = file_get_contents(file_directory_temp() . '/' . 'system.maintenance.yml');
|
||||
$exported = Yaml::decode($file_contents);
|
||||
$this->assertNotIdentical($exported['message'], 'Foo');
|
||||
|
||||
// Check the single export form doesn't have "form-required" elements.
|
||||
$this->drupalGet('admin/config/development/configuration/single/export');
|
||||
$this->assertNoRaw('js-form-required form-required', 'No form required fields are found.');
|
||||
|
||||
// Ensure the temporary file is not available to users without the
|
||||
// permission.
|
||||
$this->drupalLogout();
|
||||
$this->drupalGet('system/temporary', ['query' => ['file' => 'config.tar.gz']]);
|
||||
$this->assertResponse(403);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests config overrides do not appear on forms that extend ConfigFormBase.
|
||||
*
|
||||
* @group config
|
||||
* @see \Drupal\Core\Form\ConfigFormBase
|
||||
*/
|
||||
class ConfigFormOverrideTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Tests that overrides do not affect forms.
|
||||
*/
|
||||
public function testFormsWithOverrides() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['access administration pages', 'administer site configuration']));
|
||||
|
||||
$overridden_name = 'Site name global conf override';
|
||||
|
||||
// Set up an override.
|
||||
$settings['config']['system.site']['name'] = (object) [
|
||||
'value' => $overridden_name,
|
||||
'required' => TRUE,
|
||||
];
|
||||
$this->writeSettings($settings);
|
||||
|
||||
// Test that everything on the form is the same, but that the override
|
||||
// worked for the actual site name.
|
||||
$this->drupalGet('admin/config/system/site-information');
|
||||
$this->assertTitle('Basic site settings | ' . $overridden_name);
|
||||
$elements = $this->xpath('//input[@name="site_name"]');
|
||||
$this->assertIdentical($elements[0]->getValue(), 'Drupal');
|
||||
|
||||
// Submit the form and ensure the site name is not changed.
|
||||
$edit = [
|
||||
'site_name' => 'Custom site name',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/system/site-information', $edit, t('Save configuration'));
|
||||
$this->assertTitle('Basic site settings | ' . $overridden_name);
|
||||
$elements = $this->xpath('//input[@name="site_name"]');
|
||||
$this->assertIdentical($elements[0]->getValue(), $edit['site_name']);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\Core\Entity\ContentEntityTypeInterface;
|
||||
use Drupal\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Tests\system\Functional\Module\ModuleTestBase;
|
||||
|
||||
/**
|
||||
* Tests the largest configuration import possible with all available modules.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportAllTest extends ModuleTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* A user with the 'synchronize configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* Using the standard profile as this has a lot of additional configuration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(['synchronize configuration']);
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a fixed set of modules can be installed and uninstalled.
|
||||
*/
|
||||
public function testInstallUninstall() {
|
||||
|
||||
// Get a list of modules to enable.
|
||||
$all_modules = system_rebuild_module_data();
|
||||
$all_modules = array_filter($all_modules, function ($module) {
|
||||
// Filter contrib, hidden, already enabled modules and modules in the
|
||||
// Testing package.
|
||||
if ($module->origin !== 'core' || !empty($module->info['hidden']) || $module->status == TRUE || $module->info['package'] == 'Testing') {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
});
|
||||
|
||||
// Install every module possible.
|
||||
\Drupal::service('module_installer')->install(array_keys($all_modules));
|
||||
|
||||
$this->assertModules(array_keys($all_modules), TRUE);
|
||||
foreach ($all_modules as $module => $info) {
|
||||
$this->assertModuleConfig($module);
|
||||
$this->assertModuleTablesExist($module);
|
||||
}
|
||||
|
||||
// Export active config to sync.
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
|
||||
system_list_reset();
|
||||
$this->resetAll();
|
||||
|
||||
// Delete all entities provided by modules that prevent uninstallation. For
|
||||
// example, if any content entity exists its provider cannot be uninstalled.
|
||||
// So deleting all taxonomy terms allows the Taxonomy to be uninstalled.
|
||||
// Additionally, every field is deleted so modules can be uninstalled. For
|
||||
// example, if a comment field exists then Comment cannot be uninstalled.
|
||||
$entity_type_manager = \Drupal::entityTypeManager();
|
||||
foreach ($entity_type_manager->getDefinitions() as $entity_type) {
|
||||
if (($entity_type instanceof ContentEntityTypeInterface || in_array($entity_type->id(), ['field_storage_config', 'filter_format'], TRUE))
|
||||
&& !in_array($entity_type->getProvider(), ['system', 'user'], TRUE)) {
|
||||
$storage = $entity_type_manager->getStorage($entity_type->id());
|
||||
$storage->delete($storage->loadMultiple());
|
||||
}
|
||||
}
|
||||
|
||||
// Purge the field data.
|
||||
field_purge_batch(1000);
|
||||
|
||||
system_list_reset();
|
||||
$all_modules = system_rebuild_module_data();
|
||||
|
||||
// Ensure that only core required modules and the install profile can not be uninstalled.
|
||||
$validation_reasons = \Drupal::service('module_installer')->validateUninstall(array_keys($all_modules));
|
||||
$this->assertEqual(['system', 'user', 'standard'], array_keys($validation_reasons));
|
||||
|
||||
$modules_to_uninstall = array_filter($all_modules, function ($module) use ($validation_reasons) {
|
||||
// Filter required and not enabled modules.
|
||||
if (!empty($module->info['required']) || $module->status == FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
});
|
||||
|
||||
// Can not uninstall config and use admin/config/development/configuration!
|
||||
unset($modules_to_uninstall['config']);
|
||||
|
||||
$this->assertTrue(isset($modules_to_uninstall['comment']), 'The comment module will be disabled');
|
||||
$this->assertTrue(isset($modules_to_uninstall['file']), 'The File module will be disabled');
|
||||
$this->assertTrue(isset($modules_to_uninstall['editor']), 'The Editor module will be disabled');
|
||||
|
||||
// Uninstall all modules that can be uninstalled.
|
||||
\Drupal::service('module_installer')->uninstall(array_keys($modules_to_uninstall));
|
||||
|
||||
$this->assertModules(array_keys($modules_to_uninstall), FALSE);
|
||||
foreach ($modules_to_uninstall as $module => $info) {
|
||||
$this->assertNoModuleConfig($module);
|
||||
$this->assertModuleTablesDoNotExist($module);
|
||||
}
|
||||
|
||||
// Import the configuration thereby re-installing all the modules.
|
||||
$this->drupalPostForm('admin/config/development/configuration', [], t('Import all'));
|
||||
// Modules have been installed that have services.
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Check that there are no errors.
|
||||
$this->assertIdentical($this->configImporter()->getErrors(), []);
|
||||
|
||||
// Check that all modules that were uninstalled are now reinstalled.
|
||||
$this->assertModules(array_keys($modules_to_uninstall), TRUE);
|
||||
foreach ($modules_to_uninstall as $module => $info) {
|
||||
$this->assertModuleConfig($module);
|
||||
$this->assertModuleTablesExist($module);
|
||||
}
|
||||
|
||||
// Ensure that we have no configuration changes to import.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->assertIdentical($storage_comparer->createChangelist()->getChangelist(), $storage_comparer->getEmptyChangelist());
|
||||
|
||||
// Now we have all configuration imported, test all of them for schema
|
||||
// conformance. Ensures all imported default configuration is valid when
|
||||
// all modules are enabled.
|
||||
$names = $this->container->get('config.storage')->listAll();
|
||||
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
|
||||
$typed_config = $this->container->get('config.typed');
|
||||
foreach ($names as $name) {
|
||||
$config = $this->config($name);
|
||||
$this->assertConfigSchema($typed_config, $name, $config->get());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the importing/exporting configuration based on the install profile.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportInstallProfileTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The profile to install as a basis for testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_import';
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config'];
|
||||
|
||||
/**
|
||||
* A user with the 'synchronize configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config importer cannot uninstall install profiles.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testInstallProfileValidation() {
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$this->copyConfig($this->container->get('config.storage'), $sync);
|
||||
$core = $sync->read('core.extension');
|
||||
|
||||
// Ensure install profiles can not be uninstalled.
|
||||
unset($core['module']['testing_config_import']);
|
||||
$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 Testing config import profile since it is the install profile.');
|
||||
|
||||
// Uninstall dependencies of testing_config_import.
|
||||
$core['module']['testing_config_import'] = 0;
|
||||
unset($core['module']['syslog']);
|
||||
unset($core['theme']['stark']);
|
||||
$core['theme']['stable'] = 0;
|
||||
$core['theme']['classy'] = 0;
|
||||
$sync->write('core.extension', $core);
|
||||
$sync->deleteAll('syslog.');
|
||||
$theme = $sync->read('system.theme');
|
||||
$theme['default'] = 'classy';
|
||||
$sync->write('system.theme', $theme);
|
||||
$this->drupalPostForm('admin/config/development/configuration', [], t('Import all'));
|
||||
$this->assertText('The configuration was imported successfully.');
|
||||
$this->rebuildContainer();
|
||||
$this->assertFalse(\Drupal::moduleHandler()->moduleExists('syslog'), 'The syslog module has been uninstalled.');
|
||||
$this->assertFalse(\Drupal::service('theme_handler')->themeExists('stark'), 'The stark theme has been uninstalled.');
|
||||
$this->assertTrue(\Drupal::service('theme_handler')->themeExists('classy'), 'The classy theme has been installed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,534 @@
|
|||
<?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;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportUITest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config', 'config_test', 'config_import_test', 'text', 'options'];
|
||||
|
||||
/**
|
||||
* A user with the 'synchronize configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing configuration.
|
||||
*/
|
||||
public function testImport() {
|
||||
$name = 'system.site';
|
||||
$dynamic_name = 'config_test.dynamic.new';
|
||||
/** @var \Drupal\Core\Config\StorageInterface $sync */
|
||||
$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'));
|
||||
|
||||
// Create updated configuration object.
|
||||
$new_site_name = 'Config import test ' . $this->randomString();
|
||||
$this->prepareSiteNameUpdate($new_site_name);
|
||||
$this->assertIdentical($sync->exists($name), TRUE, $name . ' found.');
|
||||
|
||||
// Create new config entity.
|
||||
$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.');
|
||||
|
||||
// Enable the Action and Ban modules during import. The Ban
|
||||
// module is used because it creates a table during the install. The Action
|
||||
// module is used because it creates a single simple configuration file
|
||||
// during the install.
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
$core_extension['module']['action'] = 0;
|
||||
$core_extension['module']['ban'] = 0;
|
||||
$core_extension['module'] = module_config_sort($core_extension['module']);
|
||||
// Bartik is a subtheme of classy so classy must be enabled.
|
||||
$core_extension['theme']['classy'] = 0;
|
||||
$core_extension['theme']['bartik'] = 0;
|
||||
$sync->write('core.extension', $core_extension);
|
||||
|
||||
// Use the install storage so that we can read configuration from modules
|
||||
// and themes that are not installed.
|
||||
$install_storage = new InstallStorage();
|
||||
|
||||
// Set the Bartik theme as default.
|
||||
$system_theme = $this->config('system.theme')->get();
|
||||
$system_theme['default'] = 'bartik';
|
||||
$sync->write('system.theme', $system_theme);
|
||||
|
||||
// Read the action config from module default config folder.
|
||||
$action_settings = $install_storage->read('action.settings');
|
||||
$action_settings['recursion_limit'] = 50;
|
||||
$sync->write('action.settings', $action_settings);
|
||||
|
||||
// Uninstall the Options and Text modules to ensure that dependencies are
|
||||
// handled correctly. Options depends on Text so Text should be installed
|
||||
// first. Since they were enabled during the test setup the core.extension
|
||||
// file in sync will already contain them.
|
||||
\Drupal::service('module_installer')->uninstall(['text', 'options']);
|
||||
|
||||
// Set the state system to record installations and uninstallations.
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_installed', []);
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_uninstalled', []);
|
||||
|
||||
// Verify that both appear as ready to import.
|
||||
$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>action.settings');
|
||||
$this->assertFieldById('edit-submit', t('Import all'));
|
||||
|
||||
// Import and verify that both do not appear anymore.
|
||||
$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>action.settings');
|
||||
|
||||
$this->assertNoFieldById('edit-submit', t('Import all'));
|
||||
|
||||
// Verify that there are no further changes to import.
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
|
||||
$this->rebuildContainer();
|
||||
// Verify site name has changed.
|
||||
$this->assertIdentical($new_site_name, $this->config('system.site')->get('name'));
|
||||
|
||||
// Verify that new config entity exists.
|
||||
$this->assertIdentical($original_dynamic_data, $this->config($dynamic_name)->get());
|
||||
|
||||
// Verify the cache got cleared.
|
||||
$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('action'), 'Action 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.');
|
||||
|
||||
$theme_info = \Drupal::service('theme_handler')->listInfo();
|
||||
$this->assertTrue($theme_info['bartik']->status, 'Bartik theme installed during import.');
|
||||
|
||||
// Ensure installations and uninstallation occur as expected.
|
||||
$installed = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_installed', []);
|
||||
$uninstalled = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_uninstalled', []);
|
||||
$expected = ['action', 'ban', 'text', 'options'];
|
||||
$this->assertIdentical($expected, $installed, 'Action, Ban, Text and Options modules installed in the correct order.');
|
||||
$this->assertTrue(empty($uninstalled), 'No modules uninstalled during import');
|
||||
|
||||
// Verify that the action.settings configuration object was only written
|
||||
// once during the import process and only with the value set in the staged
|
||||
// configuration. This verifies that the module's default configuration is
|
||||
// used during configuration import and, additionally, that after installing
|
||||
// a module, that configuration is not synced twice.
|
||||
$recursion_limit_values = \Drupal::state()->get('ConfigImportUITest.action.settings.recursion_limit', []);
|
||||
$this->assertIdentical($recursion_limit_values, [50]);
|
||||
|
||||
$core_extension = $this->config('core.extension')->get();
|
||||
unset($core_extension['module']['action']);
|
||||
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('action.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);
|
||||
|
||||
// Set the state system to record installations and uninstallations.
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_installed', []);
|
||||
\Drupal::state()->set('ConfigImportUITest.core.extension.modules_uninstalled', []);
|
||||
|
||||
// Verify that both appear as ready to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertRaw('<td>core.extension');
|
||||
$this->assertRaw('<td>system.theme');
|
||||
$this->assertRaw('<td>action.settings');
|
||||
|
||||
// Import and verify that both do not appear anymore.
|
||||
$this->drupalPostForm(NULL, [], t('Import all'));
|
||||
$this->assertNoRaw('<td>core.extension');
|
||||
$this->assertNoRaw('<td>system.theme');
|
||||
$this->assertNoRaw('<td>action.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('action'), 'Action 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.');
|
||||
|
||||
// Ensure installations and uninstallation occur as expected.
|
||||
$installed = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_installed', []);
|
||||
$uninstalled = \Drupal::state()->get('ConfigImportUITest.core.extension.modules_uninstalled', []);
|
||||
$expected = ['options', 'text', 'ban', 'action'];
|
||||
$this->assertIdentical($expected, $uninstalled, 'Options, Text, Ban and Action 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.');
|
||||
|
||||
// Verify that the action.settings configuration object was only deleted
|
||||
// once during the import process.
|
||||
$delete_called = \Drupal::state()->get('ConfigImportUITest.action.settings.delete', 0);
|
||||
$this->assertIdentical($delete_called, 1, "The action.settings configuration was deleted once during configuration import.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests concurrent importing of configuration.
|
||||
*/
|
||||
public function testImportLock() {
|
||||
// Create updated configuration object.
|
||||
$new_site_name = 'Config import test ' . $this->randomString();
|
||||
$this->prepareSiteNameUpdate($new_site_name);
|
||||
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Acquire a fake-lock on the import mechanism.
|
||||
$config_importer = $this->configImporter();
|
||||
$this->container->get('lock.persistent')->acquire($config_importer::LOCK_NAME);
|
||||
|
||||
// Attempt to import configuration and verify that an error message appears.
|
||||
$this->drupalPostForm(NULL, [], t('Import all'));
|
||||
$this->assertText(t('Another request may be synchronizing configuration already.'));
|
||||
|
||||
// Release the lock, just to keep testing sane.
|
||||
$this->container->get('lock.persistent')->release($config_importer::LOCK_NAME);
|
||||
|
||||
// Verify site name has not changed.
|
||||
$this->assertNotEqual($new_site_name, $this->config('system.site')->get('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests verification of site UUID before importing configuration.
|
||||
*/
|
||||
public function testImportSiteUuidValidation() {
|
||||
$sync = \Drupal::service('config.storage.sync');
|
||||
// Create updated configuration object.
|
||||
$config_data = $this->config('system.site')->get();
|
||||
// Generate a new site UUID.
|
||||
$config_data['uuid'] = \Drupal::service('uuid')->generate();
|
||||
$sync->write('system.site', $config_data);
|
||||
|
||||
// Verify that there are configuration differences to import.
|
||||
$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'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the screen that shows differences between active and sync.
|
||||
*/
|
||||
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>',
|
||||
];
|
||||
// Update active storage to have html in config data.
|
||||
$this->config($config_name)->setData($original_data)->save();
|
||||
|
||||
// Change a configuration value in sync.
|
||||
$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);
|
||||
|
||||
// Load the diff UI and verify that the diff reflects the change.
|
||||
$this->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
|
||||
$this->assertNoRaw('&nbsp;');
|
||||
$this->assertTitle(format_string('View changes of @config_name | Drupal', ['@config_name' => $config_name]));
|
||||
|
||||
// The following assertions do not use $this::assertEscaped() because
|
||||
// \Drupal\Component\Diff\DiffFormatter adds markup that signifies what has
|
||||
// changed.
|
||||
|
||||
// Changed values are escaped.
|
||||
$this->assertText(Html::escape("foo: '<p><em>foobar</em></p>'"));
|
||||
$this->assertText(Html::escape("foo: '<p>foobar</p>'"));
|
||||
// The no change values are escaped.
|
||||
$this->assertText(Html::escape("baz: '<strong>no change</strong>'"));
|
||||
// Added value is escaped.
|
||||
$this->assertText(Html::escape("biff: '<em>bangpow</em>'"));
|
||||
// Deleted value is escaped.
|
||||
$this->assertText(Html::escape("404: '<em>herp</em>'"));
|
||||
|
||||
// Verify diff colors are displayed.
|
||||
$result = $this->xpath('//table[contains(@class, :class)]', [':class' => 'diff']);
|
||||
$this->assertEqual(count($result), 1, "Diff UI is displaying colors.");
|
||||
|
||||
// Reset data back to original, and remove a key
|
||||
$sync_data = $original_data;
|
||||
unset($sync_data[$remove_key]);
|
||||
$sync->write($config_name, $sync_data);
|
||||
|
||||
// Load the diff UI and verify that the diff reflects a removed key.
|
||||
$this->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
|
||||
// The no change values are escaped.
|
||||
$this->assertText(Html::escape("foo: '<p>foobar</p>'"));
|
||||
$this->assertText(Html::escape("baz: '<strong>no change</strong>'"));
|
||||
// Removed key is escaped.
|
||||
$this->assertText(Html::escape("404: '<em>herp</em>'"));
|
||||
|
||||
// Reset data back to original and add a key
|
||||
$sync_data = $original_data;
|
||||
$sync_data[$add_key] = $add_data;
|
||||
$sync->write($config_name, $sync_data);
|
||||
|
||||
// Load the diff UI and verify that the diff reflects an added key.
|
||||
$this->drupalGet('admin/config/development/configuration/sync/diff/' . $config_name);
|
||||
// The no change values are escaped.
|
||||
$this->assertText(Html::escape("baz: '<strong>no change</strong>'"));
|
||||
$this->assertText(Html::escape("404: '<em>herp</em>'"));
|
||||
// Added key is escaped.
|
||||
$this->assertText(Html::escape("biff: '<em>bangpow</em>'"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that multiple validation errors are listed on the page.
|
||||
*/
|
||||
public function testImportValidation() {
|
||||
// Set state value so that
|
||||
// \Drupal\config_import_test\EventSubscriber::onConfigImportValidate() logs
|
||||
// validation errors.
|
||||
\Drupal::state()->set('config_import_test.config_import_validate_fail', TRUE);
|
||||
// Ensure there is something to import.
|
||||
$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'));
|
||||
|
||||
// Verify that the validation messages appear.
|
||||
$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.');
|
||||
|
||||
// Verify site name has not changed.
|
||||
$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');
|
||||
|
||||
// Import and verify that both do not appear anymore.
|
||||
$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');
|
||||
// Create updated configuration object.
|
||||
$config_data = $this->config('system.site')->get();
|
||||
$config_data['name'] = $new_site_name;
|
||||
$sync->write('system.site', $config_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import that results in an error.
|
||||
*/
|
||||
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,
|
||||
// Add a dependency on primary, to ensure that is synced first.
|
||||
'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);
|
||||
// Verify that there are configuration differences to import.
|
||||
$this->drupalGet('admin/config/development/configuration');
|
||||
$this->assertNoText(t('There are no configuration changes to import.'));
|
||||
|
||||
// Attempt to import configuration and verify that an error message appears.
|
||||
$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.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the config importer cannot delete bundles with existing entities.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
|
||||
*/
|
||||
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');
|
||||
// The node type, body field and entity displays will be scheduled for
|
||||
// removal.
|
||||
$this->assertText(format_string('node.type.@type', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('field.field.node.@type.body', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.teaser', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.default', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('core.entity_form_display.node.@type.default', ['@type' => $node_type->id()]));
|
||||
|
||||
// Attempt to import configuration and verify that an error message appears
|
||||
// and the node type, body field and entity displays are still scheduled for
|
||||
// removal.
|
||||
$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(format_string('node.type.@type', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('field.field.node.@type.body', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.teaser', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('core.entity_view_display.node.@type.default', ['@type' => $node_type->id()]));
|
||||
$this->assertText(format_string('core.entity_form_display.node.@type.default', ['@type' => $node_type->id()]));
|
||||
|
||||
// Delete the node and try to import again.
|
||||
$node->delete();
|
||||
$this->drupalPostForm(NULL, [], t('Import all'));
|
||||
$this->assertNoRaw($validation_message);
|
||||
$this->assertText(t('There are no configuration changes to import.'));
|
||||
$this->assertNoText(format_string('node.type.@type', ['@type' => $node_type->id()]));
|
||||
$this->assertNoText(format_string('field.field.node.@type.body', ['@type' => $node_type->id()]));
|
||||
$this->assertNoText(format_string('core.entity_view_display.node.@type.teaser', ['@type' => $node_type->id()]));
|
||||
$this->assertNoText(format_string('core.entity_view_display.node.@type.default', ['@type' => $node_type->id()]));
|
||||
$this->assertNoText(format_string('core.entity_form_display.node.@type.default', ['@type' => $node_type->id()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config importer cannot uninstall extensions which are depended on.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testExtensionValidation() {
|
||||
\Drupal::service('module_installer')->install(['node']);
|
||||
\Drupal::service('theme_handler')->install(['bartik']);
|
||||
$this->rebuildContainer();
|
||||
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$this->copyConfig($this->container->get('config.storage'), $sync);
|
||||
$core = $sync->read('core.extension');
|
||||
// Node depends on text.
|
||||
unset($core['module']['text']);
|
||||
$module_data = system_rebuild_module_data();
|
||||
$this->assertTrue(isset($module_data['node']->requires['text']), 'The Node module depends on the Text module.');
|
||||
// Bartik depends on classy.
|
||||
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.');
|
||||
// This module does not exist.
|
||||
$core['module']['does_not_exist'] = 0;
|
||||
// This theme does not exist.
|
||||
$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.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that errors set in the batch and on the ConfigImporter are merged.
|
||||
*/
|
||||
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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests importing configuration from an uploaded file.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportUploadTest extends BrowserTestBase {
|
||||
|
||||
use TestFileCreationTrait;
|
||||
|
||||
/**
|
||||
* A user with the 'import configuration' permission.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $webUser;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['config'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->webUser = $this->drupalCreateUser(['import configuration']);
|
||||
$this->drupalLogin($this->webUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing configuration.
|
||||
*/
|
||||
public function testImport() {
|
||||
// Verify access to the config upload form.
|
||||
$this->drupalGet('admin/config/development/configuration/full/import');
|
||||
$this->assertResponse(200);
|
||||
|
||||
// Attempt to upload a non-tar file.
|
||||
$text_file = $this->getTestFiles('text')[0];
|
||||
$edit = ['files[import_tarball]' => \Drupal::service('file_system')->realpath($text_file->uri)];
|
||||
$this->drupalPostForm('admin/config/development/configuration/full/import', $edit, t('Upload'));
|
||||
$this->assertText(t('Could not extract the contents of the tar file'));
|
||||
|
||||
// Make the sync directory read-only.
|
||||
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
|
||||
\Drupal::service('file_system')->chmod($directory, 0555);
|
||||
$this->drupalGet('admin/config/development/configuration/full/import');
|
||||
$this->assertRaw(t('The directory %directory is not writable.', ['%directory' => $directory]));
|
||||
// Ensure submit button for \Drupal\config\Form\ConfigImportForm is
|
||||
// disabled.
|
||||
$submit_is_disabled = $this->cssSelect('form.config-import-form input[type="submit"]:disabled');
|
||||
$this->assertTrue(count($submit_is_disabled) === 1, 'The submit button is disabled.');
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@ class ConfigInstallProfileOverrideTest extends BrowserTestBase {
|
|||
*/
|
||||
protected $profile = 'testing_config_overrides';
|
||||
|
||||
|
||||
/**
|
||||
* Tests install profile config changes.
|
||||
*/
|
||||
|
@ -108,7 +107,16 @@ class ConfigInstallProfileOverrideTest extends BrowserTestBase {
|
|||
$this->container->get('module_installer')->install(['dblog']);
|
||||
$this->rebuildContainer();
|
||||
$this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration and is installed when its dependencies are met.');
|
||||
$this->assertEqual($config_test_storage->load('completely_new')->label(), 'Completely new optional configuration', 'The optional config_test entity is provided by the profile optional configuration and is installed when its dependencies are met.');
|
||||
$config_test_new = $config_test_storage->load('completely_new');
|
||||
$this->assertEqual($config_test_new->label(), 'Completely new optional configuration', 'The optional config_test entity is provided by the profile optional configuration and is installed when its dependencies are met.');
|
||||
$config_test_new->delete();
|
||||
|
||||
// Install another module that provides optional configuration and ensure
|
||||
// that deleted profile configuration is not re-created.
|
||||
$this->container->get('module_installer')->install(['config_other_module_config_test']);
|
||||
$this->rebuildContainer();
|
||||
$config_test_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$this->assertNull($config_test_storage->load('completely_new'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\FunctionalTests\Installer\InstallerTestBase;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
|
||||
/**
|
||||
* Tests install profile config overrides can not add unmet dependencies.
|
||||
*
|
||||
* @group Config
|
||||
*/
|
||||
class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
|
||||
|
||||
/**
|
||||
* The installation profile to install.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing_config_overrides';
|
||||
|
||||
/**
|
||||
* Contains the expected exception if it is thrown.
|
||||
*
|
||||
* @var \Drupal\Core\Config\UnmetDependenciesException
|
||||
*/
|
||||
protected $expectedException = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareEnvironment() {
|
||||
parent::prepareEnvironment();
|
||||
$this->copyTestingOverrides();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
// During set up an UnmetDependenciesException should be thrown, which will
|
||||
// be re-thrown by TestHttpClientMiddleware as a standard Exception.
|
||||
try {
|
||||
parent::setUp();
|
||||
}
|
||||
catch (\Exception $exception) {
|
||||
$this->expectedException = $exception;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the testing_config_overrides install profile.
|
||||
*
|
||||
* So we can change the configuration to include a dependency that can not be
|
||||
* met. File API functions are not available yet.
|
||||
*/
|
||||
protected function copyTestingOverrides() {
|
||||
$dest = $this->siteDirectory . '/profiles/testing_config_overrides';
|
||||
mkdir($dest, 0777, TRUE);
|
||||
$source = DRUPAL_ROOT . '/core/profiles/testing_config_overrides';
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($iterator as $item) {
|
||||
if ($item->isDir()) {
|
||||
mkdir($dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
else {
|
||||
copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
}
|
||||
|
||||
// Add a dependency that can not be met because User is installed before
|
||||
// Action.
|
||||
$config_file = $dest . DIRECTORY_SEPARATOR . InstallStorage::CONFIG_INSTALL_DIRECTORY . DIRECTORY_SEPARATOR . 'system.action.user_block_user_action.yml';
|
||||
$action = Yaml::decode(file_get_contents($config_file));
|
||||
$action['dependencies']['module'][] = 'action';
|
||||
file_put_contents($config_file, Yaml::encode($action));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the installation succeeded.
|
||||
*/
|
||||
public function testInstalled() {
|
||||
if ($this->expectedException) {
|
||||
$this->assertContains('Configuration objects provided by <em class="placeholder">user</em> have unmet dependencies: <em class="placeholder">system.action.user_block_user_action (action)</em>', $this->expectedException->getMessage());
|
||||
$this->assertContains('Drupal\Core\Config\UnmetDependenciesException', $this->expectedException->getMessage());
|
||||
}
|
||||
else {
|
||||
$this->fail('Expected Drupal\Core\Config\UnmetDependenciesException exception not thrown');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests installation and removal of configuration objects in install, disable
|
||||
* and uninstall functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigInstallWebTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* The admin user used in this test.
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->adminUser = $this->drupalCreateUser(['administer modules', 'administer themes', 'administer site configuration']);
|
||||
|
||||
// Ensure the global variable being asserted by this test does not exist;
|
||||
// a previous test executed in this request/process might have set it.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests module re-installation.
|
||||
*/
|
||||
public function testIntegrationModuleReinstallation() {
|
||||
$default_config = 'config_integration_test.settings';
|
||||
$default_configuration_entity = 'config_test.dynamic.config_integration_test';
|
||||
|
||||
// Install the config_test module we're integrating with.
|
||||
\Drupal::service('module_installer')->install(['config_test']);
|
||||
|
||||
// Verify the configuration does not exist prior to installation.
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), TRUE);
|
||||
$config_entity = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), TRUE);
|
||||
|
||||
// Install the integration module.
|
||||
\Drupal::service('module_installer')->install(['config_integration_test']);
|
||||
$this->resetAll();
|
||||
|
||||
// Verify that default module config exists.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
\Drupal::configFactory()->reset($default_configuration_entity);
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), FALSE);
|
||||
$this->assertIdentical($config_static->get('foo'), 'default setting');
|
||||
$config_entity = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), FALSE);
|
||||
$this->assertIdentical($config_entity->get('label'), 'Default integration config label');
|
||||
|
||||
// Customize both configuration objects.
|
||||
$config_static->set('foo', 'customized setting')->save();
|
||||
$config_entity->set('label', 'Customized integration config label')->save();
|
||||
|
||||
// @todo FIXME: Setting config keys WITHOUT SAVING retains the changed config
|
||||
// object in memory. Every new call to $this->config() MUST revert in-memory changes
|
||||
// that haven't been saved!
|
||||
// In other words: This test passes even without this reset, but it shouldn't.
|
||||
$this->container->get('config.factory')->reset();
|
||||
|
||||
// Disable and uninstall the integration module.
|
||||
$this->container->get('module_installer')->uninstall(['config_integration_test']);
|
||||
|
||||
// Verify the integration module's config was uninstalled.
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), TRUE);
|
||||
|
||||
// Verify the integration config still exists.
|
||||
$config_entity = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), FALSE);
|
||||
$this->assertIdentical($config_entity->get('label'), 'Customized integration config label');
|
||||
|
||||
// Reinstall the integration module.
|
||||
try {
|
||||
\Drupal::service('module_installer')->install(['config_integration_test']);
|
||||
$this->fail('Expected PreExistingConfigException not thrown.');
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_integration_test');
|
||||
$this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.config_integration_test']]);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.config_integration_test) provided by config_integration_test already exist in active configuration');
|
||||
}
|
||||
|
||||
// Delete the configuration entity so that the install will work.
|
||||
$config_entity->delete();
|
||||
\Drupal::service('module_installer')->install(['config_integration_test']);
|
||||
|
||||
// Verify the integration module's config was re-installed.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
\Drupal::configFactory()->reset($default_configuration_entity);
|
||||
$config_static = $this->config($default_config);
|
||||
$this->assertIdentical($config_static->isNew(), FALSE);
|
||||
$this->assertIdentical($config_static->get('foo'), 'default setting');
|
||||
|
||||
// Verify the integration config is using the default.
|
||||
$config_entity = \Drupal::config($default_configuration_entity);
|
||||
$this->assertIdentical($config_entity->isNew(), FALSE);
|
||||
$this->assertIdentical($config_entity->get('label'), 'Default integration config label');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pre-existing configuration detection.
|
||||
*/
|
||||
public function testPreExistingConfigInstall() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Try to install config_install_fail_test and config_test. Doing this
|
||||
// will install the config_test module first because it is a dependency of
|
||||
// config_install_fail_test.
|
||||
// @see \Drupal\system\Form\ModulesListForm::submitForm()
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_test][enable]' => TRUE, 'modules[config_install_fail_test][enable]' => TRUE], t('Install'));
|
||||
$this->assertRaw('Unable to install Configuration install fail test, <em class="placeholder">config_test.dynamic.dotted.default</em> already exists in active configuration.');
|
||||
|
||||
// Uninstall the config_test module to test the confirm form.
|
||||
$this->drupalPostForm('admin/modules/uninstall', ['uninstall[config_test]' => TRUE], t('Uninstall'));
|
||||
$this->drupalPostForm(NULL, [], t('Uninstall'));
|
||||
|
||||
// Try to install config_install_fail_test without selecting config_test.
|
||||
// The user is shown a confirm form because the config_test module is a
|
||||
// dependency.
|
||||
// @see \Drupal\system\Form\ModulesListConfirmForm::submitForm()
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_install_fail_test][enable]' => TRUE], t('Install'));
|
||||
$this->drupalPostForm(NULL, [], t('Continue'));
|
||||
$this->assertRaw('Unable to install Configuration install fail test, <em class="placeholder">config_test.dynamic.dotted.default</em> already exists in active configuration.');
|
||||
|
||||
// Test that collection configuration clashes during a module install are
|
||||
// reported correctly.
|
||||
\Drupal::service('module_installer')->install(['language']);
|
||||
$this->rebuildContainer();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('fr', 'config_test.dynamic.dotted.default')
|
||||
->set('label', 'Je suis Charlie')
|
||||
->save();
|
||||
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_install_fail_test][enable]' => TRUE], t('Install'));
|
||||
$this->assertRaw('Unable to install Configuration install fail test, <em class="placeholder">config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default</em> already exist in active configuration.');
|
||||
|
||||
// Test installing a theme through the UI that has existing configuration.
|
||||
// This relies on the fact the config_test has been installed and created
|
||||
// the config_test.dynamic.dotted.default configuration and the translation
|
||||
// override created still exists.
|
||||
$this->drupalGet('admin/appearance');
|
||||
$url = $this->xpath("//a[contains(@href,'config_clash_test_theme') and contains(@href,'/install?')]/@href")[0];
|
||||
$this->drupalGet($this->getAbsoluteUrl($url->getText()));
|
||||
$this->assertRaw('Unable to install config_clash_test_theme, <em class="placeholder">config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default</em> already exist in active configuration.');
|
||||
|
||||
// Test installing a theme through the API that has existing configuration.
|
||||
try {
|
||||
\Drupal::service('theme_handler')->install(['config_clash_test_theme']);
|
||||
$this->fail('Expected PreExistingConfigException not thrown.');
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_clash_test_theme');
|
||||
$this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.dotted.default'], 'language.fr' => ['config_test.dynamic.dotted.default']]);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default) provided by config_clash_test_theme already exist in active configuration');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests unmet dependencies detection.
|
||||
*/
|
||||
public function testUnmetDependenciesInstall() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
// We need to install separately since config_install_dependency_test does
|
||||
// not depend on config_test and order is important.
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_test][enable]' => TRUE], t('Install'));
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_install_dependency_test][enable]' => TRUE], t('Install'));
|
||||
$this->assertRaw('Unable to install <em class="placeholder">Config install dependency test</em> due to unmet dependencies: <em class="placeholder">config_test.dynamic.other_module_test_with_dependency (config_other_module_config_test, config_test.dynamic.dotted.english)</em>');
|
||||
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_test_language][enable]' => TRUE], t('Install'));
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_install_dependency_test][enable]' => TRUE], t('Install'));
|
||||
$this->assertRaw('Unable to install <em class="placeholder">Config install dependency test</em> due to unmet dependencies: <em class="placeholder">config_test.dynamic.other_module_test_with_dependency (config_other_module_config_test)</em>');
|
||||
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_other_module_config_test][enable]' => TRUE], t('Install'));
|
||||
$this->drupalPostForm('admin/modules', ['modules[config_install_dependency_test][enable]' => TRUE], t('Install'));
|
||||
$this->rebuildContainer();
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config_requirements().
|
||||
*/
|
||||
public function testConfigModuleRequirements() {
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalPostForm('admin/modules', ['modules[config][enable]' => TRUE], t('Install'));
|
||||
|
||||
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
|
||||
file_unmanaged_delete_recursive($directory);
|
||||
$this->drupalGet('/admin/reports/status');
|
||||
$this->assertRaw(t('The directory %directory does not exist.', ['%directory' => $directory]));
|
||||
}
|
||||
|
||||
}
|
|
@ -21,7 +21,7 @@ class ConfigLanguageOverrideWebTest extends BrowserTestBase {
|
|||
public static $modules = [
|
||||
'block',
|
||||
'language',
|
||||
'system'
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -62,12 +62,29 @@ class ConfigOtherModuleTest extends BrowserTestBase {
|
|||
$this->assertNull(entity_load('config_test', 'other_module_test_unmet', TRUE), 'The optional configuration config_test.dynamic.other_module_test_unmet whose dependencies are not met is not created.');
|
||||
$this->assertNull(entity_load('config_test', 'other_module_test_optional_entity_unmet', TRUE), 'The optional configuration config_test.dynamic.other_module_test_optional_entity_unmet whose dependencies are not met is not created.');
|
||||
$this->installModule('config_test_language');
|
||||
$this->assertNull(entity_load('config_test', 'other_module_test_optional_entity_unmet2', TRUE), 'The optional configuration config_test.dynamic.other_module_test_optional_entity_unmet2 whose dependencies are not met is not created.');
|
||||
$this->installModule('config_install_dependency_test');
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_unmet', TRUE), 'The optional configuration config_test.dynamic.other_module_test_unmet whose dependencies are met is now created.');
|
||||
// Although the following configuration entity's are now met it is not
|
||||
// installed because it does not have a direct dependency on the
|
||||
// config_install_dependency_test module.
|
||||
$this->assertNull(entity_load('config_test', 'other_module_test_optional_entity_unmet', TRUE), 'The optional configuration config_test.dynamic.other_module_test_optional_entity_unmet whose dependencies are met is not created.');
|
||||
// The following configuration entity's dependencies are now met. It is
|
||||
// indirectly dependent on the config_install_dependency_test module because
|
||||
// it has a dependency on the config_test.dynamic.dependency_for_unmet2
|
||||
// configuration provided by that module and, therefore, should be created.
|
||||
$this->assertTrue(entity_load('config_test', 'other_module_test_optional_entity_unmet2', TRUE), 'The optional configuration config_test.dynamic.other_module_test_optional_entity_unmet2 whose dependencies are met is now created.');
|
||||
|
||||
// The following configuration entity's dependencies are now met even though
|
||||
// it has no direct dependency on the module. It is indirectly dependent on
|
||||
// the config_install_dependency_test module because it has a dependency on
|
||||
// the config_test.dynamic.other_module_test_unmet configuration that is
|
||||
// dependent on the config_install_dependency_test module and, therefore,
|
||||
// should be created.
|
||||
$entity = entity_load('config_test', 'other_module_test_optional_entity_unmet', TRUE);
|
||||
$this->assertTrue($entity, 'The optional configuration config_test.dynamic.other_module_test_optional_entity_unmet whose dependencies are met is created.');
|
||||
$entity->delete();
|
||||
|
||||
// Install another module to ensure the configuration just deleted is not
|
||||
// recreated.
|
||||
$this->installModule('config');
|
||||
$this->assertFalse(entity_load('config_test', 'other_module_test_optional_entity_unmet', TRUE), 'The optional configuration config_test.dynamic.other_module_test_optional_entity_unmet whose dependencies are met is not installed when an unrelated module is installed.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Core\Serialization\Yaml;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing/exporting a single configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigSingleImportExportTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'block',
|
||||
'config',
|
||||
'config_test',
|
||||
// Adding language module makes it possible to involve non-default
|
||||
// (language.xx) collections in import/export operations.
|
||||
'language',
|
||||
];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing a single configuration file.
|
||||
*/
|
||||
public function testImport() {
|
||||
$storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$uuid = \Drupal::service('uuid');
|
||||
|
||||
$this->drupalLogin($this->drupalCreateUser(['import configuration']));
|
||||
|
||||
// Attempt an import with invalid YAML.
|
||||
$edit = [
|
||||
'config_type' => 'action',
|
||||
'import' => '{{{',
|
||||
];
|
||||
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
// Assert the static portion of the error since different parsers could give different text in their error.
|
||||
$this->assertText('The import failed with the following message: ');
|
||||
|
||||
$import = <<<EOD
|
||||
label: First
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '1'
|
||||
EOD;
|
||||
$edit = [
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
];
|
||||
// Attempt an import with a missing ID.
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('Missing ID key "@id_key" for this @entity_type import.', ['@id_key' => 'id', '@entity_type' => 'Test configuration']));
|
||||
|
||||
// Perform an import with no specified UUID and a unique ID.
|
||||
$this->assertNull($storage->load('first'));
|
||||
$edit['import'] = "id: first\n" . $edit['import'];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to create a new %name @type?', ['%name' => 'first', '@type' => 'test configuration']));
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$entity = $storage->load('first');
|
||||
$this->assertIdentical($entity->label(), 'First');
|
||||
$this->assertIdentical($entity->id(), 'first');
|
||||
$this->assertTrue($entity->status());
|
||||
$this->assertRaw(t('The configuration was imported successfully.'));
|
||||
|
||||
// Attempt an import with an existing ID but missing UUID.
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('An entity with this machine name already exists but the import did not specify a UUID.'));
|
||||
|
||||
// Attempt an import with a mismatched UUID and existing ID.
|
||||
$edit['import'] .= "\nuuid: " . $uuid->generate();
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('An entity with this machine name already exists but the UUID does not match.'));
|
||||
|
||||
// Attempt an import with a custom ID.
|
||||
$edit['custom_entity_id'] = 'custom_id';
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to create a new %name @type?', ['%name' => 'custom_id', '@type' => 'test configuration']));
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$this->assertRaw(t('The configuration was imported successfully.'));
|
||||
|
||||
// Perform an import with a unique ID and UUID.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
label: Second
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
EOD;
|
||||
$edit = [
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
];
|
||||
$second_uuid = $uuid->generate();
|
||||
$edit['import'] .= "\nuuid: " . $second_uuid;
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to create a new %name @type?', ['%name' => 'second', '@type' => 'test configuration']));
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$entity = $storage->load('second');
|
||||
$this->assertRaw(t('The configuration was imported successfully.'));
|
||||
$this->assertIdentical($entity->label(), 'Second');
|
||||
$this->assertIdentical($entity->id(), 'second');
|
||||
$this->assertFalse($entity->status());
|
||||
$this->assertIdentical($entity->uuid(), $second_uuid);
|
||||
|
||||
// Perform an update.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
uuid: $second_uuid
|
||||
label: 'Second updated'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
EOD;
|
||||
$edit = [
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', ['%name' => 'second', '@type' => 'test configuration']));
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$entity = $storage->load('second');
|
||||
$this->assertRaw(t('The configuration was imported successfully.'));
|
||||
$this->assertIdentical($entity->label(), 'Second updated');
|
||||
|
||||
// Try to perform an update which adds missing dependencies.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
uuid: $second_uuid
|
||||
label: 'Second updated'
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
dependencies:
|
||||
module:
|
||||
- does_not_exist
|
||||
EOD;
|
||||
$edit = [
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Configuration %name depends on the %owner module that will not be installed after import.', ['%name' => 'config_test.dynamic.second', '%owner' => 'does_not_exist']));
|
||||
|
||||
// Try to preform an update which would create a PHP object if Yaml parsing
|
||||
// not securely set up.
|
||||
// Perform an update.
|
||||
$import = <<<EOD
|
||||
id: second
|
||||
uuid: $second_uuid
|
||||
label: !php/object "O:36:\"Drupal\\\Core\\\Test\\\ObjectSerialization\":0:{}"
|
||||
weight: 0
|
||||
style: ''
|
||||
status: '0'
|
||||
EOD;
|
||||
$edit = [
|
||||
'config_type' => 'config_test',
|
||||
'import' => $import,
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
if (extension_loaded('yaml')) {
|
||||
// If the yaml extension is loaded it will work but not create the PHP
|
||||
// object.
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', [
|
||||
'%name' => 'second',
|
||||
'@type' => 'test configuration',
|
||||
]));
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$entity = $storage->load('second');
|
||||
$this->assertRaw(t('The configuration was imported successfully.'));
|
||||
$this->assertTrue(is_string($entity->label()), 'Entity label is a string');
|
||||
$this->assertTrue(strpos($entity->label(), 'ObjectSerialization') > 0, 'Label contains serialized object');
|
||||
}
|
||||
else {
|
||||
// If the Symfony parser is used there will be an error.
|
||||
$this->assertSession()->responseContains('The import failed with the following message:');
|
||||
$this->assertSession()->responseContains('Object support when parsing a YAML file has been disabled');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing a simple configuration file.
|
||||
*/
|
||||
public function testImportSimpleConfiguration() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['import configuration']));
|
||||
$config = $this->config('system.site')->set('name', 'Test simple import');
|
||||
|
||||
// Place branding block with site name into header region.
|
||||
$this->drupalPlaceBlock('system_branding_block', ['region' => 'header']);
|
||||
|
||||
$edit = [
|
||||
'config_type' => 'system.simple',
|
||||
'config_name' => $config->getName(),
|
||||
'import' => Yaml::encode($config->get()),
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', ['%name' => $config->getName(), '@type' => 'simple configuration']));
|
||||
$this->drupalPostForm(NULL, [], t('Confirm'));
|
||||
$this->drupalGet('');
|
||||
$this->assertText('Test simple import');
|
||||
|
||||
// Ensure that ConfigImporter validation is running when importing simple
|
||||
// configuration.
|
||||
$config_data = $this->config('core.extension')->get();
|
||||
// Simulate uninstalling the Config module.
|
||||
unset($config_data['module']['config']);
|
||||
$edit = [
|
||||
'config_type' => 'system.simple',
|
||||
'config_name' => 'core.extension',
|
||||
'import' => Yaml::encode($config_data),
|
||||
];
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertText(t('Can not uninstall the Configuration module as part of a configuration synchronization through the user interface.'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests exporting a single configuration file.
|
||||
*/
|
||||
public function testExport() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['export configuration']));
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/system.simple');
|
||||
$this->assertFieldByXPath('//select[@name="config_type"]//option[@selected="selected"]', t('Simple configuration'), 'The simple configuration option is selected when specified in the URL.');
|
||||
// Spot check several known simple configuration files.
|
||||
$element = $this->xpath('//select[@name="config_name"]')[0];
|
||||
$options = $element->findAll('css', 'option');
|
||||
$expected_options = ['system.site', 'user.settings'];
|
||||
foreach ($options as &$option) {
|
||||
$option = $option->getValue();
|
||||
}
|
||||
$this->assertIdentical($expected_options, array_intersect($expected_options, $options), 'The expected configuration files are listed.');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/system.simple/system.image');
|
||||
$this->assertEquals("toolkit: gd\n_core:\n default_config_hash: durWHaKeBaq4d9Wpi4RqwADj1OufDepcnJuhVLmKN24\n", $this->xpath('//textarea[@name="export"]')[0]->getValue(), 'The expected system configuration is displayed.');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/date_format');
|
||||
$this->assertFieldByXPath('//select[@name="config_type"]//option[@selected="selected"]', t('Date format'), 'The date format entity type is selected when specified in the URL.');
|
||||
|
||||
$this->drupalGet('admin/config/development/configuration/single/export/date_format/fallback');
|
||||
$this->assertFieldByXPath('//select[@name="config_name"]//option[@selected="selected"]', t('Fallback date format (fallback)'), 'The fallback date format config entity is selected when specified in the URL.');
|
||||
|
||||
$fallback_date = \Drupal::entityManager()->getStorage('date_format')->load('fallback');
|
||||
$yaml_text = $this->xpath('//textarea[@name="export"]')[0]->getValue();
|
||||
$this->assertEqual(Yaml::decode($yaml_text), $fallback_date->toArray(), 'The fallback date format config entity export code is displayed.');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests language-negotiation overrides are not on language-negotiation form.
|
||||
*
|
||||
* @group config
|
||||
* @see \Drupal\Core\Form\ConfigFormBase
|
||||
*/
|
||||
class LanguageNegotiationFormOverrideTest extends BrowserTestBase {
|
||||
|
||||
public static $modules = ['language', 'locale', 'locale_test'];
|
||||
|
||||
/**
|
||||
* Tests that overrides do not affect language-negotiation form values.
|
||||
*/
|
||||
public function testFormWithOverride() {
|
||||
$this->drupalLogin($this->rootUser);
|
||||
$overridden_value_en = 'whatever';
|
||||
$overridden_value_es = 'loquesea';
|
||||
|
||||
// Set up an override.
|
||||
$settings['config']['language.negotiation']['url']['prefixes'] = (object) [
|
||||
'value' => ['en' => $overridden_value_en, 'es' => $overridden_value_es],
|
||||
'required' => TRUE,
|
||||
];
|
||||
$this->writeSettings($settings);
|
||||
|
||||
// Add predefined language.
|
||||
$edit = [
|
||||
'predefined_langcode' => 'es',
|
||||
];
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
|
||||
|
||||
// Overridden string for language-negotiation should not exist in the form.
|
||||
$this->drupalGet('admin/config/regional/language/detection/url');
|
||||
|
||||
// The language-negotiation form should be found.
|
||||
$this->assertText('Path prefix configuration', 'Language-negotiation form found for English.');
|
||||
|
||||
// The English override should not be found.
|
||||
$this->assertNoFieldByName('prefix[en]', $overridden_value_en, 'Language-negotiation config override not found in English.');
|
||||
|
||||
// Now check the Spanish version of the page for the same thing.
|
||||
$this->drupalGet($overridden_value_es . '/admin/config/regional/language/detection/url');
|
||||
|
||||
// The language-negotiation form should be found.
|
||||
$this->assertText('Path prefix configuration', 'Language-negotiation form found for Spanish using the overridden prefix.');
|
||||
|
||||
// The Spanish override should not be found.
|
||||
$this->assertNoFieldByName('prefix[es]', $overridden_value_es, 'Language-negotiation config override not found in Spanish.');
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\FunctionalJavascript;
|
||||
|
||||
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
|
||||
|
||||
/**
|
||||
* Tests the Config operations through the UI.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityTest extends WebDriverTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['config_test'];
|
||||
|
||||
/**
|
||||
* Tests ajax operations through the UI on 'Add' page.
|
||||
*/
|
||||
public function testAjaxOnAddPage() {
|
||||
$this->drupalLogin($this->drupalCreateUser(['administer site configuration']));
|
||||
|
||||
$page = $this->getSession()->getPage();
|
||||
$assert_session = $this->assertSession();
|
||||
|
||||
$this->drupalGet('admin/structure/config_test/add');
|
||||
// Test that 'size value' field is not show initially, and it is show after
|
||||
// selecting value in the 'size' field.
|
||||
$this->assertNull($page->findField('size_value'));
|
||||
$page->findField('size')->setValue('custom');
|
||||
$this->assertNotNull($assert_session->waitForField('size_value'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Kernel;
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing configuration from files into active configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigUninstallViaCliImportTest extends KernelTestBase {
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'config'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
$this->markTestSkipped('This test has to be run from the CLI');
|
||||
}
|
||||
|
||||
$this->installConfig(['system']);
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the config module can be uninstalled via CLI config import.
|
||||
*
|
||||
* @see \Drupal\config\ConfigSubscriber
|
||||
*/
|
||||
public function testConfigUninstallViaCli() {
|
||||
$this->assertTrue($this->container->get('module_handler')->moduleExists('config'));
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$extensions = $sync->read('core.extension');
|
||||
unset($extensions['module']['config']);
|
||||
$sync->write('core.extension', $extensions);
|
||||
$this->configImporter->reset()->import();
|
||||
$this->assertFalse($this->container->get('module_handler')->moduleExists('config'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Tests\config\Traits;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityInterface;
|
||||
|
||||
/**
|
||||
* Provides test assertions for testing config entity synchronization.
|
||||
*
|
||||
* Can be used by test classes that extend \Drupal\Tests\BrowserTestBase or
|
||||
* \Drupal\KernelTests\KernelTestBase.
|
||||
*/
|
||||
trait AssertConfigEntityImportTrait {
|
||||
|
||||
/**
|
||||
* Asserts that a config entity can be imported without changing it.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
|
||||
* The config entity to test importing.
|
||||
*/
|
||||
public function assertConfigEntityImport(ConfigEntityInterface $entity) {
|
||||
// Save original config information.
|
||||
$entity_uuid = $entity->uuid();
|
||||
$entity_type_id = $entity->getEntityTypeId();
|
||||
$original_data = $entity->toArray();
|
||||
// Copy everything to sync.
|
||||
$this->copyConfig(\Drupal::service('config.storage'), \Drupal::service('config.storage.sync'));
|
||||
// Delete the configuration from active. Don't worry about side effects of
|
||||
// deleting config like fields cleaning up field storages. The coming import
|
||||
// should recreate everything as necessary.
|
||||
$entity->delete();
|
||||
$this->configImporter()->reset()->import();
|
||||
$imported_entity = \Drupal::service('entity.repository')->loadEntityByUuid($entity_type_id, $entity_uuid);
|
||||
$this->assertIdentical($original_data, $imported_entity->toArray());
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue