diff --git a/src/Controller/InstallerController.php b/src/Controller/InstallerController.php index f91671d3f9ab24478935ce458d082f214463789f..1a79c296456ce5586c93b8940f8f9044b679ac06 100644 --- a/src/Controller/InstallerController.php +++ b/src/Controller/InstallerController.php @@ -13,7 +13,6 @@ use Drupal\project_browser\ActivatorInterface; use Drupal\project_browser\ComposerInstaller\Installer; use Drupal\project_browser\EnabledSourceHandler; use Drupal\project_browser\InstallState; -use Drupal\project_browser\ProjectBrowser\Project; use Drupal\project_browser_test\Plugin\ProjectBrowserSource\ProjectBrowserTestMock; use Drupal\system\SystemManager; use Psr\Log\LoggerInterface; @@ -31,27 +30,6 @@ final class InstallerController extends ControllerBase { use StatusCheckTrait; - /** - * No require or install in progress for a given module. - * - * @var int - */ - protected const STATUS_IDLE = 0; - - /** - * A staging install in progress for a given module. - * - * @var int - */ - protected const STATUS_REQUIRING_PROJECT = 1; - - /** - * A core install in progress for a given project. - * - * @var int - */ - protected const STATUS_INSTALLING_PROJECT = 2; - /** * The endpoint successfully returned the expected data. * @@ -119,28 +97,6 @@ final class InstallerController extends ControllerBase { } } - /** - * Returns the status of the project in the temp store. - * - * @param \Drupal\project_browser\ProjectBrowser\Project $project - * A project whose status to report. - * - * @return \Symfony\Component\HttpFoundation\JsonResponse - * Information about the project's require/install status. - */ - public function inProgress(Project $project): JsonResponse { - $project_state = $this->installState->getStatus($project); - $return = ['status' => self::STATUS_IDLE]; - - if ($project_state !== NULL) { - $return['status'] = ($project_state === 'requiring' || $project_state === 'applying') - ? self::STATUS_REQUIRING_PROJECT - : self::STATUS_INSTALLING_PROJECT; - $return['phase'] = $project_state; - } - return new JsonResponse($return); - } - /** * Provides a JSON response for a given error. * @@ -364,10 +320,10 @@ final class InstallerController extends ControllerBase { */ public function require(Request $request, string $stage_id): JsonResponse { $package_names = []; - foreach ($request->toArray() as $project) { - $project = $this->enabledSourceHandler->getStoredProject($project); - if ($project->source === 'project_browser_test_mock') { - $source = $this->enabledSourceHandler->getCurrentSources()[$project->source] ?? NULL; + foreach ($request->toArray() as $project_id) { + $project = $this->enabledSourceHandler->getStoredProject($project_id); + if (str_starts_with($project_id, 'project_browser_test_mock/')) { + $source = $this->enabledSourceHandler->getCurrentSources()['project_browser_test_mock'] ?? NULL; if ($source === NULL) { return new JsonResponse(['message' => "Cannot download $project->id from any available source"], 500); } @@ -376,7 +332,7 @@ final class InstallerController extends ControllerBase { return new JsonResponse(['message' => "$project->machineName is not safe to add because its security coverage has been revoked"], 500); } } - $this->installState->setState($project, 'requiring'); + $this->installState->setState($project_id, 'requiring'); $package_names[] = $project->packageName; } try { @@ -400,7 +356,7 @@ final class InstallerController extends ControllerBase { */ public function apply(string $stage_id): JsonResponse { foreach (array_keys($this->installState->toArray()) as $project_id) { - $this->installState->setState($this->enabledSourceHandler->getStoredProject($project_id), 'applying'); + $this->installState->setState($project_id, 'applying'); } try { $this->installer->claim($stage_id)->apply(); @@ -464,12 +420,12 @@ final class InstallerController extends ControllerBase { * Status message. */ public function activate(Request $request): Response { - foreach ($request->toArray() as $project) { - $project = $this->enabledSourceHandler->getStoredProject($project); - $this->installState->setState($project, 'activating'); + foreach ($request->toArray() as $project_id) { + $this->installState->setState($project_id, 'activating'); try { + $project = $this->enabledSourceHandler->getStoredProject($project_id); $response = $this->activator->activate($project); - $this->installState->setState($project, 'installed'); + $this->installState->setState($project_id, 'installed'); } catch (\Throwable $e) { return $this->errorResponse($e, 'project install'); diff --git a/src/Controller/ProjectBrowserEndpointController.php b/src/Controller/ProjectBrowserEndpointController.php index 0c985be2585bb5002179c0a29d8f49402efcd52c..5b1c4299cf284b0ab3af6fb2e1687829fc189806 100644 --- a/src/Controller/ProjectBrowserEndpointController.php +++ b/src/Controller/ProjectBrowserEndpointController.php @@ -3,6 +3,7 @@ namespace Drupal\project_browser\Controller; use Drupal\Core\Controller\ControllerBase; +use Drupal\project_browser\ActivatorInterface; use Drupal\project_browser\EnabledSourceHandler; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -14,14 +15,9 @@ use Symfony\Component\HttpFoundation\Response; */ final class ProjectBrowserEndpointController extends ControllerBase { - /** - * Constructor for endpoint controller. - * - * @param \Drupal\project_browser\EnabledSourceHandler $enabledSource - * The enabled project browser source. - */ public function __construct( private readonly EnabledSourceHandler $enabledSource, + private readonly ActivatorInterface $activator, ) {} /** @@ -30,33 +26,37 @@ final class ProjectBrowserEndpointController extends ControllerBase { public static function create(ContainerInterface $container): static { return new static( $container->get(EnabledSourceHandler::class), + $container->get(ActivatorInterface::class), ); } /** - * Responds to GET requests. - * - * Returns a list of bundles for specified entity. + * Returns a list of projects that match a query. * * @param \Symfony\Component\HttpFoundation\Request $request * The request. * * @return \Symfony\Component\HttpFoundation\JsonResponse - * Typically a project listing. + * A list of projects. + * + * @see \Drupal\project_browser\ProjectBrowser\ProjectsResultsPage */ public function getAllProjects(Request $request): JsonResponse { - $id = $request->query->get('id'); - if ($id) { - assert(is_string($id)); - return new JsonResponse($this->enabledSource->getStoredProject($id)); - } - $current_sources = $this->enabledSource->getCurrentSources(); $query = $this->buildQuery($request); if (!$current_sources || empty($query['source'])) { return new JsonResponse([], Response::HTTP_ACCEPTED); } - return new JsonResponse($this->enabledSource->getProjects($query['source'], $query)); + + // The activator is the source of truth about the status of the project with + // respect to the current site, and it is responsible for generating + // the activation instructions or commands. + $result = $this->enabledSource->getProjects($query['source'], $query); + foreach ($result->list as $project) { + $project->status = $this->activator->getStatus($project); + $project->commands = $this->activator->getInstructions($project); + } + return new JsonResponse($result); } /** diff --git a/src/EnabledSourceHandler.php b/src/EnabledSourceHandler.php index 07933be686eed666c416dc8abfbc470c29975652..1f3ad4119749edc6db24117efbc9d8e3fb91f7f3 100644 --- a/src/EnabledSourceHandler.php +++ b/src/EnabledSourceHandler.php @@ -25,7 +25,6 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter public function __construct( private readonly ConfigFactoryInterface $configFactory, private readonly ProjectBrowserSourceManager $pluginManager, - private readonly ActivatorInterface $activator, private readonly KeyValueFactoryInterface $keyValueFactory, ) {} @@ -105,10 +104,10 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter * @param array $query * (optional) The query to pass to the specified source. * - * @return \Drupal\project_browser\ProjectBrowser\ProjectsResultsPage[] - * The result of the query, keyed by source plugin ID. + * @return \Drupal\project_browser\ProjectBrowser\ProjectsResultsPage + * The result of the query. */ - public function getProjects(string $source_id, array $query = []): array { + public function getProjects(string $source_id, array $query = []): ProjectsResultsPage { // Cache only exact query, down to the page number. $cache_key = 'query:' . md5(Json::encode($query)); @@ -118,35 +117,28 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter // If $results is an array, it's a set of arguments to ProjectsResultsPage, // with a list of project IDs that we expect to be in the data store. if (is_array($results)) { - $results[1] = array_map($this->getStoredProject(...), $results[1]); - $results = new ProjectsResultsPage(...$results); + $results[1] = $storage->getMultiple($results[1]); + $results[1] = array_values($results[1]); + return new ProjectsResultsPage(...$results); } - else { - $results = $this->doQuery($source_id, $query); - - foreach ($results->list as $project) { - // Prefix the local project ID with the source plugin ID, so we can - // look it up unambiguously. - $project->id = $source_id . '/' . $project->id; - - $storage->setIfNotExists($project->id, $project); - // Add activation data to the project. This is volatile, which is why we - // never store it. - $this->getActivationData($project); - } - // If there were no query errors, store the results as a set of arguments - // to ProjectsResultsPage. - if (empty($results->error)) { - $storage->set($cache_key, [ - $results->totalResults, - array_column($results->list, 'id'), - $results->pluginLabel, - $source_id, - $results->error, - ]); - } + $results = $this->doQuery($source_id, $query); + // Cache all the projects individually so they can be loaded by + // ::getStoredProject(). + foreach ($results->list as $project) { + $storage->setIfNotExists($project->id, $project); + } + // If there were no query errors, store the results as a set of arguments + // to ProjectsResultsPage. + if (empty($results->error)) { + $storage->set($cache_key, [ + $results->totalResults, + array_column($results->list, 'id'), + $results->pluginLabel, + $source_id, + $results->error, + ]); } - return [$source_id => $results]; + return $results; } /** @@ -174,35 +166,17 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter * Looks up a previously stored project by its ID. * * @param string $id - * The project ID. See ::getProjects() for where this is set. + * The fully qualified project ID, in the form `SOURCE_ID/LOCAL_ID`. * * @return \Drupal\project_browser\ProjectBrowser\Project - * The project object, with activation status and commands added. + * The project object. * * @throws \RuntimeException * Thrown if the project is not found in the non-volatile data store. */ public function getStoredProject(string $id): Project { - [$source_id] = explode('/', $id, 2); - $project = $this->keyValue($source_id)->get($id) ?? throw new \RuntimeException("Project '$id' was not found in non-volatile storage."); - $this->getActivationData($project); - return $project; - } - - /** - * Adds activation data to a project object. - * - * @param \Drupal\project_browser\ProjectBrowser\Project $project - * The project object. - */ - private function getActivationData(Project $project): void { - // The project's activator is the source of truth about the status of - // the project with respect to the current site. - $project->status = $this->activator->getStatus($project); - // The activator is responsible for generating the instructions. - $project->commands = $this->activator->getInstructions($project); - // Give the front-end the ID of the source plugin that exposed this project. - [$project->source] = explode('/', $project->id, 2); + [$source_id, $local_id] = explode('/', $id, 2); + return $this->keyValue($source_id)->get($local_id) ?? throw new \RuntimeException("Project '$id' was not found in non-volatile storage."); } /** diff --git a/src/InstallState.php b/src/InstallState.php index acc81e53fe96ac03a6b5b50e96cdc53d202328cb..dea603390b47c384f17153d176edb1b8f1d4fb9c 100644 --- a/src/InstallState.php +++ b/src/InstallState.php @@ -43,11 +43,9 @@ final class InstallState { * Example return value: * [ * 'project_id1' => [ - * 'source' => 'source_plugin_id1', * 'status' => 'requiring', * ], * 'project_id2' => [ - * 'source' => 'source_plugin_id2', * 'status' => 'installing', * ], * '__timestamp' => 1732086755, @@ -64,20 +62,20 @@ final class InstallState { /** * Sets project state and initializes a timestamp if not set. * - * @param \Drupal\project_browser\ProjectBrowser\Project $project - * The project object containing the ID and source of the project. + * @param string $project_id + * The fully qualified ID of the project, in the form `SOURCE_ID/LOCAL_ID`. * @param string|null $status * The installation status to set for the project, or NULL if no status. * The status can be any arbitrary string, depending on the context * or use case. */ - public function setState(Project $project, ?string $status): void { + public function setState(string $project_id, ?string $status): void { $this->keyValue->setIfNotExists('__timestamp', $this->time->getRequestTime()); if (is_string($status)) { - $this->keyValue->set($project->id, ['source' => $project->source, 'status' => $status]); + $this->keyValue->set($project_id, ['status' => $status]); } else { - $this->keyValue->delete($project->id); + $this->keyValue->delete($project_id); } } diff --git a/src/ProjectBrowser/Project.php b/src/ProjectBrowser/Project.php index 953f997cb78d4c3cf1a15f0ce681a70f0fadab83..46b6b93461e197a7489d852a6b24f39d20ad1a1d 100644 --- a/src/ProjectBrowser/Project.php +++ b/src/ProjectBrowser/Project.php @@ -19,24 +19,9 @@ class Project implements \JsonSerializable { /** * A persistent ID for this project in non-volatile storage. * - * This property is internal and should be ignored by source plugins. - * * @var string - * - * @see \Drupal\project_browser\EnabledSourceHandler::getProjects() - */ - public string $id; - - /** - * The ID of the source plugin which exposed this project. - * - * This property is internal and should be ignored by source plugins. - * - * @var string - * - * @see \Drupal\project_browser\EnabledSourceHandler::getProjects() */ - public string $source; + public readonly string $id; /** * The status of this project in the current site. @@ -56,7 +41,7 @@ class Project implements \JsonSerializable { * * @see \Drupal\project_browser\ActivatorInterface::getInstructions() */ - public string|Url|null $commands; + public string|Url|null $commands = NULL; /** * The project type (e.g., module, theme, recipe, or something else). @@ -232,7 +217,6 @@ class Project implements \JsonSerializable { 'selector_id' => $this->getSelectorId(), 'commands' => $commands, 'id' => $this->id, - 'source' => $this->source, ]; } diff --git a/src/ProjectBrowser/ProjectsResultsPage.php b/src/ProjectBrowser/ProjectsResultsPage.php index 3ce74890f682865b6e8b6c18cc0f90b6391fed23..7e89a60d5118a6abab3c24f1dd33f4e54278aa4e 100644 --- a/src/ProjectBrowser/ProjectsResultsPage.php +++ b/src/ProjectBrowser/ProjectsResultsPage.php @@ -7,7 +7,7 @@ use Drupal\Component\Assertion\Inspector; /** * One page of search results from a query. */ -class ProjectsResultsPage { +class ProjectsResultsPage implements \JsonSerializable { /** * Constructor for project browser results page. @@ -34,4 +34,20 @@ class ProjectsResultsPage { assert(Inspector::assertAllObjects($list, Project::class)); } + /** + * {@inheritdoc} + */ + public function jsonSerialize(): array { + // Fully qualify the project IDs before sending them to the front end. + $map = function (Project $project): array { + return [ + 'id' => $this->pluginId . '/' . $project->id, + ] + $project->jsonSerialize(); + }; + + return [ + 'list' => array_map($map, $this->list), + ] + get_object_vars($this); + } + } diff --git a/src/Routing/ProjectBrowserRoutes.php b/src/Routing/ProjectBrowserRoutes.php index 2ca6b7859f4d6367ef3ff3bce5b52c11b5018221..c72bd9c4e0a7f7943416d5709be00d4f4239b589 100644 --- a/src/Routing/ProjectBrowserRoutes.php +++ b/src/Routing/ProjectBrowserRoutes.php @@ -143,25 +143,6 @@ final class ProjectBrowserRoutes implements ContainerInjectionInterface { 'methods' => ['POST'], ] ); - $routes['project_browser.module.install_in_progress'] = new Route( - '/admin/modules/project_browser/install_in_progress/{source}/{id}', - [ - '_controller' => InstallerController::class . '::inProgress', - '_title' => 'Install in progress', - 'project' => NULL, - ], - [ - '_permission' => 'administer modules', - '_custom_access' => InstallerController::class . '::access', - ], - [ - 'parameters' => [ - 'project' => [ - 'project_browser.project' => ['source', 'id'], - ], - ], - ] - ); $routes['project_browser.install.unlock'] = new Route( '/admin/modules/project_browser/install/unlock', [ diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js index a5cccf50992e64ca199944035b6570b47ace8a1b..e6485015a05bd9e1a27627069385f94533919d71 100644 Binary files a/sveltejs/public/build/bundle.js and b/sveltejs/public/build/bundle.js differ diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map index 523888a5249060df4794f8a62742063dc487937c..547b743ececee6a2160a1145beeab700720cfe06 100644 Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte index 24f1410bad75a613bb0f2f1452d46d7e017981ed..46ff0b192918f5a45f7281f3dc6d8ac552d90bfd 100644 --- a/sveltejs/src/ProjectBrowser.svelte +++ b/sveltejs/src/ProjectBrowser.svelte @@ -45,8 +45,6 @@ let rowsCount = 0; let data; let rows = []; - let sources = []; - let dataArray = []; const pageIndex = 0; // first row const preferredView = writable('Grid'); @@ -89,16 +87,12 @@ const res = await fetch(url); if (res.ok) { - const messenger = new Drupal.Message(); data = await res.json(); - // A list of the available sources to get project data. - sources = Object.keys(data); - dataArray = Object.values(data); - rows = data[source].list; - rowsCount = data[source].totalResults; + rows = data.list; + rowsCount = data.totalResults; - if (data[source].error && data[source].error.length) { - messenger.add(data[source].error, { type: 'error' }); + if (data.error && data.error.length) { + new Drupal.Message().add(data.error, { type: 'error' }); } } else { rows = []; @@ -227,15 +221,13 @@ <div class="pb-layout__header"> <div class="pb-search-results"> - {#each dataArray as dataValue} - {#if source === dataValue.pluginId} - {Drupal.formatPlural( - rowsCount, - `${numberFormatter.format(1)} Result`, - `${numberFormatter.format(rowsCount)} Results`, - )} - {/if} - {/each} + {#if data && source === data.pluginId} + {Drupal.formatPlural( + rowsCount, + `${numberFormatter.format(1)} Result`, + `${numberFormatter.format(rowsCount)} Results`, + )} + {/if} </div> {#if matches} diff --git a/tests/src/Functional/EnabledSourceHandlerTest.php b/tests/src/Functional/EnabledSourceHandlerTest.php index 6d44e2f63d89a3d045c36029774b6d5bfc9c5dc0..6942a41c71ad2ab497bfe0d4c79a96fceefc307d 100644 --- a/tests/src/Functional/EnabledSourceHandlerTest.php +++ b/tests/src/Functional/EnabledSourceHandlerTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\Tests\project_browser\Functional; +use Drupal\project_browser\ActivationStatus; use Drupal\project_browser\EnabledSourceHandler; use Drupal\project_browser\ProjectBrowser\Project; use Drupal\project_browser_test\Plugin\ProjectBrowserSource\ProjectBrowserTestMock; @@ -41,10 +42,10 @@ class EnabledSourceHandlerTest extends BrowserTestBase { */ public function testExceptionOnGetUnknownProject(): void { $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage("Project 'unseen' was not found in non-volatile storage."); + $this->expectExceptionMessage("Project 'sight/unseen' was not found in non-volatile storage."); $this->container->get(EnabledSourceHandler::class) - ->getStoredProject('unseen'); + ->getStoredProject('sight/unseen'); } /** @@ -53,43 +54,20 @@ class EnabledSourceHandlerTest extends BrowserTestBase { public function testGetStoredProject(): void { $handler = $this->container->get(EnabledSourceHandler::class); - $projects = $handler->getProjects('project_browser_test_mock'); - assert(!empty($projects)); - $list = reset($projects)->list; - $this->assertNotEmpty($list); - $project = reset($list); + $project = $handler->getProjects('project_browser_test_mock')->list[0]; - $project_again = $handler->getStoredProject($project->id); + $project_again = $handler->getStoredProject('project_browser_test_mock/' . $project->id); $this->assertNotSame($project, $project_again); + // Project::$status is a typed property and therefore must be initialized + // before it is accessed by jsonSerialize(). + $project->status = ActivationStatus::Active; + $project_again->status = ActivationStatus::Active; $this->assertSame($project->jsonSerialize(), $project_again->jsonSerialize()); // The activation status and commands should be set. $this->assertTrue(self::hasActivationData($project_again)); } - /** - * Tests that projects are not stored with any activation data. - */ - public function testProjectsAreStoredWithoutActivationData(): void { - // Projects returned from getProjects() should have their activation status - // and commands set. - $projects = $this->container->get(EnabledSourceHandler::class) - ->getProjects('project_browser_test_mock'); - assert(!empty($projects)); - $list = reset($projects)->list; - $this->assertNotEmpty($list); - $project = reset($list); - $this->assertTrue(self::hasActivationData($project)); - - // But if we pull the project directly from the data store, the `status` and - // `commands` properties should be uninitialized. - $project = $this->container->get('keyvalue') - ->get('project_browser:project_browser_test_mock') - ->get($project->id); - $this->assertInstanceOf(Project::class, $project); - $this->assertFalse(self::hasActivationData($project)); - } - /** * Tests that query results are not stored if there was an error. */ @@ -133,10 +111,9 @@ class EnabledSourceHandlerTest extends BrowserTestBase { * Tests that the install profile is ignored by the drupal_core source. */ public function testProfileNotListedByCoreSource(): void { - $projects = $this->container->get(EnabledSourceHandler::class)->getProjects('drupal_core'); - $this->assertNotEmpty($projects); + $result = $this->container->get(EnabledSourceHandler::class)->getProjects('drupal_core'); // Assert that the current install profile is not returned by the source. - $this->assertNotContains($this->profile, array_column(reset($projects)->list, 'machineName')); + $this->assertNotContains($this->profile, array_column($result->list, 'machineName')); } } diff --git a/tests/src/Functional/InstallerControllerTest.php b/tests/src/Functional/InstallerControllerTest.php index 8ed41aa590d7faa926f3444b14d730bc971c4434..6d370ee5b66896d51a5156d61f9c7abf97efc6bf 100644 --- a/tests/src/Functional/InstallerControllerTest.php +++ b/tests/src/Functional/InstallerControllerTest.php @@ -243,7 +243,7 @@ class InstallerControllerTest extends BrowserTestBase { ]); $expected_output = sprintf('{"phase":"create","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'requiring'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'requiring'); } /** @@ -255,7 +255,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet("/admin/modules/project_browser/install-apply/$this->stageId"); $expected_output = sprintf('{"phase":"apply","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'applying'); } /** @@ -267,7 +267,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet("/admin/modules/project_browser/install-post_apply/$this->stageId"); $expected_output = sprintf('{"phase":"post apply","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'applying'); } /** @@ -279,7 +279,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet("/admin/modules/project_browser/install-destroy/$this->stageId"); $expected_output = sprintf('{"phase":"destroy","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'applying'); } /** @@ -469,7 +469,6 @@ class InstallerControllerTest extends BrowserTestBase { $assert_unlock_response($response, "The process for adding the project that was locked less than 1 minutes ago might still be in progress. Consider waiting a few more minutes before using [+unlock link]."); $expected = [ 'project_browser_test_mock/awesome_module' => [ - 'source' => 'project_browser_test_mock', 'status' => 'requiring', ], ]; @@ -593,21 +592,14 @@ class InstallerControllerTest extends BrowserTestBase { * * @param string $project_id * The ID of the project being enabled. - * @param string $source - * The project source. - * @param string $status + * @param string|null $status * The install state. */ - protected function assertInstallInProgress(string $project_id, string $source, ?string $status = NULL): void { + protected function assertInstallInProgress(string $project_id, ?string $status = NULL): void { $expect_install[$project_id] = [ - 'source' => $source, 'status' => $status, ]; $this->assertSame($expect_install, $this->getInstallState()->toArray()); - $this->drupalGet("/admin/modules/project_browser/install_in_progress/$project_id"); - $this->assertSame(sprintf('{"status":1,"phase":"%s"}', $status), $this->getSession()->getPage()->getContent()); - $this->drupalGet('/admin/modules/project_browser/install_in_progress/project_browser_test_mock/metatag'); - $this->assertSame('{"status":0}', $this->getSession()->getPage()->getContent()); } /** diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index b3dadd9ced80a7386e50a48ff745b1f7e78c7fea..daa07e2ade8806943a2e86e5f5a7a7094cb71ca2 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -349,8 +349,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { */ private function chooseProjectToInstall(array $except_these_machine_names = [], string $source_id = 'project_browser_test_mock'): string { $handler = $this->container->get(EnabledSourceHandler::class); - $projects = $handler->getProjects($source_id); - $results = $projects[$source_id]; + $results = $handler->getProjects($source_id); foreach ($results->list as $project) { if (in_array($project->machineName, $except_these_machine_names, TRUE)) {