From b76148eddaa1e7c4fce246d6e1b18cbef36b6fd5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 13:27:38 -0500
Subject: [PATCH 1/8] Remove some uses of random_data

---
 .../ProjectBrowserTestMock.php                |  5 +-
 .../ProjectBrowserPluginTest.php              | 48 +++++--------------
 2 files changed, 16 insertions(+), 37 deletions(-)

diff --git a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php
index 7e167c9dd..f4f12a6cd 100644
--- a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php
+++ b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php
@@ -321,8 +321,9 @@ final class ProjectBrowserTestMock extends ProjectBrowserSourceBase {
         $returned_list[] = new Project(
           logo: Url::fromUri($avatar_url),
           // Mock projects are filtered and made sure that they are compatible
-          // before we even put them in the database.
-          isCompatible: TRUE,
+          // before we even put them in the database, but allow that to be
+          // overridden for testing.
+          isCompatible: $this->state->get('project_browser_test_mock isCompatible', TRUE),
           isMaintained: in_array($project_data['maintenance_status'], self::MAINTAINED_VALUES),
           isCovered: in_array($project_data['field_security_advisory_coverage'], self::COVERED_VALUES),
           projectUsageTotal: array_reduce($project_data['project_data']['project_usage'] ?? [], fn($total, $project_usage): int => $total + $project_usage) ?: 0,
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
index 4e0221e44..0f781faac 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
@@ -28,7 +28,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
    */
   protected static $modules = [
     'project_browser',
-    'project_browser_devel',
+    'project_browser_test',
   ];
 
   /**
@@ -45,34 +45,9 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
       'administer modules',
       'administer site configuration',
     ]));
-    // Update configuration, enable only random_data source.
-    $this->config('project_browser.admin_settings')->set('enabled_sources', ['random_data'])->save(TRUE);
-  }
-
-  /**
-   * Tests the Random Data plugin.
-   */
-  public function testRandomDataPlugin(): void {
-    $assert_session = $this->assertSession();
-
-    $this->getSession()->resizeWindow(1280, 960);
-    $this->drupalGet('admin/modules/browse/random_data');
-    $this->svelteInitHelper('css', '#project-browser .pb-project--grid');
-    $this->assertEquals('Grid', $this->getElementText('#project-browser .pb-display__button[value="Grid"]'));
-    $this->assertElementIsVisible('css', '#project-browser .pb-project');
-    $this->assertPageHasText('Results');
-    $assert_session->pageTextNotContains('No modules found');
-  }
-
-  /**
-   * Tests the available categories.
-   */
-  public function testCategories(): void {
-    $assert_session = $this->assertSession();
-
-    $this->drupalGet('admin/modules/browse/random_data');
-    $this->svelteInitHelper('css', '.pb-filter__checkbox');
-    $assert_session->elementsCount('css', '.pb-filter__checkbox', 20);
+    $this->config('project_browser.admin_settings')
+      ->set('enabled_sources', ['random_data', 'drupal_core', 'project_browser_test_mock'])
+      ->save();
   }
 
   /**
@@ -85,7 +60,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
     $page = $this->getSession()->getPage();
     $assert_session = $this->assertSession();
 
-    $this->drupalGet('admin/modules/browse/random_data');
+    $this->drupalGet('admin/modules/browse/drupal_core');
     // Immediately clear filters so there are enough visible to enable paging.
     $this->svelteInitHelper('test', 'Clear Filters');
     $this->svelteInitHelper('css', '.pager__item--next');
@@ -100,7 +75,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
    * Tests advanced filtering.
    */
   public function testAdvancedFiltering(): void {
-    $this->drupalGet('admin/modules/browse/random_data');
+    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
     $this->svelteInitHelper('text', 'Results');
 
     $this->assertEquals('Show projects covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED));
@@ -132,7 +107,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
   public function testBrokenImages(): void {
     $assert_session = $this->assertSession();
 
-    $this->drupalGet('admin/modules/browse/random_data');
+    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
     $this->svelteInitHelper('css', 'img[src$="images/puzzle-piece-placeholder.svg"]');
 
     // RandomData always give an image URL. Sometimes it is a fake URL on
@@ -145,7 +120,10 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
    * Tests the not-compatible flag.
    */
   public function testNotCompatibleText(): void {
-    $this->drupalGet('admin/modules/browse/random_data');
+    \Drupal::state()
+      ->set('project_browser_test_mock isCompatible', FALSE);
+
+    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
     $this->svelteInitHelper('css', '.project_status-indicator');
     $this->assertEquals($this->getElementText('.project_status-indicator .visually-hidden') . ' Not compatible', $this->getElementText('.project_status-indicator'));
   }
@@ -153,8 +131,8 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
   /**
    * Tests the detail page.
    */
-  public function testDetailPageRandomDataPlugin(): void {
-    $this->drupalGet('admin/modules/browse/random_data');
+  public function testDetailPage(): void {
+    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
     $this->assertElementIsVisible('css', '#project-browser .pb-project');
     $this->assertPageHasText('Results');
 
-- 
GitLab


From 72b2255231743e87a89d9522eeba70c2e23276a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 13:43:26 -0500
Subject: [PATCH 2/8] Remove project_browser_devel and all remaining mentions
 of random_data

---
 .../project_browser_devel.info.yml            |   6 -
 .../project_browser_devel.install             |  40 ----
 .../ProjectBrowserSource/RandomDataPlugin.php | 225 ------------------
 tests/src/Functional/RoutingTest.php          |  38 ++-
 .../ProjectBrowserInstallerUiTest.php         |   1 -
 .../ProjectBrowserPluginTest.php              |   2 +-
 .../ProjectBrowserUiTest.php                  | 149 +-----------
 .../ProjectBrowserUiTestJsonApi.php           |  20 +-
 8 files changed, 33 insertions(+), 448 deletions(-)
 delete mode 100644 modules/project_browser_devel/project_browser_devel.info.yml
 delete mode 100644 modules/project_browser_devel/project_browser_devel.install
 delete mode 100644 modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php

diff --git a/modules/project_browser_devel/project_browser_devel.info.yml b/modules/project_browser_devel/project_browser_devel.info.yml
deleted file mode 100644
index 9c21176e0..000000000
--- a/modules/project_browser_devel/project_browser_devel.info.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-name: Project Browser Devel
-type: module
-description: Provides a Project Browser source plugin that generates random data.
-core_version_requirement: ^10 || ^11
-dependencies:
-  - project_browser:project_browser
diff --git a/modules/project_browser_devel/project_browser_devel.install b/modules/project_browser_devel/project_browser_devel.install
deleted file mode 100644
index ffc19ab23..000000000
--- a/modules/project_browser_devel/project_browser_devel.install
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Install and uninstall functions for the project_browser_devel module.
- */
-
-use Drupal\project_browser\Plugin\ProjectBrowserSourceManager;
-
-/**
- * Implements hook_install().
- */
-function project_browser_devel_install(): void {
-  // Set the new random data generator as plugin and keep the current one.
-  $configFactory = \Drupal::configFactory();
-  $current_source_plugin = $configFactory->getEditable('project_browser.admin_settings')
-    ->get('enabled_sources');
-  $current_source_plugin[] = 'random_data';
-  $configFactory->getEditable('project_browser.admin_settings')
-    ->set('enabled_sources', $current_source_plugin)
-    ->save(TRUE);
-
-  // Invalidate the cache to reflect the configuration changes.
-  \Drupal::service(ProjectBrowserSourceManager::class)->clearCachedDefinitions();
-}
-
-/**
- * Implements hook_uninstall().
- */
-function project_browser_devel_uninstall(): void {
-  // Set the previous plugin.
-  $admin_settings = \Drupal::configFactory()->getEditable('project_browser.admin_settings');
-  $enabled_sources = $admin_settings->get('enabled_sources');
-  if (($key = array_search('random_data', $enabled_sources)) !== FALSE) {
-    unset($enabled_sources[$key]);
-    $admin_settings
-      ->set('enabled_sources', array_values($enabled_sources) ?: ['drupalorg_jsonapi'])
-      ->save(TRUE);
-  }
-}
diff --git a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
deleted file mode 100644
index 3a29c7fb5..000000000
--- a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
+++ /dev/null
@@ -1,225 +0,0 @@
-<?php
-
-namespace Drupal\project_browser_devel\Plugin\ProjectBrowserSource;
-
-use Drupal\Component\Utility\Random;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\StringTranslation\TranslatableMarkup;
-use Drupal\Core\Url;
-use Drupal\project_browser\Attribute\ProjectBrowserSource;
-use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
-use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter;
-use Drupal\project_browser\ProjectBrowser\Project;
-use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
-/**
- * Random data plugin. Used mostly for testing.
- */
-#[ProjectBrowserSource(
-  id: 'random_data',
-  label: new TranslatableMarkup('Random data'),
-  description: new TranslatableMarkup('Gets random project and filters information'),
-  local_task: [],
-)]
-final class RandomDataPlugin extends ProjectBrowserSourceBase {
-
-  /**
-   * Utility to create random data.
-   *
-   * @var \Drupal\Component\Utility\Random
-   */
-  protected Random $randomGenerator;
-
-  /**
-   * ProjectBrowser cache bin.
-   *
-   * @var \Drupal\Core\Cache\CacheBackendInterface
-   */
-  protected CacheBackendInterface $cacheBin;
-
-  /**
-   * Constructs a MockDrupalDotOrg object.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin ID for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_bin
-   *   The cache bin.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, CacheBackendInterface $cache_bin) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-    $this->randomGenerator = new Random();
-    $this->cacheBin = $cache_bin;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('cache.project_browser'),
-    );
-  }
-
-  /**
-   * Generate random IDs and labels.
-   *
-   * @param int $array_length
-   *   Length of the array to generate.
-   *
-   * @return array
-   *   Array of random IDs and names.
-   */
-  protected function getRandomIdsAndNames(int $array_length = 4): array {
-    $data = [];
-    for ($i = 0; $i < $array_length; $i++) {
-      $data[] = [
-        'id' => uniqid(),
-        'name' => ucwords($this->randomGenerator->word(rand(6, 10))),
-      ];
-    }
-
-    return $data;
-  }
-
-  /**
-   * Returns a random date.
-   *
-   * @return int
-   *   Random timestamp.
-   */
-  protected function getRandomDate(): int {
-    return rand(strtotime('2 years ago'), strtotime('today'));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getCategories(): array {
-    $stored_categories = $this->cacheBin->get('RandomData:categories');
-    if ($stored_categories) {
-      $categories = $stored_categories->data;
-    }
-    else {
-      $categories = $this->getRandomIdsAndNames(20);
-      $this->cacheBin->set('RandomData:categories', $categories);
-    }
-    return $categories;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFilterDefinitions(): array {
-    $filters = parent::getFilterDefinitions();
-
-    $filters['security_advisory_coverage'] = new BooleanFilter(
-      TRUE,
-      $this->t('Show projects covered by a security policy'),
-      $this->t('Show all'),
-      $this->t('Security advisory coverage'),
-      NULL,
-    );
-    $filters['maintenance_status'] = new BooleanFilter(
-      TRUE,
-      $this->t('Show actively maintained projects'),
-      $this->t('Show all'),
-      $this->t('Maintenance status'),
-      NULL,
-    );
-    $filters['development_status'] = new BooleanFilter(
-      FALSE,
-      $this->t('Show projects under active development'),
-      $this->t('Show all'),
-      $this->t('Development status'),
-      NULL,
-    );
-
-    return $filters;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getProjects(array $query = []) : ProjectsResultsPage {
-    $projects = $this->getProjectData();
-
-    // Filter by project machine name.
-    if (!empty($query['machine_name'])) {
-      $projects = array_filter($projects, fn(Project $project): bool => $project->machineName === $query['machine_name']);
-    }
-
-    // Filter by categories.
-    if (!empty($query['categories'])) {
-      $projects = array_filter($projects, fn(Project $project): bool => empty(array_intersect(array_column($project->categories, 'id'), explode(',', $query['categories']))));
-    }
-
-    // Filter by search text.
-    if (!empty($query['search'])) {
-      $projects = array_filter($projects, fn(Project $project): bool => stripos($project->title, $query['search']) !== FALSE);
-    }
-
-    return $this->createResultsPage($projects);
-  }
-
-  /**
-   * Gets the project data from cache if available, or builds it if not.
-   *
-   * @return \Drupal\project_browser\ProjectBrowser\Project[]
-   *   An array of projects.
-   */
-  private function getProjectData(): array {
-    $stored_projects = $this->cacheBin->get('RandomData:projects');
-    if ($stored_projects) {
-      return $stored_projects->data;
-    }
-
-    $projects = [];
-    $number_of_projects = rand(16, 36);
-    $categories = $this->getCategories();
-    $broken_image = 'https://image.not/found' . uniqid() . '.jpg';
-    $good_image = 'https://picsum.photos/600/400';
-    for ($i = 0; $i < $number_of_projects; $i++) {
-      $machine_name = strtolower($this->randomGenerator->word(10));
-      $project_images = [];
-      if ($i !== 0) {
-        $project_images[] = [
-          'file' => Url::fromUri(str_replace('4', '5', $good_image)),
-          'alt' => $machine_name . ' something',
-        ];
-        $project_images[] = [
-          'file' => Url::fromUri(str_replace('4', '6', $good_image)),
-          'alt' => $machine_name . ' another thing',
-        ];
-      }
-
-      $projects[] = new Project(
-        logo: Url::fromUri($i % 3 ? $good_image : $broken_image),
-        isCompatible: (bool) ($i / 4),
-        isMaintained: (bool) rand(0, 1),
-        isCovered: (bool) rand(0, 1),
-        projectUsageTotal: rand(0, 100000),
-        machineName: $machine_name,
-        body: [
-          'summary' => $this->randomGenerator->paragraphs(1),
-          'value' => $this->randomGenerator->paragraphs(5),
-        ],
-        title: ucwords($machine_name),
-        packageName: 'random/' . $machine_name,
-        categories: [$categories[array_rand($categories)]],
-        images: $project_images,
-        id: $machine_name,
-      );
-    }
-    $this->cacheBin->set('RandomData:projects', $projects);
-    return $projects;
-  }
-
-}
diff --git a/tests/src/Functional/RoutingTest.php b/tests/src/Functional/RoutingTest.php
index d7ac5dc20..09e085961 100644
--- a/tests/src/Functional/RoutingTest.php
+++ b/tests/src/Functional/RoutingTest.php
@@ -43,37 +43,35 @@ class RoutingTest extends BrowserTestBase {
   public function testSources(): void {
     $assert_session = $this->assertSession();
 
-    // Install module for extra source plugin.
-    $this->container->get('module_installer')->install(['project_browser_devel']);
-    $this->rebuildContainer();
+    $url = Url::fromRoute('project_browser.browse', [
+      'source' => 'drupal_core',
+    ]);
+    $this->drupalGet($url);
+    $assert_session->statusCodeEquals(404);
+
+    // Enable another source plugin and ensure that the enabled source handler
+    // is aware of it.
+    $this->config('project_browser.admin_settings')
+      ->set('enabled_sources', [
+        'project_browser_test_mock',
+        'drupal_core',
+      ])
+      ->save();
 
     $enabled_source_ids = array_keys($this->container->get(EnabledSourceHandler::class)->getCurrentSources());
     sort($enabled_source_ids);
     $expected = [
-      'project_browser_test_mock' => 200,
-      'random_data' => 200,
+      'drupal_core',
+      'project_browser_test_mock',
     ];
-    $this->assertSame(array_keys($expected), $enabled_source_ids);
-
-    foreach ($enabled_source_ids as $plugin_id) {
-      $url = Url::fromRoute('project_browser.browse', [
-        'source' => $plugin_id,
-      ]);
-      $this->drupalGet($url);
-      $assert_session->statusCodeEquals($expected[$plugin_id]);
-    }
-
-    // Uninstall extra source plugin.
-    $this->container->get('module_installer')->uninstall(['project_browser_devel']);
-    $this->rebuildContainer();
+    $this->assertSame($expected, $enabled_source_ids);
 
-    $expected['random_data'] = 404;
     foreach ($enabled_source_ids as $plugin_id) {
       $url = Url::fromRoute('project_browser.browse', [
         'source' => $plugin_id,
       ]);
       $this->drupalGet($url);
-      $assert_session->statusCodeEquals($expected[$plugin_id]);
+      $assert_session->statusCodeEquals(200);
     }
   }
 
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
index cf5fc99e2..a0c09d22e 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php
@@ -303,7 +303,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase {
    */
   public function testPluginSpecificInstallList(): void {
     $assert_session = $this->assertSession();
-    $this->container->get('module_installer')->install(['project_browser_devel'], TRUE);
     $this->drupalGet('project-browser/project_browser_test_mock');
 
     $this->waitForProject('Cream cheese on a bagel')
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
index 0f781faac..23164c107 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
@@ -46,7 +46,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
       'administer site configuration',
     ]));
     $this->config('project_browser.admin_settings')
-      ->set('enabled_sources', ['random_data', 'drupal_core', 'project_browser_test_mock'])
+      ->set('enabled_sources', ['drupal_core', 'project_browser_test_mock'])
       ->save();
   }
 
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index 28180b0ab..bdb991fe0 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -726,120 +726,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->assertPageHasText('10 Results');
   }
 
-  /**
-   * Tests multiple source plugins at once.
-   */
-  public function testMultiplePlugins(): void {
-    $this->markTestSkipped('This test is skipped because it needs to be rewritten now that in-app tabbing and persistence is removed.');
-    // @phpstan-ignore deadCode.unreachable
-    $page = $this->getSession()->getPage();
-    $assert_session = $this->assertSession();
-    // Enable module for extra source plugin.
-    $this->container->get('module_installer')->install(['project_browser_devel']);
-    // Test categories with multiple plugin enabled.
-    $this->drupalGet('admin/modules/browse');
-    $this->svelteInitHelper('css', '.pb-filter__checkbox');
-    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown__items > div input', 19);
-
-    $this->svelteInitHelper('css', '#project-browser .pb-project');
-    // Count tabs.
-    $tab_count = $page->findAll('css', '.pb-tabs__link');
-    $this->assertCount(2, $tab_count);
-    // Get result count for first tab.
-    $this->assertEquals('10 Results', $this->getElementText('.pb-search-results'));
-    // Get second tab text.
-    $second_tab_text = $assert_session->buttonExists('random_data')->getText();
-
-    // Apply filters in project_browser_test_mock(first tab).
-    $assert_session->waitForElement('css', '.views-exposed-form__item input[type="checkbox"]');
-
-    $this->pressWithWait('Clear filters', '25 Results');
-    // Removing/applying filters will not change second tab results.
-    $this->assertSame($second_tab_text, $assert_session->buttonExists('random_data')->getText());
-
-    // Open category drop-down.
-    $this->clickWithWait('.pb-filter__multi-dropdown', 'E-commerce', TRUE);
-
-    // Click 'E-commerce' checkbox.
-    $this->clickWithWait('#104');
-
-    // Click 'Media' checkbox. It will change results on first tab.
-    $this->clickWithWait('#67', '20 Results');
-    // Applying filters will not change second tab results.
-    $this->assertSame($second_tab_text, $assert_session->buttonExists('random_data')->getText());
-
-    // Use blur event to close drop-down so Clear is visible.
-    $assert_session->elementExists('css', '.pb-filter__multi-dropdown')->blur();
-    $this->assertSame('2 categories selected', $page->find('css', '.pb-filter__multi-dropdown__label')->getText());
-
-    // Click other tab.
-    $this->pressWithWait('random_data');
-    $this->svelteInitHelper('css', '.pb-filter__checkbox');
-    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown__items > div input', 20);
-    $this->assertElementIsVisible('css', '#project-browser .pb-project');
-    $this->assertNotEquals('9 Results Sorted by Active installs', $this->getElementText('.pb-search-results'));
-    // Switching tab will not change result count.
-    $this->assertEquals($second_tab_text . ' (active tab)', $page->findButton('random_data')->getText());
-
-    // Open category drop-down again by pressing space.
-    $assert_session->elementExists('css', '.pb-filter__multi-dropdown')->keyDown(' ');
-
-    // Apply the second module category filter.
-    $second_category_filter_selector = '.pb-filter__multi-dropdown__items > div:nth-child(2) input';
-    $this->clickWithWait("$second_category_filter_selector");
-
-    // Assert that the filters persist.
-    $second_label_selector = '.pb-filter__multi-dropdown__items > div:nth-child(2) label';
-    $second_label_text = $page->find('css', $second_label_selector)->getText();
-    $assert_session->fieldExists($second_label_text)->check();
-
-    // Applying filter on second tab will change result count.
-    $this->assertNotSame($second_tab_text, $assert_session->buttonExists('random_data')->getText());
-    $this->assertSame('1 category selected', $page->find('css', '.pb-filter__multi-dropdown__label')->getText());
-    // Save the filter applied in second tab.
-    [$applied_filter] = $this->getSelectedCategories();
-    // Save the number of results.
-    $results_before = count($page->findAll('css', '#project-browser .pb-project.list'));
-
-    // Switch back to first tab.
-    $this->pressWithWait('project_browser_test_mock');
-    $this->assertSame('2 categories selected', $page->find('css', '.pb-filter__multi-dropdown__label')->getText());
-    $this->assertSame(['E-commerce', 'Media'], $this->getSelectedCategories());
-
-    // Again switch to second tab.
-    $this->pressWithWait('random_data');
-    // Assert that the filters persist.
-    $this->assertSame($applied_filter, $this->getSelectedCategories()[0]);
-    $this->assertSame('1 category selected', $page->find('css', '.pb-filter__multi-dropdown__label')->getText());
-
-    // Assert that the number of results is the same.
-    $results_after = count($page->findAll('css', '#project-browser .pb-project.list'));
-    $this->assertEquals($results_before, $results_after);
-
-    // Switch back to first tab.
-    $this->pressWithWait('project_browser_test_mock');
-    // Filter by search text.
-    $this->inputSearchField('Number', TRUE);
-    $this->assertElementIsVisible('css', ".search__search-submit")->click();
-    $this->assertPageHasText('2 Results');
-    $this->assertProjectsVisible([
-      '9 Starts With a Higher Number',
-      '1 Starts With a Number',
-    ]);
-    // Again switch to second tab.
-    $this->pressWithWait('random_data');
-    $this->pressWithWait('Clear filters');
-    // Switch back to first tab.
-    $this->pressWithWait('project_browser_test_mock');
-    $this->svelteInitHelper('css', '#project-browser .pb-project');
-    // Assert that the filters persist.
-    $this->assertPageHasText('2 Results');
-    $this->assertProjectsVisible([
-      '9 Starts With a Higher Number',
-      '1 Starts With a Number',
-    ]);
-  }
-
   /**
    * Tests the view mode toggle keeps its state.
    */
@@ -875,10 +761,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
   public function testTabledrag(): void {
     $page = $this->getSession()->getPage();
     $assert_session = $this->assertSession();
-    $this->container->get('module_installer')->install([
-      'block',
-      'project_browser_devel',
-    ]);
+    $this->container->get('module_installer')->install(['block']);
     $this->drupalPlaceBlock('local_tasks_block');
 
     $this->drupalGet('admin/modules/browse/project_browser_test_mock');
@@ -891,16 +774,16 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     // Re-order plugins.
     $this->drupalGet('admin/config/development/project_browser');
     $first_plugin = $page->find('css', '#source--project_browser_test_mock');
-    $second_plugin = $page->find('css', '#source--random_data');
+    $second_plugin = $page->find('css', '#source--drupal_core');
     $this->assertNotNull($second_plugin);
     $first_plugin?->find('css', '.handle')?->dragTo($second_plugin);
     $this->assertNotNull($first_plugin);
     $this->assertTableRowWasDragged($first_plugin);
     $this->submitForm([], 'Save');
 
-    // Verify that Random data is first tab.
+    // Verify that Core modules is first tab.
     $this->drupalGet('admin/modules/browse/project_browser_test_mock');
-    $this->assertSame('Random data', $local_tasks[0]->getText());
+    $this->assertSame('Core modules', $local_tasks[0]->getText());
 
     // Disable the mock plugin.
     $this->drupalGet('admin/config/development/project_browser');
@@ -913,10 +796,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->submitForm([], 'Save');
     $assert_session->pageTextContains('The configuration options have been saved.');
 
-    // Verify that only Random data plugin is enabled.
-    $this->drupalGet('admin/modules/browse/random_data');
-    $this->svelteInitHelper('css', '.pb-filter__checkbox');
-    $assert_session->elementsCount('css', '.pb-filter__checkbox', 20);
+    // Verify that only core modules plugin is enabled.
+    $this->drupalGet('admin/modules/browse/drupal_core');
+    $this->assertElementIsVisible('css', '.pb-filter__checkbox');
 
     $this->config('project_browser.admin_settings')->set('enabled_sources', ['project_browser_test_mock'])->save(TRUE);
     $this->drupalGet('admin/config/development/project_browser');
@@ -1131,23 +1013,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->assertNull($last_install_count_container, 'Last project contains the install count container, but it should not.');
   }
 
-  /**
-   * Tests that each source plugin has its own dedicated route.
-   */
-  public function testSourcePluginRoutes(): void {
-    // Enable module for extra source plugin.
-    $this->container->get('module_installer')->install(['project_browser_devel']);
-    $this->rebuildContainer();
-
-    $current_sources = $this->container->get(EnabledSourceHandler::class)->getCurrentSources();
-    $this->assertCount(2, $current_sources);
-
-    foreach (array_keys($current_sources) as $plugin_id) {
-      $this->drupalGet("/admin/modules/browse/{$plugin_id}");
-      $this->assertElementIsVisible('css', '#project-browser .pb-project.pb-project--list');
-    }
-  }
-
   /**
    * Verifies that the wrench icon is displayed only on maintained projects.
    */
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
index a953c0442..37941b39b 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
@@ -334,8 +334,6 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
       $this->markTestSkipped('This test requires Drupal 10.3 or later.');
     }
     $assert_session = $this->assertSession();
-    // Enable module for extra source plugin.
-    $this->container->get('module_installer')->install(['project_browser_devel']);
     $this->config('project_browser.admin_settings')
       ->set('enabled_sources', ['project_browser_test_mock'])
       ->save();
@@ -401,10 +399,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
   public function testTabledrag(): void {
     $page = $this->getSession()->getPage();
     $assert_session = $this->assertSession();
-    $this->container->get('module_installer')->install([
-      'block',
-      'project_browser_devel',
-    ]);
+    $this->container->get('module_installer')->install(['block']);
     $this->drupalPlaceBlock('local_tasks_block');
 
     $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
@@ -417,17 +412,17 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     // Re-order plugins.
     $this->drupalGet('admin/config/development/project_browser');
     $first_plugin = $page->find('css', '#source--drupalorg_jsonapi');
-    $second_plugin = $page->find('css', '#source--random_data');
+    $second_plugin = $page->find('css', '#source--drupal_core');
     $this->assertNotNull($second_plugin);
     $first_plugin?->find('css', '.tabledrag-handle')?->dragTo($second_plugin);
     $this->assertNotNull($first_plugin);
     $this->assertTableRowWasDragged($first_plugin);
     $this->submitForm([], 'Save');
 
-    // Verify that Random data is first tab.
+    // Verify that core modules is first tab.
     $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
     $this->assertElementIsVisible('css', '#project-browser .pb-project');
-    $this->assertSame('Random data', $local_tasks[0]->getText());
+    $this->assertSame('Core modules', $local_tasks[0]->getText());
 
     // Disable the mock plugin.
     $this->drupalGet('admin/config/development/project_browser');
@@ -440,10 +435,9 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->submitForm([], 'Save');
     $assert_session->pageTextContains('The configuration options have been saved.');
 
-    // Verify that only Random data plugin is enabled.
-    $this->drupalGet('admin/modules/browse/random_data');
-    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 20);
+    // Verify that only core modules plugin is enabled.
+    $this->drupalGet('admin/modules/browse/drupal_core');
+    $this->assertElementIsVisible('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
 
     $this->config('project_browser.admin_settings')->set('enabled_sources', ['project_browser_test_mock'])->save(TRUE);
     $this->drupalGet('admin/config/development/project_browser');
-- 
GitLab


From ecda4a4d303f58f577bae298ce7f5946c7608e24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 13:48:44 -0500
Subject: [PATCH 3/8] Coding standards

---
 tests/src/FunctionalJavascript/ProjectBrowserUiTest.php | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index bdb991fe0..f20e44e14 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -8,7 +8,6 @@ use Behat\Mink\Element\DocumentElement;
 use Behat\Mink\Element\NodeElement;
 use Drupal\Core\Extension\MissingDependencyException;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
-use Drupal\project_browser\EnabledSourceHandler;
 
 // cspell:ignore coverageall doomer eggman quiznos statusactive statusmaintained
 // cspell:ignore vetica
-- 
GitLab


From 160e304503dacb94f72dbea4a3355ad6612f02df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 13:57:37 -0500
Subject: [PATCH 4/8] Update assertions for testTabledrag()

---
 .../ProjectBrowserUiTestJsonApi.php                  | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
index 37941b39b..98bf140a8 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
@@ -402,11 +402,15 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->container->get('module_installer')->install(['block']);
     $this->drupalPlaceBlock('local_tasks_block');
 
+    $this->config('project_browser.admin_settings')
+      ->set('enabled_sources', ['drupalorg_jsonapi', 'drupal_core'])
+      ->save();
+
     $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
     $local_tasks = $assert_session->elementExists('css', 'h2:contains("Primary tabs") + ul')
       ->findAll('css', 'li a[href*="/admin/modules/browse/"]');
     $this->assertCount(2, $local_tasks);
-    // Verify that the mocked source is first tab.
+    // Verify that the contrib modules source is first tab.
     $this->assertSame('Contrib modules', $local_tasks[0]->getText());
 
     // Re-order plugins.
@@ -424,7 +428,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     $this->assertElementIsVisible('css', '#project-browser .pb-project');
     $this->assertSame('Core modules', $local_tasks[0]->getText());
 
-    // Disable the mock plugin.
+    // Disable the contrib modules plugin.
     $this->drupalGet('admin/config/development/project_browser');
     $enabled_row = $page->find('css', '#source--drupalorg_jsonapi');
     $disabled_region_row = $page->find('css', '.status-title-disabled');
@@ -437,12 +441,12 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
 
     // Verify that only core modules plugin is enabled.
     $this->drupalGet('admin/modules/browse/drupal_core');
-    $this->assertElementIsVisible('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $this->assertElementIsVisible('css', '.pb-project');
 
     $this->config('project_browser.admin_settings')->set('enabled_sources', ['project_browser_test_mock'])->save(TRUE);
     $this->drupalGet('admin/config/development/project_browser');
     $this->assertTrue($assert_session->optionExists('edit-enabled-sources-project-browser-test-mock-status', 'enabled')->isSelected());
-    $this->assertTrue($assert_session->optionExists('edit-enabled-sources-random-data-status', 'disabled')->isSelected());
+    $this->assertTrue($assert_session->optionExists('edit-enabled-sources-drupal-core-status', 'disabled')->isSelected());
 
     // Verify that only the mock plugin is enabled.
     $this->drupalGet('admin/modules/browse/project_browser_test_mock');
-- 
GitLab


From f40a9ff131d24959925a4a6cb0e755e44627440e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 13:59:01 -0500
Subject: [PATCH 5/8] Remove duplicated testTabledrag()

---
 .../ProjectBrowserUiTest.php                  | 31 ++++++----
 .../ProjectBrowserUiTestJsonApi.php           | 61 -------------------
 2 files changed, 18 insertions(+), 74 deletions(-)

diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
index f20e44e14..a2f3d3c1b 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php
@@ -763,30 +763,35 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
     $this->container->get('module_installer')->install(['block']);
     $this->drupalPlaceBlock('local_tasks_block');
 
-    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
+    $this->config('project_browser.admin_settings')
+      ->set('enabled_sources', ['drupalorg_jsonapi', 'drupal_core'])
+      ->save();
+
+    $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
     $local_tasks = $assert_session->elementExists('css', 'h2:contains("Primary tabs") + ul')
       ->findAll('css', 'li a[href*="/admin/modules/browse/"]');
     $this->assertCount(2, $local_tasks);
-    // Verify that the mock plugin is first tab.
-    $this->assertSame('Browse', $local_tasks[0]->getText());
+    // Verify that the contrib modules source is first tab.
+    $this->assertSame('Contrib modules', $local_tasks[0]->getText());
 
     // Re-order plugins.
     $this->drupalGet('admin/config/development/project_browser');
-    $first_plugin = $page->find('css', '#source--project_browser_test_mock');
+    $first_plugin = $page->find('css', '#source--drupalorg_jsonapi');
     $second_plugin = $page->find('css', '#source--drupal_core');
     $this->assertNotNull($second_plugin);
-    $first_plugin?->find('css', '.handle')?->dragTo($second_plugin);
+    $first_plugin?->find('css', '.tabledrag-handle')?->dragTo($second_plugin);
     $this->assertNotNull($first_plugin);
     $this->assertTableRowWasDragged($first_plugin);
     $this->submitForm([], 'Save');
 
-    // Verify that Core modules is first tab.
-    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
+    // Verify that core modules is first tab.
+    $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
+    $this->assertElementIsVisible('css', '#project-browser .pb-project');
     $this->assertSame('Core modules', $local_tasks[0]->getText());
 
-    // Disable the mock plugin.
+    // Disable the contrib modules plugin.
     $this->drupalGet('admin/config/development/project_browser');
-    $enabled_row = $page->find('css', '#source--project_browser_test_mock');
+    $enabled_row = $page->find('css', '#source--drupalorg_jsonapi');
     $disabled_region_row = $page->find('css', '.status-title-disabled');
     $this->assertNotNull($disabled_region_row);
     $enabled_row?->find('css', '.handle')?->dragTo($disabled_region_row);
@@ -797,17 +802,17 @@ class ProjectBrowserUiTest extends WebDriverTestBase {
 
     // Verify that only core modules plugin is enabled.
     $this->drupalGet('admin/modules/browse/drupal_core');
-    $this->assertElementIsVisible('css', '.pb-filter__checkbox');
+    $this->assertElementIsVisible('css', '.pb-project');
 
     $this->config('project_browser.admin_settings')->set('enabled_sources', ['project_browser_test_mock'])->save(TRUE);
     $this->drupalGet('admin/config/development/project_browser');
     $this->assertTrue($assert_session->optionExists('edit-enabled-sources-project-browser-test-mock-status', 'enabled')->isSelected());
-    $this->assertTrue($assert_session->optionExists('edit-enabled-sources-random-data-status', 'disabled')->isSelected());
+    $this->assertTrue($assert_session->optionExists('edit-enabled-sources-drupal-core-status', 'disabled')->isSelected());
 
     // Verify that only the mock plugin is enabled.
     $this->drupalGet('admin/modules/browse/project_browser_test_mock');
-    $this->svelteInitHelper('css', '.pb-filter__checkbox');
-    $assert_session->elementsCount('css', '.pb-filter__checkbox', 19);
+    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
+    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 19);
   }
 
   /**
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
index 98bf140a8..e262762c6 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php
@@ -393,65 +393,4 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase {
     }
   }
 
-  /**
-   * Tests tabledrag on configuration page.
-   */
-  public function testTabledrag(): void {
-    $page = $this->getSession()->getPage();
-    $assert_session = $this->assertSession();
-    $this->container->get('module_installer')->install(['block']);
-    $this->drupalPlaceBlock('local_tasks_block');
-
-    $this->config('project_browser.admin_settings')
-      ->set('enabled_sources', ['drupalorg_jsonapi', 'drupal_core'])
-      ->save();
-
-    $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
-    $local_tasks = $assert_session->elementExists('css', 'h2:contains("Primary tabs") + ul')
-      ->findAll('css', 'li a[href*="/admin/modules/browse/"]');
-    $this->assertCount(2, $local_tasks);
-    // Verify that the contrib modules source is first tab.
-    $this->assertSame('Contrib modules', $local_tasks[0]->getText());
-
-    // Re-order plugins.
-    $this->drupalGet('admin/config/development/project_browser');
-    $first_plugin = $page->find('css', '#source--drupalorg_jsonapi');
-    $second_plugin = $page->find('css', '#source--drupal_core');
-    $this->assertNotNull($second_plugin);
-    $first_plugin?->find('css', '.tabledrag-handle')?->dragTo($second_plugin);
-    $this->assertNotNull($first_plugin);
-    $this->assertTableRowWasDragged($first_plugin);
-    $this->submitForm([], 'Save');
-
-    // Verify that core modules is first tab.
-    $this->drupalGet('admin/modules/browse/drupalorg_jsonapi');
-    $this->assertElementIsVisible('css', '#project-browser .pb-project');
-    $this->assertSame('Core modules', $local_tasks[0]->getText());
-
-    // Disable the contrib modules plugin.
-    $this->drupalGet('admin/config/development/project_browser');
-    $enabled_row = $page->find('css', '#source--drupalorg_jsonapi');
-    $disabled_region_row = $page->find('css', '.status-title-disabled');
-    $this->assertNotNull($disabled_region_row);
-    $enabled_row?->find('css', '.handle')?->dragTo($disabled_region_row);
-    $this->assertNotNull($enabled_row);
-    $this->assertTableRowWasDragged($enabled_row);
-    $this->submitForm([], 'Save');
-    $assert_session->pageTextContains('The configuration options have been saved.');
-
-    // Verify that only core modules plugin is enabled.
-    $this->drupalGet('admin/modules/browse/drupal_core');
-    $this->assertElementIsVisible('css', '.pb-project');
-
-    $this->config('project_browser.admin_settings')->set('enabled_sources', ['project_browser_test_mock'])->save(TRUE);
-    $this->drupalGet('admin/config/development/project_browser');
-    $this->assertTrue($assert_session->optionExists('edit-enabled-sources-project-browser-test-mock-status', 'enabled')->isSelected());
-    $this->assertTrue($assert_session->optionExists('edit-enabled-sources-drupal-core-status', 'disabled')->isSelected());
-
-    // Verify that only the mock plugin is enabled.
-    $this->drupalGet('admin/modules/browse/project_browser_test_mock');
-    $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]');
-    $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 19);
-  }
-
 }
-- 
GitLab


From 8f46427ff7767a2c5b8d57b178c12fff885b9caf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 14:10:44 -0500
Subject: [PATCH 6/8] Restore info file and uninstall hook

---
 .../project_browser_devel.info.yml              |  7 +++++++
 .../project_browser_devel.install               | 17 +++++++++++++++++
 .../ProjectBrowserPluginTest.php                |  3 +--
 3 files changed, 25 insertions(+), 2 deletions(-)
 create mode 100644 modules/project_browser_devel/project_browser_devel.info.yml
 create mode 100644 modules/project_browser_devel/project_browser_devel.install

diff --git a/modules/project_browser_devel/project_browser_devel.info.yml b/modules/project_browser_devel/project_browser_devel.info.yml
new file mode 100644
index 000000000..bc9628783
--- /dev/null
+++ b/modules/project_browser_devel/project_browser_devel.info.yml
@@ -0,0 +1,7 @@
+name: Project Browser Devel
+type: module
+description: Provides a Project Browser source plugin that generates random data.
+core_version_requirement: ^10 || ^11
+dependencies:
+  - project_browser:project_browser
+lifecycle: obsolete
diff --git a/modules/project_browser_devel/project_browser_devel.install b/modules/project_browser_devel/project_browser_devel.install
new file mode 100644
index 000000000..2a21d6676
--- /dev/null
+++ b/modules/project_browser_devel/project_browser_devel.install
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Install and uninstall functions for the project_browser_devel module.
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function project_browser_devel_uninstall(): void {
+  // Disable the random_data source.
+  $admin_settings = \Drupal::configFactory()->getEditable('project_browser.admin_settings');
+  $enabled_sources = $admin_settings->get('enabled_sources');
+  $enabled_sources = array_diff($enabled_sources, ['random_data']);
+  $admin_settings->set('enabled_sources', $enabled_sources)->save();
+}
diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
index 23164c107..2d12ff648 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
@@ -120,8 +120,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
    * Tests the not-compatible flag.
    */
   public function testNotCompatibleText(): void {
-    \Drupal::state()
-      ->set('project_browser_test_mock isCompatible', FALSE);
+    \Drupal::state()->set('project_browser_test_mock isCompatible', FALSE);
 
     $this->drupalGet('admin/modules/browse/project_browser_test_mock');
     $this->svelteInitHelper('css', '.project_status-indicator');
-- 
GitLab


From 18d23c54f3547c1cd04d3cb3bed91551d430b97a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net>
Date: Wed, 26 Feb 2025 14:15:34 -0500
Subject: [PATCH 7/8] Add lifecycle link

---
 modules/project_browser_devel/project_browser_devel.info.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/modules/project_browser_devel/project_browser_devel.info.yml b/modules/project_browser_devel/project_browser_devel.info.yml
index bc9628783..e43f443a8 100644
--- a/modules/project_browser_devel/project_browser_devel.info.yml
+++ b/modules/project_browser_devel/project_browser_devel.info.yml
@@ -5,3 +5,4 @@ core_version_requirement: ^10 || ^11
 dependencies:
   - project_browser:project_browser
 lifecycle: obsolete
+lifecycle_link: https://www.drupal.org/project/project_browser/issues/3509170
-- 
GitLab


From 55dfa1af75204fd4d9d50a5a4660513bc09ecd21 Mon Sep 17 00:00:00 2001
From: Adam G-H <32250-phenaproxima@users.noreply.drupalcode.org>
Date: Wed, 26 Feb 2025 21:14:09 +0000
Subject: [PATCH 8/8] Explain use of core source

---
 tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
index 2d12ff648..8de02abe7 100644
--- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
+++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php
@@ -60,6 +60,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase {
     $page = $this->getSession()->getPage();
     $assert_session = $this->assertSession();
 
+    // Browse core modules, because there are enough of them to paginate.
     $this->drupalGet('admin/modules/browse/drupal_core');
     // Immediately clear filters so there are enough visible to enable paging.
     $this->svelteInitHelper('test', 'Clear Filters');
-- 
GitLab