Issue #3294914 by Spokje, quietone, bbrala, longwave, Gábor Hojtsy, benjifisher, dww, xjm, rkoller: Create dedicated error section for missing removed core modules/themes on update
parent
7643161492
commit
22a56817e0
|
@ -28,6 +28,30 @@ use Drupal\Core\Url;
|
||||||
use GuzzleHttp\Exception\TransferException;
|
use GuzzleHttp\Exception\TransferException;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
// cspell:ignore quickedit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of machine names of modules that were removed from Drupal core.
|
||||||
|
*/
|
||||||
|
const DRUPAL_CORE_REMOVED_MODULE_LIST = [
|
||||||
|
'aggregator' => 'Aggregator',
|
||||||
|
'ckeditor' => 'CKEditor',
|
||||||
|
'color' => 'Color',
|
||||||
|
'hal' => 'HAL',
|
||||||
|
'quickedit' => 'Quick Edit',
|
||||||
|
'rdf' => 'RDF',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of machine names of themes that were removed from Drupal core.
|
||||||
|
*/
|
||||||
|
const DRUPAL_CORE_REMOVED_THEME_LIST = [
|
||||||
|
'bartik' => 'Bartik',
|
||||||
|
'classy' => 'Classy',
|
||||||
|
'seven' => 'Seven',
|
||||||
|
'stable' => 'Stable',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_requirements().
|
* Implements hook_requirements().
|
||||||
*/
|
*/
|
||||||
|
@ -989,13 +1013,16 @@ function system_requirements($phase) {
|
||||||
|
|
||||||
// Display an error if a newly introduced dependency in a module is not resolved.
|
// Display an error if a newly introduced dependency in a module is not resolved.
|
||||||
if ($phase === 'update' || $phase === 'runtime') {
|
if ($phase === 'update' || $phase === 'runtime') {
|
||||||
$create_extension_incompatibility_list = function ($extension_names, $description, $title) {
|
$create_extension_incompatibility_list = function (array $extension_names, PluralTranslatableMarkup $description, PluralTranslatableMarkup $title, TranslatableMarkup|string $message = '', TranslatableMarkup|string $additional_description = '') {
|
||||||
|
if ($message === '') {
|
||||||
|
$message = new TranslatableMarkup('Review the <a href=":url"> suggestions for resolving this incompatibility</a> to repair your installation, and then re-run update.php.', [':url' => 'https://www.drupal.org/docs/updating-drupal/troubleshooting-database-updates']);
|
||||||
|
}
|
||||||
// Use an inline twig template to:
|
// Use an inline twig template to:
|
||||||
// - Concatenate two MarkupInterface objects and preserve safeness.
|
// - Concatenate MarkupInterface objects and preserve safeness.
|
||||||
// - Use the item_list theme for the extension list.
|
// - Use the item_list theme for the extension list.
|
||||||
$template = [
|
$template = [
|
||||||
'#type' => 'inline_template',
|
'#type' => 'inline_template',
|
||||||
'#template' => '{{ description }}{{ extensions }}',
|
'#template' => '{{ description }}{{ extensions }}{{ additional_description }}<br>',
|
||||||
'#context' => [
|
'#context' => [
|
||||||
'extensions' => [
|
'extensions' => [
|
||||||
'#theme' => 'item_list',
|
'#theme' => 'item_list',
|
||||||
|
@ -1004,15 +1031,13 @@ function system_requirements($phase) {
|
||||||
];
|
];
|
||||||
$template['#context']['extensions']['#items'] = $extension_names;
|
$template['#context']['extensions']['#items'] = $extension_names;
|
||||||
$template['#context']['description'] = $description;
|
$template['#context']['description'] = $description;
|
||||||
|
$template['#context']['additional_description'] = $additional_description;
|
||||||
return [
|
return [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'value' => [
|
'value' => [
|
||||||
'list' => $template,
|
'list' => $template,
|
||||||
'handbook_link' => [
|
'handbook_link' => [
|
||||||
'#markup' => t(
|
'#markup' => $message,
|
||||||
'Review the <a href=":url"> suggestions for resolving this incompatibility</a> to repair your installation, and then re-run update.php.',
|
|
||||||
[':url' => 'https://www.drupal.org/docs/8/update/troubleshooting-database-updates']
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'severity' => REQUIREMENT_ERROR,
|
'severity' => REQUIREMENT_ERROR,
|
||||||
|
@ -1134,13 +1159,89 @@ function system_requirements($phase) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for invalid modules.
|
|
||||||
$extension_config = \Drupal::configFactory()->get('core.extension');
|
$extension_config = \Drupal::configFactory()->get('core.extension');
|
||||||
$is_missing_extension = function ($extension_name) use (&$module_extension_list) {
|
|
||||||
return !$module_extension_list->exists($extension_name);
|
|
||||||
};
|
|
||||||
|
|
||||||
$invalid_modules = array_filter(array_keys($extension_config->get('module')), $is_missing_extension);
|
// Look for removed core modules.
|
||||||
|
$is_removed_module = function ($extension_name) use ($module_extension_list) {
|
||||||
|
return !$module_extension_list->exists($extension_name)
|
||||||
|
&& array_key_exists($extension_name, DRUPAL_CORE_REMOVED_MODULE_LIST);
|
||||||
|
};
|
||||||
|
$removed_modules = array_filter(array_keys($extension_config->get('module')), $is_removed_module);
|
||||||
|
if (!empty($removed_modules)) {
|
||||||
|
$list = [];
|
||||||
|
foreach ($removed_modules as $removed_module) {
|
||||||
|
$list[] = t('<a href=":url">@module</a>', [
|
||||||
|
':url' => "https://www.drupal.org/project/$removed_module",
|
||||||
|
'@module' => DRUPAL_CORE_REMOVED_MODULE_LIST[$removed_module],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$requirements['removed_module'] = $create_extension_incompatibility_list(
|
||||||
|
$list,
|
||||||
|
new PluralTranslatableMarkup(
|
||||||
|
count($removed_modules),
|
||||||
|
'You must add the following contributed module and reload this page.',
|
||||||
|
'You must add the following contributed modules and reload this page.'
|
||||||
|
),
|
||||||
|
new PluralTranslatableMarkup(
|
||||||
|
count($removed_modules),
|
||||||
|
'Removed core module',
|
||||||
|
'Removed core modules'
|
||||||
|
),
|
||||||
|
new TranslatableMarkup(
|
||||||
|
'For more information read the <a href=":url">documentation on deprecated modules.</a>',
|
||||||
|
[':url' => 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-modules']
|
||||||
|
),
|
||||||
|
new PluralTranslatableMarkup(
|
||||||
|
count($removed_modules),
|
||||||
|
'This module is installed on your site but is no longer provided by Core.',
|
||||||
|
'These modules are installed on your site but are no longer provided by Core.'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for removed core themes.
|
||||||
|
$is_removed_theme = function ($extension_name) use ($theme_extension_list) {
|
||||||
|
return !$theme_extension_list->exists($extension_name)
|
||||||
|
&& array_key_exists($extension_name, DRUPAL_CORE_REMOVED_THEME_LIST);
|
||||||
|
};
|
||||||
|
$removed_themes = array_filter(array_keys($extension_config->get('theme')), $is_removed_theme);
|
||||||
|
if (!empty($removed_themes)) {
|
||||||
|
$list = [];
|
||||||
|
foreach ($removed_themes as $removed_theme) {
|
||||||
|
$list[] = t('<a href=":url">@theme</a>', [
|
||||||
|
':url' => "https://www.drupal.org/project/$removed_theme",
|
||||||
|
'@theme' => DRUPAL_CORE_REMOVED_THEME_LIST[$removed_theme],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$requirements['removed_theme'] = $create_extension_incompatibility_list(
|
||||||
|
$list,
|
||||||
|
new PluralTranslatableMarkup(
|
||||||
|
count($removed_themes),
|
||||||
|
'You must add the following contributed theme and reload this page.',
|
||||||
|
'You must add the following contributed themes and reload this page.'
|
||||||
|
),
|
||||||
|
new PluralTranslatableMarkup(
|
||||||
|
count($removed_themes),
|
||||||
|
'Removed core theme',
|
||||||
|
'Removed core themes'
|
||||||
|
),
|
||||||
|
new TranslatableMarkup(
|
||||||
|
'For more information read the <a href=":url">documentation on deprecated themes.</a>',
|
||||||
|
[':url' => 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-themes']
|
||||||
|
),
|
||||||
|
new PluralTranslatableMarkup(
|
||||||
|
count($removed_themes),
|
||||||
|
'This theme is installed on your site but is no longer provided by Core.',
|
||||||
|
'These themes are installed on your site but are no longer provided by Core.'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for missing modules.
|
||||||
|
$is_missing_module = function ($extension_name) use ($module_extension_list) {
|
||||||
|
return !$module_extension_list->exists($extension_name) && !in_array($extension_name, array_keys(DRUPAL_CORE_REMOVED_MODULE_LIST), TRUE);
|
||||||
|
};
|
||||||
|
$invalid_modules = array_filter(array_keys($extension_config->get('module')), $is_missing_module);
|
||||||
|
|
||||||
if (!empty($invalid_modules)) {
|
if (!empty($invalid_modules)) {
|
||||||
$requirements['invalid_module'] = $create_extension_incompatibility_list(
|
$requirements['invalid_module'] = $create_extension_incompatibility_list(
|
||||||
|
@ -1160,7 +1261,7 @@ function system_requirements($phase) {
|
||||||
|
|
||||||
// Look for invalid themes.
|
// Look for invalid themes.
|
||||||
$is_missing_theme = function ($extension_name) use (&$theme_extension_list) {
|
$is_missing_theme = function ($extension_name) use (&$theme_extension_list) {
|
||||||
return !$theme_extension_list->exists($extension_name);
|
return !$theme_extension_list->exists($extension_name) && !in_array($extension_name, array_keys(DRUPAL_CORE_REMOVED_THEME_LIST), TRUE);
|
||||||
};
|
};
|
||||||
$invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_theme);
|
$invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_theme);
|
||||||
if (!empty($invalid_themes)) {
|
if (!empty($invalid_themes)) {
|
||||||
|
|
|
@ -224,7 +224,7 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
*
|
*
|
||||||
* @dataProvider providerExtensionCompatibilityChange
|
* @dataProvider providerExtensionCompatibilityChange
|
||||||
*/
|
*/
|
||||||
public function testExtensionCompatibilityChange(array $correct_info, array $breaking_info, $expected_error) {
|
public function testExtensionCompatibilityChange(array $correct_info, array $breaking_info, string $expected_error): void {
|
||||||
$extension_type = $correct_info['type'];
|
$extension_type = $correct_info['type'];
|
||||||
$this->drupalLogin(
|
$this->drupalLogin(
|
||||||
$this->drupalCreateUser(
|
$this->drupalCreateUser(
|
||||||
|
@ -236,8 +236,9 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$extension_machine_name = "changing_extension";
|
$extension_machine_names = ['changing_extension'];
|
||||||
$extension_name = "$extension_machine_name name";
|
$extension_name = "$extension_machine_names[0] name";
|
||||||
|
$test_error_urls = ['https://www.drupal.org/docs/updating-drupal/troubleshooting-database-updates'];
|
||||||
|
|
||||||
$test_error_text = "Incompatible $extension_type "
|
$test_error_text = "Incompatible $extension_type "
|
||||||
. $expected_error
|
. $expected_error
|
||||||
|
@ -247,25 +248,28 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
if ($extension_type === 'theme') {
|
if ($extension_type === 'theme') {
|
||||||
$base_info['base theme'] = FALSE;
|
$base_info['base theme'] = FALSE;
|
||||||
}
|
}
|
||||||
$folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$extension_type}s/$extension_machine_name";
|
$folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$extension_type}s/$extension_machine_names[0]";
|
||||||
$file_path = "$folder_path/$extension_machine_name.info.yml";
|
$file_path = "$folder_path/$extension_machine_names[0].info.yml";
|
||||||
mkdir($folder_path, 0777, TRUE);
|
mkdir($folder_path, 0777, TRUE);
|
||||||
file_put_contents($file_path, Yaml::encode($base_info + $correct_info));
|
file_put_contents($file_path, Yaml::encode($base_info + $correct_info));
|
||||||
$this->enableExtension($extension_type, $extension_machine_name, $extension_name);
|
$this->enableExtensions($extension_type, $extension_machine_names, [$extension_name]);
|
||||||
$this->assertInstalledExtensionConfig($extension_type, $extension_machine_name);
|
$this->assertInstalledExtensionsConfig($extension_type, $extension_machine_names);
|
||||||
|
|
||||||
// If there are no requirements warnings or errors, we expect to be able to
|
// If there are no requirements warnings or errors, we expect to be able to
|
||||||
// go through the update process uninterrupted.
|
// go through the update process uninterrupted.
|
||||||
$this->assertUpdateWithNoError($test_error_text, $extension_type, $extension_machine_name);
|
$this->drupalGet($this->statusReportUrl);
|
||||||
|
$this->assertUpdateWithNoErrors([$test_error_text], $extension_type, $extension_machine_names);
|
||||||
|
|
||||||
// Change the values in the info.yml and confirm updating is not possible.
|
// Change the values in the info.yml and confirm updating is not possible.
|
||||||
file_put_contents($file_path, Yaml::encode($base_info + $breaking_info));
|
file_put_contents($file_path, Yaml::encode($base_info + $breaking_info));
|
||||||
$this->assertErrorOnUpdate($test_error_text, $extension_type, $extension_machine_name);
|
$this->drupalGet($this->statusReportUrl);
|
||||||
|
$this->assertErrorOnUpdates([$test_error_text], $extension_type, $extension_machine_names, $test_error_urls);
|
||||||
|
|
||||||
// Fix the values in the info.yml file and confirm updating is possible
|
// Fix the values in the info.yml file and confirm updating is possible
|
||||||
// again.
|
// again.
|
||||||
file_put_contents($file_path, Yaml::encode($base_info + $correct_info));
|
file_put_contents($file_path, Yaml::encode($base_info + $correct_info));
|
||||||
$this->assertUpdateWithNoError($test_error_text, $extension_type, $extension_machine_name);
|
$this->drupalGet($this->statusReportUrl);
|
||||||
|
$this->assertUpdateWithNoErrors([$test_error_text], $extension_type, $extension_machine_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -329,53 +333,165 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
/**
|
/**
|
||||||
* Tests that a missing extension prevents updates.
|
* Tests that a missing extension prevents updates.
|
||||||
*
|
*
|
||||||
* @param string $extension_type
|
* @param array $core
|
||||||
* The extension type, either 'module' or 'theme'.
|
* An array keyed by 'module' and 'theme' where each sub array contains
|
||||||
|
* a list of extension machine names.
|
||||||
|
* @param array $contrib
|
||||||
|
* An array keyed by 'module' and 'theme' where each sub array contains
|
||||||
|
* a list of extension machine names.
|
||||||
*
|
*
|
||||||
* @dataProvider providerMissingExtension
|
* @dataProvider providerMissingExtension
|
||||||
*/
|
*/
|
||||||
public function testMissingExtension($extension_type) {
|
public function testMissingExtension(array $core, array $contrib): void {
|
||||||
$this->drupalLogin(
|
$this->drupalLogin(
|
||||||
$this->drupalCreateUser(
|
$this->drupalCreateUser(
|
||||||
[
|
[
|
||||||
'administer software updates',
|
'administer software updates',
|
||||||
'administer site configuration',
|
'administer site configuration',
|
||||||
$extension_type === 'module' ? 'administer modules' : 'administer themes',
|
'administer modules',
|
||||||
|
'administer themes',
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$extension_machine_name = "disappearing_$extension_type";
|
|
||||||
$extension_name = 'The magically disappearing extension';
|
$all_extensions_info = [];
|
||||||
$test_error_text = "Missing or invalid $extension_type "
|
$file_paths = [];
|
||||||
. "The following $extension_type is marked as installed in the core.extension configuration, but it is missing:"
|
$test_error_texts = [];
|
||||||
. $extension_machine_name
|
$test_error_urls = [];
|
||||||
. static::HANDBOOK_MESSAGE;
|
$extension_base_info = [
|
||||||
$extension_info = [
|
'version' => 'VERSION',
|
||||||
'name' => $extension_name,
|
|
||||||
'type' => $extension_type,
|
|
||||||
'core_version_requirement' => '^8 || ^9 || ^10',
|
'core_version_requirement' => '^8 || ^9 || ^10',
|
||||||
];
|
];
|
||||||
if ($extension_type === 'theme') {
|
|
||||||
|
// For each core extension create and error of info.yml information and
|
||||||
|
// the expected error message.
|
||||||
|
foreach ($core as $type => $extensions) {
|
||||||
|
$removed_list = [];
|
||||||
|
$error_url = 'https://www.drupal.org/node/3223395#s-recommendations-for-deprecated-modules';
|
||||||
|
$extension_base_info += ['package' => 'Core'];
|
||||||
|
if ($type === 'module') {
|
||||||
|
$removed_core_list = \DRUPAL_CORE_REMOVED_MODULE_LIST;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$removed_core_list = \DRUPAL_CORE_REMOVED_THEME_LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($extensions as $extension) {
|
||||||
|
$extension_info = $extension_base_info +
|
||||||
|
[
|
||||||
|
'name' => "The magically disappearing core $type $extension",
|
||||||
|
'type' => $type,
|
||||||
|
];
|
||||||
|
if ($type === 'theme') {
|
||||||
$extension_info['base theme'] = FALSE;
|
$extension_info['base theme'] = FALSE;
|
||||||
}
|
}
|
||||||
$folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$extension_type}s/$extension_machine_name";
|
$all_extensions_info[$extension] = $extension_info;
|
||||||
$file_path = "$folder_path/$extension_machine_name.info.yml";
|
$removed_list[] = $removed_core_list[$extension];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the requirements test message.
|
||||||
|
if (!empty($extensions)) {
|
||||||
|
$handbook_message = "For more information read the documentation on deprecated {$type}s.";
|
||||||
|
if (count($removed_list) === 1) {
|
||||||
|
$test_error_texts[$type][] = "Removed core {$type} "
|
||||||
|
. "You must add the following contributed $type and reload this page."
|
||||||
|
. implode($removed_list)
|
||||||
|
. "This $type is installed on your site but is no longer provided by Core."
|
||||||
|
. $handbook_message;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$test_error_texts[$type][] = "Removed core {$type}s "
|
||||||
|
. "You must add the following contributed {$type}s and reload this page."
|
||||||
|
. implode($removed_list)
|
||||||
|
. "These {$type}s are installed on your site but are no longer provided by Core."
|
||||||
|
. $handbook_message;
|
||||||
|
}
|
||||||
|
$test_error_urls[$type][] = $error_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each contrib extension create and error of info.yml information and
|
||||||
|
// the expected error message.
|
||||||
|
foreach ($contrib as $type => $extensions) {
|
||||||
|
unset($extension_base_info['package']);
|
||||||
|
$handbook_message = 'Review the suggestions for resolving this incompatibility to repair your installation, and then re-run update.php.';
|
||||||
|
$error_url = 'https://www.drupal.org/docs/updating-drupal/troubleshooting-database-updates';
|
||||||
|
foreach ($extensions as $extension) {
|
||||||
|
$extension_info = $extension_base_info +
|
||||||
|
[
|
||||||
|
'name' => "The magically disappearing contrib $type $extension",
|
||||||
|
'type' => $type,
|
||||||
|
];
|
||||||
|
if ($type === 'theme') {
|
||||||
|
$extension_info['base theme'] = FALSE;
|
||||||
|
}
|
||||||
|
$all_extensions_info[$extension] = $extension_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the requirements test message.
|
||||||
|
if (!empty($extensions)) {
|
||||||
|
if (count($extensions) === 1) {
|
||||||
|
$test_error_texts[$type][] = "Missing or invalid {$type} "
|
||||||
|
. "The following {$type} is marked as installed in the core.extension configuration, but it is missing:"
|
||||||
|
. implode($extensions)
|
||||||
|
. $handbook_message;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$test_error_texts[$type][] = "Missing or invalid {$type}s "
|
||||||
|
. "The following {$type}s are marked as installed in the core.extension configuration, but they are missing:"
|
||||||
|
. implode($extensions)
|
||||||
|
. $handbook_message;
|
||||||
|
}
|
||||||
|
$test_error_urls[$type][] = $error_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the info.yml files for each extension.
|
||||||
|
foreach ($all_extensions_info as $machine_name => $extension_info) {
|
||||||
|
$type = $extension_info['type'];
|
||||||
|
$folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$type}s/contrib/$machine_name";
|
||||||
|
$file_path = "$folder_path/$machine_name.info.yml";
|
||||||
mkdir($folder_path, 0777, TRUE);
|
mkdir($folder_path, 0777, TRUE);
|
||||||
file_put_contents($file_path, Yaml::encode($extension_info));
|
file_put_contents($file_path, Yaml::encode($extension_info));
|
||||||
$this->enableExtension($extension_type, $extension_machine_name, $extension_name);
|
$file_paths[$machine_name] = $file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable all the extensions.
|
||||||
|
foreach ($all_extensions_info as $machine_name => $extension_info) {
|
||||||
|
$extension_machine_names = [$machine_name];
|
||||||
|
$extension_names = [$extension_info['name']];
|
||||||
|
$this->enableExtensions($extension_info['type'], $extension_machine_names, $extension_names);
|
||||||
|
}
|
||||||
|
|
||||||
// If there are no requirements warnings or errors, we expect to be able to
|
// If there are no requirements warnings or errors, we expect to be able to
|
||||||
// go through the update process uninterrupted.
|
// go through the update process uninterrupted.
|
||||||
$this->assertUpdateWithNoError($test_error_text, $extension_type, $extension_machine_name);
|
$this->drupalGet($this->statusReportUrl);
|
||||||
|
$types = ['module', 'theme'];
|
||||||
|
foreach ($types as $type) {
|
||||||
|
$all = array_merge($core[$type], $contrib[$type]);
|
||||||
|
$this->assertUpdateWithNoErrors($test_error_texts[$type], $type, $all);
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the info.yml and confirm updates are prevented.
|
// Delete the info.yml(s) and confirm updates are prevented.
|
||||||
|
foreach ($file_paths as $file_path) {
|
||||||
unlink($file_path);
|
unlink($file_path);
|
||||||
$this->assertErrorOnUpdate($test_error_text, $extension_type, $extension_machine_name);
|
}
|
||||||
|
$this->drupalGet($this->statusReportUrl);
|
||||||
|
foreach ($types as $type) {
|
||||||
|
$all = array_merge($core[$type], $contrib[$type]);
|
||||||
|
$this->assertErrorOnUpdates($test_error_texts[$type], $type, $all, $test_error_urls[$type]);
|
||||||
|
}
|
||||||
|
|
||||||
// Add the info.yml file back and confirm we are able to go through the
|
// Add the info.yml file(s) back and confirm we are able to go through the
|
||||||
// update process uninterrupted.
|
// update process uninterrupted.
|
||||||
file_put_contents($file_path, Yaml::encode($extension_info));
|
foreach ($all_extensions_info as $machine_name => $extension_info) {
|
||||||
$this->assertUpdateWithNoError($test_error_text, $extension_type, $extension_machine_name);
|
file_put_contents($file_paths[$machine_name], Yaml::encode($extension_info));
|
||||||
|
}
|
||||||
|
$this->drupalGet($this->statusReportUrl);
|
||||||
|
foreach ($types as $type) {
|
||||||
|
$all = array_merge($core[$type], $contrib[$type]);
|
||||||
|
$this->assertUpdateWithNoErrors($test_error_texts[$type], $type, $all);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -427,12 +543,44 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data provider for testMissingExtension().
|
* Data provider for ::testMissingExtension().
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
* Set of testcases to pass to the test method.
|
||||||
*/
|
*/
|
||||||
public function providerMissingExtension() {
|
public function providerMissingExtension(): array {
|
||||||
return [
|
return [
|
||||||
|
'core only' => [
|
||||||
|
'core' => [
|
||||||
|
'module' => ['aggregator'],
|
||||||
|
'theme' => ['seven'],
|
||||||
|
],
|
||||||
|
'contrib' => [
|
||||||
|
'module' => [],
|
||||||
|
'theme' => [],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'contrib only' => [
|
||||||
|
'core' => [
|
||||||
|
'module' => [],
|
||||||
|
'theme' => [],
|
||||||
|
],
|
||||||
|
'contrib' => [
|
||||||
'module' => ['module'],
|
'module' => ['module'],
|
||||||
'theme' => ['theme'],
|
'theme' => ['theme'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'core and contrib' =>
|
||||||
|
[
|
||||||
|
'core' => [
|
||||||
|
'module' => ['aggregator', 'rdf'],
|
||||||
|
'theme' => ['seven'],
|
||||||
|
],
|
||||||
|
'contrib' => [
|
||||||
|
'module' => ['module_a', 'module_b'],
|
||||||
|
'theme' => ['theme_a', 'theme_b'],
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,24 +589,57 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
*
|
*
|
||||||
* @param string $extension_type
|
* @param string $extension_type
|
||||||
* The extension type.
|
* The extension type.
|
||||||
* @param string $extension_machine_name
|
* @param array $extension_machine_names
|
||||||
* The extension machine name.
|
* An array of the extension machine names.
|
||||||
* @param string $extension_name
|
* @param array $extension_names
|
||||||
* The extension name.
|
* An array of extension names.
|
||||||
*/
|
*/
|
||||||
protected function enableExtension($extension_type, $extension_machine_name, $extension_name) {
|
protected function enableExtensions(string $extension_type, array $extension_machine_names, array $extension_names): void {
|
||||||
if ($extension_type === 'module') {
|
if ($extension_type === 'module') {
|
||||||
$edit = [
|
$edit = [];
|
||||||
"modules[$extension_machine_name][enable]" => $extension_machine_name,
|
foreach ($extension_machine_names as $extension_machine_name) {
|
||||||
];
|
$edit["modules[$extension_machine_name][enable]"] = $extension_machine_name;
|
||||||
|
}
|
||||||
$this->drupalGet('admin/modules');
|
$this->drupalGet('admin/modules');
|
||||||
$this->submitForm($edit, 'Install');
|
$this->submitForm($edit, 'Install');
|
||||||
}
|
}
|
||||||
elseif ($extension_type === 'theme') {
|
elseif ($extension_type === 'theme') {
|
||||||
$this->drupalGet('admin/appearance');
|
$this->drupalGet('admin/appearance');
|
||||||
|
foreach ($extension_names as $extension_name) {
|
||||||
$this->click("a[title~=\"$extension_name\"]");
|
$this->click("a[title~=\"$extension_name\"]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables extensions the UI.
|
||||||
|
*
|
||||||
|
* @param array $extension_info
|
||||||
|
* An array of extension information arrays. The array is keyed by 'module'
|
||||||
|
* and 'theme'.
|
||||||
|
*/
|
||||||
|
protected function enableMissingExtensions(array $extension_info): void {
|
||||||
|
$edit = [];
|
||||||
|
foreach ($extension_info as $info) {
|
||||||
|
if ($info['type'] === 'module') {
|
||||||
|
$machine_name = $info['machine_name'];
|
||||||
|
$edit["modules[$machine_name][enable]"] = $machine_name;
|
||||||
|
}
|
||||||
|
if (!empty($edit)) {
|
||||||
|
$this->drupalGet('admin/modules');
|
||||||
|
$this->submitForm($edit, 'Install');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($extension_info['theme'])) {
|
||||||
|
$this->drupalGet('admin/appearance');
|
||||||
|
foreach ($extension_info as $info) {
|
||||||
|
if ($info['type' === 'theme']) {
|
||||||
|
$this->click('a[title~="' . $info['name'] . '"]');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the effect of using the update script on the theme system.
|
* Tests the effect of using the update script on the theme system.
|
||||||
|
@ -779,70 +960,83 @@ class UpdateScriptTest extends BrowserTestBase {
|
||||||
*
|
*
|
||||||
* @param string $extension_type
|
* @param string $extension_type
|
||||||
* The extension type, either 'module' or 'theme'.
|
* The extension type, either 'module' or 'theme'.
|
||||||
* @param string $extension_machine_name
|
* @param array $extension_machine_names
|
||||||
* The extension machine name.
|
* An array of the extension machine names.
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected function assertInstalledExtensionConfig(string $extension_type, string $extension_machine_name): void {
|
protected function assertInstalledExtensionsConfig(string $extension_type, array $extension_machine_names): void {
|
||||||
$extension_config = $this->container->get('config.factory')->getEditable('core.extension');
|
$extension_config = $this->container->get('config.factory')->getEditable('core.extension');
|
||||||
|
foreach ($extension_machine_names as $extension_machine_name) {
|
||||||
$this->assertSame(0, $extension_config->get("$extension_type.$extension_machine_name"));
|
$this->assertSame(0, $extension_config->get("$extension_type.$extension_machine_name"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts a particular error is not shown on update and status report pages.
|
* Asserts particular errors are not shown on update and status report pages.
|
||||||
*
|
*
|
||||||
* @param string $unexpected_error_text
|
* @param array $unexpected_error_texts
|
||||||
* The error text that should not be shown.
|
* An array of the error texts that should not be shown.
|
||||||
* @param string $extension_type
|
* @param string $extension_type
|
||||||
* The extension type, either 'module' or 'theme'.
|
* The extension type, either 'module' or 'theme'.
|
||||||
* @param string $extension_machine_name
|
* @param array $extension_machine_names
|
||||||
* The extension machine name.
|
* An array of the extension machine names.
|
||||||
*
|
*
|
||||||
* @throws \Behat\Mink\Exception\ResponseTextException
|
* @throws \Behat\Mink\Exception\ResponseTextException
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected function assertUpdateWithNoError(string $unexpected_error_text, string $extension_type, string $extension_machine_name): void {
|
protected function assertUpdateWithNoErrors(array $unexpected_error_texts, string $extension_type, array $extension_machine_names): void {
|
||||||
$assert_session = $this->assertSession();
|
$assert_session = $this->assertSession();
|
||||||
$this->drupalGet($this->statusReportUrl);
|
foreach ($unexpected_error_texts as $unexpected_error_text) {
|
||||||
$this->assertSession()->pageTextNotContains($unexpected_error_text);
|
$this->assertSession()->pageTextNotContains($unexpected_error_text);
|
||||||
|
}
|
||||||
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
|
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
|
||||||
|
foreach ($unexpected_error_texts as $unexpected_error_text) {
|
||||||
$this->assertSession()->pageTextNotContains($unexpected_error_text);
|
$this->assertSession()->pageTextNotContains($unexpected_error_text);
|
||||||
|
}
|
||||||
$this->updateRequirementsProblem();
|
$this->updateRequirementsProblem();
|
||||||
$this->clickLink('Continue');
|
$this->clickLink('Continue');
|
||||||
$assert_session->pageTextContains('No pending updates.');
|
$assert_session->pageTextContains('No pending updates.');
|
||||||
$this->assertInstalledExtensionConfig($extension_type, $extension_machine_name);
|
$this->assertInstalledExtensionsConfig($extension_type, $extension_machine_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts an error is shown on the update and status report pages.
|
* Asserts errors are shown on the update and status report pages.
|
||||||
*
|
*
|
||||||
* @param string $expected_error_text
|
* @param array $expected_error_texts
|
||||||
* The expected error text.
|
* The expected error texts.
|
||||||
* @param string $extension_type
|
* @param string $extension_type
|
||||||
* The extension type, either 'module' or 'theme'.
|
* The extension type, either 'module' or 'theme'.
|
||||||
* @param string $extension_machine_name
|
* @param array $extension_machine_names
|
||||||
* The extension machine name.
|
* The extension machine names.
|
||||||
|
* @param array $test_error_urls
|
||||||
|
* The URLs in the error texts.
|
||||||
*
|
*
|
||||||
* @throws \Behat\Mink\Exception\ExpectationException
|
* @throws \Behat\Mink\Exception\ExpectationException
|
||||||
* @throws \Behat\Mink\Exception\ResponseTextException
|
* @throws \Behat\Mink\Exception\ResponseTextException
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected function assertErrorOnUpdate(string $expected_error_text, string $extension_type, string $extension_machine_name): void {
|
protected function assertErrorOnUpdates(array $expected_error_texts, string $extension_type, array $extension_machine_names, array $test_error_urls): void {
|
||||||
$assert_session = $this->assertSession();
|
$assert_session = $this->assertSession();
|
||||||
$this->drupalGet($this->statusReportUrl);
|
foreach ($expected_error_texts as $expected_error_text) {
|
||||||
$this->assertSession()->pageTextContains($expected_error_text);
|
$this->assertSession()->pageTextContains($expected_error_text);
|
||||||
|
}
|
||||||
|
foreach ($test_error_urls as $test_error_url) {
|
||||||
|
$this->assertSession()->linkByHrefExists($test_error_url);
|
||||||
|
}
|
||||||
|
|
||||||
// Reload the update page to ensure the extension with the breaking values
|
// Reload the update page to ensure the extension with the breaking values
|
||||||
// has not been uninstalled or otherwise affected.
|
// has not been uninstalled or otherwise affected.
|
||||||
for ($reload = 0; $reload <= 1; $reload++) {
|
for ($reload = 0; $reload <= 1; $reload++) {
|
||||||
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
|
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
|
||||||
|
foreach ($expected_error_texts as $expected_error_text) {
|
||||||
$this->assertSession()->pageTextContains($expected_error_text);
|
$this->assertSession()->pageTextContains($expected_error_text);
|
||||||
|
}
|
||||||
$assert_session->linkNotExists('Continue');
|
$assert_session->linkNotExists('Continue');
|
||||||
}
|
}
|
||||||
$this->assertInstalledExtensionConfig($extension_type, $extension_machine_name);
|
$this->assertInstalledExtensionsConfig($extension_type, $extension_machine_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue