diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 36dea438509..4a99b7a5d48 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -28,6 +28,30 @@ use Drupal\Core\Url;
use GuzzleHttp\Exception\TransferException;
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().
*/
@@ -989,13 +1013,16 @@ function system_requirements($phase) {
// Display an error if a newly introduced dependency in a module is not resolved.
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 suggestions for resolving this incompatibility 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:
- // - Concatenate two MarkupInterface objects and preserve safeness.
+ // - Concatenate MarkupInterface objects and preserve safeness.
// - Use the item_list theme for the extension list.
$template = [
'#type' => 'inline_template',
- '#template' => '{{ description }}{{ extensions }}',
+ '#template' => '{{ description }}{{ extensions }}{{ additional_description }}
',
'#context' => [
'extensions' => [
'#theme' => 'item_list',
@@ -1004,15 +1031,13 @@ function system_requirements($phase) {
];
$template['#context']['extensions']['#items'] = $extension_names;
$template['#context']['description'] = $description;
+ $template['#context']['additional_description'] = $additional_description;
return [
'title' => $title,
'value' => [
'list' => $template,
'handbook_link' => [
- '#markup' => t(
- 'Review the suggestions for resolving this incompatibility to repair your installation, and then re-run update.php.',
- [':url' => 'https://www.drupal.org/docs/8/update/troubleshooting-database-updates']
- ),
+ '#markup' => $message,
],
],
'severity' => REQUIREMENT_ERROR,
@@ -1134,13 +1159,89 @@ function system_requirements($phase) {
);
}
- // Look for invalid modules.
$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('@module', [
+ ':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 documentation on deprecated modules.',
+ [':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('@theme', [
+ ':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 documentation on deprecated themes.',
+ [':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)) {
$requirements['invalid_module'] = $create_extension_incompatibility_list(
@@ -1160,7 +1261,7 @@ function system_requirements($phase) {
// Look for invalid themes.
$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);
if (!empty($invalid_themes)) {
diff --git a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
index b1ff588ea5a..9a71aba4f2f 100644
--- a/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
+++ b/core/modules/system/tests/src/Functional/UpdateSystem/UpdateScriptTest.php
@@ -224,7 +224,7 @@ class UpdateScriptTest extends BrowserTestBase {
*
* @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'];
$this->drupalLogin(
$this->drupalCreateUser(
@@ -236,8 +236,9 @@ class UpdateScriptTest extends BrowserTestBase {
)
);
- $extension_machine_name = "changing_extension";
- $extension_name = "$extension_machine_name name";
+ $extension_machine_names = ['changing_extension'];
+ $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 "
. $expected_error
@@ -247,25 +248,28 @@ class UpdateScriptTest extends BrowserTestBase {
if ($extension_type === 'theme') {
$base_info['base theme'] = FALSE;
}
- $folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$extension_type}s/$extension_machine_name";
- $file_path = "$folder_path/$extension_machine_name.info.yml";
+ $folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$extension_type}s/$extension_machine_names[0]";
+ $file_path = "$folder_path/$extension_machine_names[0].info.yml";
mkdir($folder_path, 0777, TRUE);
file_put_contents($file_path, Yaml::encode($base_info + $correct_info));
- $this->enableExtension($extension_type, $extension_machine_name, $extension_name);
- $this->assertInstalledExtensionConfig($extension_type, $extension_machine_name);
+ $this->enableExtensions($extension_type, $extension_machine_names, [$extension_name]);
+ $this->assertInstalledExtensionsConfig($extension_type, $extension_machine_names);
// If there are no requirements warnings or errors, we expect to be able to
// 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.
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
// again.
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.
*
- * @param string $extension_type
- * The extension type, either 'module' or 'theme'.
+ * @param array $core
+ * 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
*/
- public function testMissingExtension($extension_type) {
+ public function testMissingExtension(array $core, array $contrib): void {
$this->drupalLogin(
$this->drupalCreateUser(
[
'administer software updates',
'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';
- $test_error_text = "Missing or invalid $extension_type "
- . "The following $extension_type is marked as installed in the core.extension configuration, but it is missing:"
- . $extension_machine_name
- . static::HANDBOOK_MESSAGE;
- $extension_info = [
- 'name' => $extension_name,
- 'type' => $extension_type,
+
+ $all_extensions_info = [];
+ $file_paths = [];
+ $test_error_texts = [];
+ $test_error_urls = [];
+ $extension_base_info = [
+ 'version' => 'VERSION',
'core_version_requirement' => '^8 || ^9 || ^10',
];
- if ($extension_type === 'theme') {
- $extension_info['base theme'] = FALSE;
+
+ // 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;
+ }
+ $all_extensions_info[$extension] = $extension_info;
+ $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);
+ file_put_contents($file_path, Yaml::encode($extension_info));
+ $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);
}
- $folder_path = \Drupal::getContainer()->getParameter('site.path') . "/{$extension_type}s/$extension_machine_name";
- $file_path = "$folder_path/$extension_machine_name.info.yml";
- mkdir($folder_path, 0777, TRUE);
- file_put_contents($file_path, Yaml::encode($extension_info));
- $this->enableExtension($extension_type, $extension_machine_name, $extension_name);
// If there are no requirements warnings or errors, we expect to be able to
// 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.
- unlink($file_path);
- $this->assertErrorOnUpdate($test_error_text, $extension_type, $extension_machine_name);
+ // Delete the info.yml(s) and confirm updates are prevented.
+ foreach ($file_paths as $file_path) {
+ unlink($file_path);
+ }
+ $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.
- file_put_contents($file_path, Yaml::encode($extension_info));
- $this->assertUpdateWithNoError($test_error_text, $extension_type, $extension_machine_name);
+ foreach ($all_extensions_info as $machine_name => $extension_info) {
+ 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 [
- 'module' => ['module'],
- 'theme' => ['theme'],
+ 'core only' => [
+ 'core' => [
+ 'module' => ['aggregator'],
+ 'theme' => ['seven'],
+ ],
+ 'contrib' => [
+ 'module' => [],
+ 'theme' => [],
+ ],
+ ],
+ 'contrib only' => [
+ 'core' => [
+ 'module' => [],
+ 'theme' => [],
+ ],
+ 'contrib' => [
+ 'module' => ['module'],
+ 'theme' => ['theme'],
+ ],
+ ],
+ 'core and contrib' =>
+ [
+ 'core' => [
+ 'module' => ['aggregator', 'rdf'],
+ 'theme' => ['seven'],
+ ],
+ 'contrib' => [
+ 'module' => ['module_a', 'module_b'],
+ 'theme' => ['theme_a', 'theme_b'],
+ ],
+ ],
];
}
@@ -441,22 +589,55 @@ class UpdateScriptTest extends BrowserTestBase {
*
* @param string $extension_type
* The extension type.
- * @param string $extension_machine_name
- * The extension machine name.
- * @param string $extension_name
- * The extension name.
+ * @param array $extension_machine_names
+ * An array of the extension machine names.
+ * @param array $extension_names
+ * 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') {
- $edit = [
- "modules[$extension_machine_name][enable]" => $extension_machine_name,
- ];
+ $edit = [];
+ foreach ($extension_machine_names as $extension_machine_name) {
+ $edit["modules[$extension_machine_name][enable]"] = $extension_machine_name;
+ }
$this->drupalGet('admin/modules');
$this->submitForm($edit, 'Install');
}
elseif ($extension_type === 'theme') {
$this->drupalGet('admin/appearance');
- $this->click("a[title~=\"$extension_name\"]");
+ foreach ($extension_names as $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'] . '"]');
+ }
+ }
}
}
@@ -779,70 +960,83 @@ class UpdateScriptTest extends BrowserTestBase {
*
* @param string $extension_type
* The extension type, either 'module' or 'theme'.
- * @param string $extension_machine_name
- * The extension machine name.
+ * @param array $extension_machine_names
+ * An array of the extension machine names.
*
* @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');
- $this->assertSame(0, $extension_config->get("$extension_type.$extension_machine_name"));
+ foreach ($extension_machine_names as $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
- * The error text that should not be shown.
+ * @param array $unexpected_error_texts
+ * An array of the error texts that should not be shown.
* @param string $extension_type
* The extension type, either 'module' or 'theme'.
- * @param string $extension_machine_name
- * The extension machine name.
+ * @param array $extension_machine_names
+ * An array of the extension machine names.
*
* @throws \Behat\Mink\Exception\ResponseTextException
*
* @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();
- $this->drupalGet($this->statusReportUrl);
- $this->assertSession()->pageTextNotContains($unexpected_error_text);
+ foreach ($unexpected_error_texts as $unexpected_error_text) {
+ $this->assertSession()->pageTextNotContains($unexpected_error_text);
+ }
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
- $this->assertSession()->pageTextNotContains($unexpected_error_text);
+ foreach ($unexpected_error_texts as $unexpected_error_text) {
+ $this->assertSession()->pageTextNotContains($unexpected_error_text);
+ }
$this->updateRequirementsProblem();
$this->clickLink('Continue');
$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
- * The expected error text.
+ * @param array $expected_error_texts
+ * The expected error texts.
* @param string $extension_type
* The extension type, either 'module' or 'theme'.
- * @param string $extension_machine_name
- * The extension machine name.
+ * @param array $extension_machine_names
+ * The extension machine names.
+ * @param array $test_error_urls
+ * The URLs in the error texts.
*
* @throws \Behat\Mink\Exception\ExpectationException
* @throws \Behat\Mink\Exception\ResponseTextException
*
* @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();
- $this->drupalGet($this->statusReportUrl);
- $this->assertSession()->pageTextContains($expected_error_text);
+ foreach ($expected_error_texts as $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
// has not been uninstalled or otherwise affected.
for ($reload = 0; $reload <= 1; $reload++) {
$this->drupalGet($this->updateUrl, ['external' => TRUE]);
- $this->assertSession()->pageTextContains($expected_error_text);
+ foreach ($expected_error_texts as $expected_error_text) {
+ $this->assertSession()->pageTextContains($expected_error_text);
+ }
$assert_session->linkNotExists('Continue');
}
- $this->assertInstalledExtensionConfig($extension_type, $extension_machine_name);
+ $this->assertInstalledExtensionsConfig($extension_type, $extension_machine_names);
}
}