From dc42bf129b914b3312b8e2bd5f3649043c6ee3a6 Mon Sep 17 00:00:00 2001 From: "Theresa.Grannum" <theresa.grannum@3688861.no-reply.drupal.org> Date: Wed, 30 Mar 2022 18:23:49 +0000 Subject: [PATCH] Issue #3267386 by Theresa.Grannum, tedbow: Automatic Updates Extensions: run readiness checks to determine if an update is possible --- .../automatic_updates_extensions.routing.yml | 1 + .../src/Form/UpdaterForm.php | 65 ++++++++++++--- .../tests/src/Functional/UpdaterFormTest.php | 81 ++++++++++++++++--- 3 files changed, 128 insertions(+), 19 deletions(-) diff --git a/automatic_updates_extensions/automatic_updates_extensions.routing.yml b/automatic_updates_extensions/automatic_updates_extensions.routing.yml index db8214748b..ca8b733fb2 100644 --- a/automatic_updates_extensions/automatic_updates_extensions.routing.yml +++ b/automatic_updates_extensions/automatic_updates_extensions.routing.yml @@ -7,3 +7,4 @@ automatic_updates_extensions.update: _permission: 'administer software updates' options: _admin_route: TRUE + _automatic_updates_readiness_messages: skip diff --git a/automatic_updates_extensions/src/Form/UpdaterForm.php b/automatic_updates_extensions/src/Form/UpdaterForm.php index f7a3a31f85..a032b5773c 100644 --- a/automatic_updates_extensions/src/Form/UpdaterForm.php +++ b/automatic_updates_extensions/src/Form/UpdaterForm.php @@ -2,39 +2,71 @@ namespace Drupal\automatic_updates_extensions\Form; -use Drupal\automatic_updates\Updater; +use Drupal\automatic_updates\Event\ReadinessCheckEvent; +use Drupal\automatic_updates\Validation\ReadinessTrait; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\package_manager\Stage; +use Drupal\system\SystemManager; use Drupal\update\UpdateManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * A form for selecting extension updates. */ class UpdaterForm extends FormBase { + use ReadinessTrait; + /** * The updater service. * * @var \Drupal\automatic_updates\Updater */ - private $updater; + private $stage; + + /** + * The event dispatcher service. + * + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + private $eventDispatcher; /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { - return new static($container->get('automatic_updates.updater')); + // @todo Create a our servcie that extends stage instead of creating a + // generic stage class here. + $stage = new Stage( + $container->get('config.factory'), + $container->get('package_manager.path_locator'), + $container->get('package_manager.beginner'), + $container->get('package_manager.stager'), + $container->get('package_manager.committer'), + $container->get('file_system'), + $container->get('event_dispatcher'), + $container->get('tempstore.shared'), + $container->get('datetime.time') + ); + return new static( + $stage, + $container->get('event_dispatcher'), + ); } /** * Constructs a new UpdaterForm object. * - * @param \Drupal\automatic_updates\Updater $updater - * The extension updater service. + * @param \Drupal\package_manager\Stage $stage + * The stage service. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher + * The extension event dispatcher service. */ - public function __construct(Updater $updater) { - $this->updater = $updater; + public function __construct(Stage $stage, EventDispatcherInterface $event_dispatcher) { + $this->stage = $stage; + $this->eventDispatcher = $event_dispatcher; } /** @@ -87,9 +119,22 @@ class UpdaterForm extends FormBase { '#empty' => $this->t('There are no available updates.'), '#attributes' => ['class' => ['update-recommended']], ]; - if ($update_projects) { + + if ($form_state->getUserInput()) { + $results = []; + } + else { + $event = new ReadinessCheckEvent($this->stage); + $this->eventDispatcher->dispatch($event); + $results = $event->getResults(); + } + $this->displayResults($results, $this->messenger()); + $security_level = $this->getOverallSeverity($results); + + if ($update_projects && $security_level !== SystemManager::REQUIREMENT_ERROR) { $form['actions'] = $this->actions($form_state); } + return $form; } @@ -104,7 +149,7 @@ class UpdaterForm extends FormBase { */ protected function actions(FormStateInterface $form_state): array { $actions = ['#type' => 'actions']; - if (!$this->updater->isAvailable()) { + if (!$this->stage->isAvailable()) { // If the form has been submitted do not display this error message // because ::deleteExistingUpdate() may run on submit. The message will // still be displayed on form build if needed. @@ -130,7 +175,7 @@ class UpdaterForm extends FormBase { * Submit function to delete an existing in-progress update. */ public function deleteExistingUpdate(): void { - $this->updater->destroy(TRUE); + $this->stage->destroy(TRUE); $this->messenger()->addMessage($this->t("Staged update deleted")); } diff --git a/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php b/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php index b4a1ff5055..73b062bc80 100644 --- a/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php +++ b/automatic_updates_extensions/tests/src/Functional/UpdaterFormTest.php @@ -2,7 +2,11 @@ namespace Drupal\Tests\automatic_updates_extensions\Functional; +use Drupal\automatic_updates\Event\ReadinessCheckEvent; +use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1; +use Drupal\package_manager\ValidationResult; use Drupal\Tests\automatic_updates\Functional\AutomaticUpdatesFunctionalTestBase; +use Drupal\Tests\automatic_updates\Traits\ValidationTestTrait; /** * Tests updating using the form. @@ -11,6 +15,8 @@ use Drupal\Tests\automatic_updates\Functional\AutomaticUpdatesFunctionalTestBase */ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { + use ValidationTestTrait; + /** * {@inheritdoc} */ @@ -35,7 +41,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { /** * {@inheritdoc} */ - protected function setUp():void { + protected function setUp(): void { parent::setUp(); $this->setReleaseMetadata(__DIR__ . '/../../../../tests/fixtures/release-history/semver_test.1.1.xml'); } @@ -63,13 +69,26 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { 'hidden' => FALSE, ], ]; - $this->config('update_test.settings')->set('system_info', $system_info)->save(); + $this->config('update_test.settings') + ->set('system_info', $system_info) + ->save(); + } + + /** + * Asserts the table shows the updates. + */ + private function assertTableShowsUpdates() { + $assert = $this->assertSession(); + $assert->elementTextContains('css', '.update-recommended td:nth-of-type(2)', 'Semver Test'); + $assert->elementTextContains('css', '.update-recommended td:nth-of-type(3)', '8.1.0'); + $assert->elementTextContains('css', '.update-recommended td:nth-of-type(4)', '8.1.1'); + $assert->elementsCount('css', '.update-recommended tbody tr', 1); } /** * Tests the form when a module requires an update. */ - public function testHasUpdate():void { + public function testHasUpdate(): void { $assert = $this->assertSession(); $user = $this->createUser(['administer site configuration']); $this->drupalLogin($user); @@ -81,20 +100,18 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $user = $this->createUser(['administer software updates']); $this->drupalLogin($user); $this->drupalGet('/admin/automatic-update-extensions'); + $this->assertTableShowsUpdates(); $assert->pageTextContains('Automatic Updates Form'); - $assert->elementTextContains('css', '.update-recommended td:nth-of-type(2)', 'Semver Test'); - $assert->elementTextContains('css', '.update-recommended td:nth-of-type(3)', '8.1.0'); - $assert->elementTextContains('css', '.update-recommended td:nth-of-type(4)', '8.1.1'); - $assert->elementsCount('css', '.update-recommended tbody tr', 1); $assert->buttonExists('Update'); } /** * Tests the form when there are no available updates. */ - public function testNoUpdate():void { + public function testNoUpdate(): void { $assert = $this->assertSession(); - $user = $this->createUser(['administer site configuration', + $user = $this->createUser([ + 'administer site configuration', 'administer software updates', ]); $this->drupalLogin($user); @@ -105,4 +122,50 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase { $assert->buttonNotExists('Update'); } + /** + * Test the form for errors. + */ + public function testErrors(): void { + $assert = $this->assertSession(); + $user = $this->createUser([ + 'administer site configuration', + 'administer software updates', + ]); + $this->drupalLogin($user); + $this->setProjectInstalledVersion('8.1.0'); + $this->checkForUpdates(); + $this->drupalGet('/admin/automatic-update-extensions'); + $this->assertTableShowsUpdates(); + $message = t("You've not experienced Shakespeare until you have read him in the original Klingon."); + $error = ValidationResult::createError([$message]); + TestSubscriber1::setTestResult([$error], ReadinessCheckEvent::class); + $this->getSession()->reload(); + $assert->pageTextContains($message); + $assert->pageTextContains(static::$errorsExplanation); + $assert->pageTextNotContains(static::$warningsExplanation); + $assert->buttonNotExists('Update'); + } + + /** + * Test the form for warning messages. + */ + public function testWarnings(): void { + $assert = $this->assertSession(); + $user = $this->createUser([ + 'administer site configuration', + 'administer software updates', + ]); + $this->drupalLogin($user); + $this->setProjectInstalledVersion('8.1.0'); + $this->checkForUpdates(); + $message = t("Warning! Updating this module may cause an error."); + $warning = ValidationResult::createWarning([$message]); + TestSubscriber1::setTestResult([$warning], ReadinessCheckEvent::class); + $this->drupalGet('/admin/automatic-update-extensions'); + $this->assertTableShowsUpdates(); + $assert->pageTextContains(static::$warningsExplanation); + $assert->pageTextNotContains(static::$errorsExplanation); + $assert->buttonExists('Update'); + } + } -- GitLab