diff --git a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php index 9e28991b66f2ca0cd1bca2b0a23c2d816be791c4..7b16a795fb73c873f5aed5c56a05e4dc15b1a86f 100644 --- a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php +++ b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php @@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * id = "random_data", * label = @Translation("Random data"), * description = @Translation("Gets random project and filters information"), + * local_task = {} * ) */ class RandomDataPlugin extends ProjectBrowserSourceBase { diff --git a/project_browser.links.task.yml b/project_browser.links.task.yml index c808c538cdd0822012760e807c5eea3ec64952fa..320afe230e1fce6d3b036f03bd8f7400fa864ad1 100644 --- a/project_browser.links.task.yml +++ b/project_browser.links.task.yml @@ -1,5 +1,4 @@ project_browser.browse: route_name: project_browser.browse base_route: system.modules_list - title: 'Browse' - weight: 5 + deriver: 'Drupal\project_browser\Plugin\Derivative\LocalTaskDeriver' diff --git a/project_browser.module b/project_browser.module index 329bffad7f777351e4fce43a9772d5711300f7ab..a823a5531ffb805cf64b7cfa3fd5f1b66d4d1c67 100644 --- a/project_browser.module +++ b/project_browser.module @@ -52,6 +52,9 @@ function project_browser_project_browser_source_info_alter(array &$definitions): 'id' => 'recipes', 'label' => t('Recipes'), 'description' => t('Recipes available in the local code base'), + 'local_task' => [ + 'weight' => ($definitions['drupalorg_jsonapi']['local_task']['weight'] ?? 5) + 2, + ], 'class' => Recipes::class, ]; } diff --git a/project_browser.routing.yml b/project_browser.routing.yml index 3d0b4f96cb769c83cf23fb80af6945ff78e9363a..2067321095503a258f05d5bb0e9c7876f1e67baa 100644 --- a/project_browser.routing.yml +++ b/project_browser.routing.yml @@ -24,7 +24,6 @@ project_browser.browse: _controller: '\Drupal\project_browser\Controller\BrowserController::browse' _title: 'Browse projects' id: null - source: null requirements: _permission: 'administer modules' project_browser.settings: diff --git a/src/Annotation/ProjectBrowserSource.php b/src/Annotation/ProjectBrowserSource.php index e5852f6c3ac33fe678ef00fdfd57e97b92379609..aab59a0160a0c9ea9d0a87a7790d0f29f9bd3fe6 100644 --- a/src/Annotation/ProjectBrowserSource.php +++ b/src/Annotation/ProjectBrowserSource.php @@ -48,4 +48,13 @@ class ProjectBrowserSource extends Plugin { */ public $description; + /** + * The local task definition at which this source should be exposed. + * + * If NULL, the source will never be exposed as a local task. + * + * @var array|null + */ + public ?array $local_task = NULL; + } diff --git a/src/Controller/BrowserController.php b/src/Controller/BrowserController.php index 73941c43b6644e8a0d6becc67f4f57ccbf11e058..f1a8ae13f921bc7a625378ebc2702db25f247e00 100644 --- a/src/Controller/BrowserController.php +++ b/src/Controller/BrowserController.php @@ -24,8 +24,8 @@ class BrowserController extends ControllerBase { * rendered. For example, 'https//drupal-site/admin/modules/browse/ctools' * will display the details for ctools. * - * @param string|null $source - * If viewing a specific project, the ID of its source plugin. + * @param string $source + * The ID of the source plugin to query for projects. * @param string|null $id * If viewing a specific project, the project's local ID (as known to the * source plugin). @@ -33,7 +33,7 @@ class BrowserController extends ControllerBase { * @return array * A render array. */ - public function browse(?string $source, ?string $id): array { + public function browse(string $source, ?string $id): array { return [ '#type' => 'project_browser', '#source' => $source, diff --git a/src/Controller/InstallerController.php b/src/Controller/InstallerController.php index 47f223f16cd963e670279c43e02d9db6d5dfc0b0..dcca7a89589173e9fcb5ab8034cb362ce112553d 100644 --- a/src/Controller/InstallerController.php +++ b/src/Controller/InstallerController.php @@ -201,10 +201,13 @@ class InstallerController extends ControllerBase { /** * Unlocks and destroys the stage. * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse * Redirects to the main project browser page. */ - public function unlock(): JsonResponse|RedirectResponse { + public function unlock(Request $request): JsonResponse|RedirectResponse { try { // It's possible the unlock url was provided before applying began, but // accessed after. This final check ensures a destroy is not attempted @@ -231,7 +234,11 @@ class InstallerController extends ControllerBase { } $this->installState->deleteAll(); $this->messenger()->addStatus($this->t('Operation complete, you can add a new project again.')); - return $this->redirect('project_browser.browse'); + + $redirect = Url::fromUserInput($this->getRedirectDestination()->get()) + ->setAbsolute() + ->toString(); + return new RedirectResponse($redirect); } /** @@ -264,20 +271,28 @@ class InstallerController extends ControllerBase { /** * Begins requiring by creating a stage. * + * @param \Symfony\Component\HttpFoundation\Request $request + * The current request. + * * @return \Symfony\Component\HttpFoundation\JsonResponse * Status message. */ - public function begin(): JsonResponse { + public function begin(Request $request): JsonResponse { $stage_available = $this->installer->isAvailable(); if (!$stage_available) { + $unlock_url = self::getUrlWithReplacedCsrfTokenPlaceholder( + Url::fromRoute('project_browser.install.unlock'), + ); + $destination = Url::fromRoute('project_browser.browse', [ + 'source' => $request->query->get('source'), + ]); + $unlock_url .= '&destination=' . $destination->toString(); + $updated_time = $this->installState->getFirstUpdatedTime(); if (!$this->installer->lockCameFromProjectBrowserInstaller()) { return $this->lockedResponse($this->t('The process for adding projects was locked by something else outside of Project Browser. Projects can be added again once the process is unlocked. Try again in a few minutes.'), ''); } if (empty($updated_time)) { - $unlock_url = self::getUrlWithReplacedCsrfTokenPlaceholder( - Url::fromRoute('project_browser.install.unlock') - ); $message = t('The process for adding projects is locked, but that lock has expired. Use [+ unlock link] to unlock the process and try to add the project again.'); return $this->lockedResponse($message, $unlock_url); } @@ -302,10 +317,6 @@ class InstallerController extends ControllerBase { $this->t('The process for adding the project was locked @hours hours, @minutes minutes ago. Use [+ unlock link] to unlock the process.', ['@hours' => $hours, '@minutes' => $minutes]); } - - $unlock_url = self::getUrlWithReplacedCsrfTokenPlaceholder( - Url::fromRoute('project_browser.install.unlock') - ); return $this->lockedResponse($message, $unlock_url); } diff --git a/src/Element/ProjectBrowser.php b/src/Element/ProjectBrowser.php index 06b284163eb468eac44aa323754834702ba10dc8..7308643c50e46d2246cb1633abdcbd01893df197 100644 --- a/src/Element/ProjectBrowser.php +++ b/src/Element/ProjectBrowser.php @@ -97,7 +97,7 @@ final class ProjectBrowser implements ElementInterface, ContainerFactoryPluginIn */ public function attachProjectBrowserSettings(array $element): array { $element['#attached']['drupalSettings']['project_browser'] = $this->getDrupalSettings( - $element['#source'] ?? NULL, + $element['#source'], $element['#id'] ?? NULL ); return $element; @@ -106,8 +106,8 @@ final class ProjectBrowser implements ElementInterface, ContainerFactoryPluginIn /** * Gets the Drupal settings for the Project Browser. * - * @param string|null $source - * If viewing a specific project, the ID of its source plugin. + * @param string $source + * The ID of the source plugin to query for projects. * @param string|null $id * If viewing a specific project, the project's local ID (as known to the * source plugin). @@ -115,13 +115,10 @@ final class ProjectBrowser implements ElementInterface, ContainerFactoryPluginIn * @return array * An array of Drupal settings. */ - private function getDrupalSettings(?string $source, ?string $id): array { - $current_sources = $this->enabledSourceHandler->getCurrentSources(); - if ($source) { - $current_sources = [ - $source => $current_sources[$source], - ]; - } + private function getDrupalSettings(string $source, ?string $id): array { + $current_sources = [ + $source => $this->enabledSourceHandler->getCurrentSources()[$source], + ]; $package_manager = [ 'available' => (bool) $this->configFactory->get('project_browser.admin_settings')->get('allow_ui_install'), @@ -129,7 +126,7 @@ final class ProjectBrowser implements ElementInterface, ContainerFactoryPluginIn 'warnings' => [], 'status_checked' => FALSE, ]; - if (empty($source) || empty($id)) { + if (empty($id)) { if ($package_manager['available']) { $package_manager = array_merge($package_manager, $this->installReadiness->validatePackageManager()); $package_manager['status_checked'] = TRUE; @@ -151,7 +148,6 @@ final class ProjectBrowser implements ElementInterface, ContainerFactoryPluginIn 'security_options' => SecurityStatus::asOptions(), 'development_options' => DevelopmentStatus::asOptions(), 'default_plugin_id' => reset($current_sources)->getPluginId(), - 'current_sources_keys' => array_keys($current_sources), 'package_manager' => $package_manager, 'filters' => array_map( fn (ProjectBrowserSourceInterface $source) => $source->getFilterDefinitions(), diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php index d2f8023b3d696bb2bd3479b07f0b842d45d51ecf..30a5a0e490998f7037fe5835a774435e1f5938b3 100644 --- a/src/Form/SettingsForm.php +++ b/src/Form/SettingsForm.php @@ -2,12 +2,14 @@ namespace Drupal\project_browser\Form; +use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Menu\LocalTaskManagerInterface; use Drupal\project_browser\Plugin\ProjectBrowserSourceManager; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -16,26 +18,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class SettingsForm extends ConfigFormBase { - /** - * Constructor for settings form. - * - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The config factory interface. - * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager - * The typed config manager. - * @param \Drupal\project_browser\Plugin\ProjectBrowserSourceManager $manager - * The module source manger. - * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBin - * The back end cache. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler - * The module handler. - */ public function __construct( ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, private readonly ProjectBrowserSourceManager $manager, private readonly CacheBackendInterface $cacheBin, private readonly ModuleHandlerInterface $moduleHandler, + private readonly LocalTaskManagerInterface&CachedDiscoveryInterface $localTaskManager, ) { parent::__construct($config_factory, $typed_config_manager); } @@ -50,6 +39,7 @@ class SettingsForm extends ConfigFormBase { $container->get(ProjectBrowserSourceManager::class), $container->get('cache.project_browser'), $container->get(ModuleHandlerInterface::class), + $container->get(LocalTaskManagerInterface::class), ); } @@ -233,6 +223,7 @@ class SettingsForm extends ConfigFormBase { ->set('allow_ui_install', $form_state->getValue('allow_ui_install')) ->save(); $this->cacheBin->deleteAll(); + $this->localTaskManager->clearCachedDefinitions(); parent::submitForm($form, $form_state); } diff --git a/src/Plugin/Derivative/LocalTaskDeriver.php b/src/Plugin/Derivative/LocalTaskDeriver.php new file mode 100644 index 0000000000000000000000000000000000000000..cb2d31d4a362f0eadd8378a07ee19d284d56b9a8 --- /dev/null +++ b/src/Plugin/Derivative/LocalTaskDeriver.php @@ -0,0 +1,61 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\project_browser\Plugin\Derivative; + +use Drupal\Component\Plugin\Derivative\DeriverBase; +use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\project_browser\EnabledSourceHandler; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Exposes local tasks for all enabled source plugins. + */ +final class LocalTaskDeriver extends DeriverBase implements ContainerDeriverInterface { + + use StringTranslationTrait; + + public function __construct( + private readonly EnabledSourceHandler $enabledSources, + ) {} + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get(EnabledSourceHandler::class), + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $i = 5; + foreach ($this->enabledSources->getCurrentSources() as $source) { + $source_definition = $source->getPluginDefinition(); + + if (isset($source_definition['local_task'])) { + $local_task = $base_plugin_definition + $source_definition['local_task']; + // If no title was provided for the local task, fall back to the + // source's administrative label. + $local_task += [ + 'title' => $source_definition['label'], + 'weight' => $i++, + ]; + $source_id = $source->getPluginId(); + $local_task['route_parameters'] = [ + 'source' => $source_id, + 'id' => NULL, + ]; + $derivative_id = str_replace($source::DERIVATIVE_SEPARATOR, '__', $source_id); + $this->derivatives[$derivative_id] = $local_task; + } + } + return parent::getDerivativeDefinitions($base_plugin_definition); + } + +} diff --git a/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php b/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php index b047e3b7805c2c6189308b6cca992035968d66a3..70e1496b7eacf94cd00ab3b2a91f9c0e9888b71d 100644 --- a/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php +++ b/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php @@ -30,6 +30,9 @@ use Symfony\Component\HttpFoundation\Response; * id = "drupalorg_jsonapi", * label = @Translation("Contrib modules"), * description = @Translation("Modules on Drupal.org queried via the JSON:API endpoint"), + * local_task = { + * "title" = @Translation("Browse"), + * } * ) */ class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase { diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js index af3bd9d38857ecbfc6a3f18998f03c37eabf9225..873722c94ef0d7a31cbcd045e29748890ac6878c 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 80bae75a083a63eb2e13fc5d7ec756e62fb77ebb..b8782dc1ba8789c34f7cb2ce08f3e933eadff1f4 100644 Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ diff --git a/sveltejs/src/ProcessQueueButton.svelte b/sveltejs/src/ProcessQueueButton.svelte index 44d4a5b9b15c8ec0599b6437514a3b3bfa989438..af379705aab234a7c9c5fda9f8b86ab6ca0d4cba 100644 --- a/sveltejs/src/ProcessQueueButton.svelte +++ b/sveltejs/src/ProcessQueueButton.svelte @@ -110,7 +110,7 @@ * Returns a promise that resolves once the download and activation process is complete. */ async function doRequests(projectIds) { - const beginInstallUrl = `${BASE_URL}admin/modules/project_browser/install-begin`; + const beginInstallUrl = `${BASE_URL}admin/modules/project_browser/install-begin?source=${$activeTab}`; const beginInstallResponse = await fetch(beginInstallUrl); if (!beginInstallResponse.ok) { await handleError(beginInstallResponse); diff --git a/sveltejs/src/ProjectBrowser.svelte b/sveltejs/src/ProjectBrowser.svelte index 6d733765a3b3312467dc16192022517d701416d9..e5168987a3c748e00346e91d3c1a0121f9589ea0 100644 --- a/sveltejs/src/ProjectBrowser.svelte +++ b/sveltejs/src/ProjectBrowser.svelte @@ -4,7 +4,6 @@ import ProjectGrid, { Search } from './ProjectGrid.svelte'; import Pagination from './Pagination.svelte'; import Project from './Project/Project.svelte'; - import Tabs from './Tabs.svelte'; import { numberFormatter } from './util'; import ProcessQueueButton from './ProcessQueueButton.svelte'; import { @@ -12,7 +11,6 @@ filters, rowsCount, moduleCategoryFilter, - isFirstLoad, page, sort, focusedElement, @@ -32,7 +30,6 @@ COVERED_ID, ALL_VALUES_ID, DEFAULT_SOURCE_ID, - CURRENT_SOURCES_KEYS, BASE_URL, FULL_MODULE_PATH, SORT_OPTIONS, @@ -173,7 +170,6 @@ $filters.securityCoverage = COVERED_ID; $filters.developmentStatus = ALL_VALUES_ID; } - isFirstLoad.set(false); } /** @@ -184,33 +180,18 @@ if (savedPageSize) { pageSize.set(Number(savedPageSize)); } - const matches = window.location.pathname.match( - /\/admin\/modules\/browse\/(.+)/, - ); - projectId = matches ? matches[1] : null; - // If current active plugin is disabled or if this is a plugin specific - // route, remove storage keys and reload page. - const settingsActiveTab = JSON.stringify(DEFAULT_SOURCE_ID); - if ( - ($activeTab !== settingsActiveTab && - CURRENT_SOURCES_KEYS.indexOf($activeTab) === -1) || - (projectId && $isFirstLoad) - ) { - sessionStorage.removeItem('activeTab'); - sessionStorage.removeItem('categoryFilter'); - sessionStorage.removeItem('categoryCheckedTrack'); - sessionStorage.removeItem('sortCriteria'); - sessionStorage.removeItem('sourceFilters'); - sessionStorage.removeItem('sort'); - sessionStorage.setItem('activeTab', settingsActiveTab); - window.location.reload(); - } - // Only filter by recommended on first page load or if this is a - // plugin specific route. - if (projectId || $isFirstLoad) { - await filterRecommended(); + + $activeTab = DEFAULT_SOURCE_ID; + // The project ID, if there is one, will be the last thing in the URL + // path, and we can reasonably expect it to be different than the + // source plugin ID. + projectId = window.location.pathname.substring(1).split('/').pop(); + if (projectId === $activeTab) { + projectId = null; } + await filterRecommended(); + await load($page); const focus = element ? document.getElementById(element) : false; if (focus) { @@ -353,10 +334,6 @@ <MediaQuery query="(min-width: 1200px)" let:matches> <ProjectGrid {toggleView} {loading} {rows} {pageIndex} {$pageSize} let:rows> <div slot="head"> - <!--Show tabs only if there are 2 or more plugins enabled.--> - {#if dataArray.length >= 2 && !projectId} - <Tabs {dataArray} on:tabChange={toggleRows} /> - {/if} <Search bind:this={searchComponent} on:search={onSearch} diff --git a/sveltejs/src/Tabs.svelte b/sveltejs/src/Tabs.svelte deleted file mode 100644 index ea2cbe34beee167e2bc806b030ad9995e070097b..0000000000000000000000000000000000000000 --- a/sveltejs/src/Tabs.svelte +++ /dev/null @@ -1,55 +0,0 @@ -<script> - import { createEventDispatcher } from 'svelte'; - import { activeTab } from './stores'; - - const { Drupal } = window; - const dispatch = createEventDispatcher(); - - // eslint-disable-next-line import/no-mutable-exports,import/prefer-default-export - export let dataArray = []; - let tabButtons; -</script> - -<nav class="tabs-wrapper tabs-wrapper--secondary is-horizontal"> - <div - role="tablist" - id="plugin-tabs" - aria-label={Drupal.t('Plugin tabs')} - bind:this={tabButtons} - class="tabs tabs--secondary pb-tabs" - > - {#each dataArray.map( (item) => ({ ...item, isActive: item.pluginId === $activeTab }), ) as { pluginId, pluginLabel, totalResults, isActive }} - <span - class="tabs__tab pb-tabs__tab" - class:is-active={isActive === true} - class:pb-tabs__tab--active={isActive === true} - > - <button - type="button" - role="tab" - aria-selected={isActive ? 'true' : 'false'} - aria-controls={pluginId} - tabindex="0" - id={pluginId} - class="pb-tabs__link tabs__link" - class:is-active={isActive === true} - class:pb-tabs__link--active={isActive === true} - value={pluginId} - on:click={(event) => { - dispatch('tabChange', { - pluginId, - event, - }); - }} - > - {pluginLabel} - <br /> - {Drupal.formatPlural(totalResults, '1 result', '@count results')} - {#if isActive} - <span class="visually-hidden">({Drupal.t('active tab')})</span> - {/if} - </button> - </span> - {/each} - </div> -</nav> diff --git a/sveltejs/src/constants.js b/sveltejs/src/constants.js index 3f5ede0781e97954a5f416d314973b0edf084f2b..1dd3633979c076d962fdb4563856eb8a1d8eab4e 100644 --- a/sveltejs/src/constants.js +++ b/sveltejs/src/constants.js @@ -12,8 +12,6 @@ export const ALL_VALUES_ID = drupalSettings.project_browser.special_ids.all_values; export const DEFAULT_SOURCE_ID = drupalSettings.project_browser.default_plugin_id; -export const CURRENT_SOURCES_KEYS = - drupalSettings.project_browser.current_sources_keys; export const BASE_URL = `${window.location.protocol}//${window.location.host}${drupalSettings.path.baseUrl + drupalSettings.path.pathPrefix}`; export const FULL_MODULE_PATH = `${BASE_URL}${drupalSettings.project_browser.module_path}`; export const DARK_COLOR_SCHEME = diff --git a/sveltejs/src/stores.js b/sveltejs/src/stores.js index 11124f69e552e85857454e2b11ca82f0c1a87b7c..8ec141381f71a7f0d9806e96febe47ea4e786381 100644 --- a/sveltejs/src/stores.js +++ b/sveltejs/src/stores.js @@ -6,25 +6,20 @@ import { } from './constants'; // Store the selected tab. -const storedActiveTab = JSON.parse(sessionStorage.getItem('activeTab')) || DEFAULT_SOURCE_ID; +const storedActiveTab = DEFAULT_SOURCE_ID; let activeFilters = {}; -if (sessionStorage.getItem('sourceFilters')){ - activeFilters = JSON.parse(sessionStorage.getItem('sourceFilters')); -} -else if (storedActiveTab in FILTERS) { +if (storedActiveTab in FILTERS) { activeFilters = FILTERS[storedActiveTab]; } export const sourceFilters = writable(activeFilters); -sourceFilters.subscribe((val) => sessionStorage.setItem('sourceFilters', JSON.stringify(val))); // Store for applied advanced filters. -const storedFilters = JSON.parse(sessionStorage.getItem('advancedFilter')) || { +const storedFilters = { developmentStatus: '', maintenanceStatus: '', securityCoverage: '' }; export const filters = writable(storedFilters); -filters.subscribe((val) => sessionStorage.setItem('advancedFilter', JSON.stringify(val))); export const rowsCount = writable(0); @@ -35,9 +30,8 @@ export const filtersVocabularies = writable({ }); // Store for applied category filters. -const storedModuleCategoryFilter = JSON.parse(sessionStorage.getItem('categoryFilter')) || []; +const storedModuleCategoryFilter = []; export const moduleCategoryFilter = writable(storedModuleCategoryFilter); -moduleCategoryFilter.subscribe((val) => sessionStorage.setItem('categoryFilter', JSON.stringify(val))); // Store for module category vocabularies. const storedModuleCategoryVocabularies = JSON.parse(localStorage.getItem('moduleCategoryVocabularies')) || {}; @@ -45,52 +39,42 @@ export const moduleCategoryVocabularies = writable(storedModuleCategoryVocabular moduleCategoryVocabularies.subscribe((val) => localStorage.setItem('moduleCategoryVocabularies', JSON.stringify(val))); // Store used to check if the page has loaded once already. -const storedIsFirstLoad = JSON.parse(sessionStorage.getItem('isFirstLoad')) === false ? JSON.parse(sessionStorage.getItem('isFirstLoad')) : true; +const storedIsFirstLoad = true; export const isFirstLoad = writable(storedIsFirstLoad); -isFirstLoad.subscribe((val) => sessionStorage.setItem('isFirstLoad', JSON.stringify(val))); // Store the page the user is on. -const storedPage = JSON.parse(sessionStorage.getItem('page')) || 0; +const storedPage = 0; export const page = writable(storedPage); -page.subscribe((val) => sessionStorage.setItem('page', JSON.stringify(val))); export const activeTab = writable(storedActiveTab); -activeTab.subscribe((val) => sessionStorage.setItem('activeTab', JSON.stringify(val))); // Store the current sort selected. -const storedSort = JSON.parse(sessionStorage.getItem('sort')) || SORT_OPTIONS[storedActiveTab][0].id; +const storedSort = SORT_OPTIONS[storedActiveTab][0].id; export const sort = writable(storedSort); -sort.subscribe((val) => sessionStorage.setItem('sort', JSON.stringify(val))); // Store tab-wise checked categories. -const storedCategoryCheckedTrack = JSON.parse(sessionStorage.getItem('categoryCheckedTrack')) || {}; +const storedCategoryCheckedTrack = {}; export const categoryCheckedTrack = writable(storedCategoryCheckedTrack); -categoryCheckedTrack.subscribe((val) => sessionStorage.setItem('categoryCheckedTrack', JSON.stringify(val))); // Store the element that was last focused. -const storedFocus = JSON.parse(sessionStorage.getItem('focusedElement')) || ''; +const storedFocus = ''; export const focusedElement = writable(storedFocus); -focusedElement.subscribe((val) => sessionStorage.setItem('focusedElement', JSON.stringify(val))); // Store the search string. -const storedSearchString = JSON.parse(sessionStorage.getItem('searchString')) || ''; +const storedSearchString = ''; export const searchString = writable(storedSearchString); -searchString.subscribe((val) => sessionStorage.setItem('searchString', JSON.stringify(val))); // Store for sort criteria. -const storedSortCriteria = JSON.parse(sessionStorage.getItem('sortCriteria')) || SORT_OPTIONS[storedActiveTab]; +const storedSortCriteria = SORT_OPTIONS[storedActiveTab]; export const sortCriteria = writable(storedSortCriteria); -sortCriteria.subscribe((val) => sessionStorage.setItem('sortCriteria', JSON.stringify(val))); // Store the selected toggle view. -const storedPreferredView = JSON.parse(sessionStorage.getItem('preferredView')) || 'Grid'; +const storedPreferredView = 'Grid'; export const preferredView = writable(storedPreferredView); -preferredView.subscribe((val) => sessionStorage.setItem('preferredView', JSON.stringify(val))); // Store the selected page size. -const storedPageSize = JSON.parse(sessionStorage.getItem('pageSize')) || 12; +const storedPageSize = 12; export const pageSize = writable(storedPageSize); -pageSize.subscribe((val) => sessionStorage.setItem('pageSize', JSON.stringify(val))); // Store the value of media queries. export const mediaQueryValues = writable(new Map()); @@ -98,11 +82,8 @@ export const mediaQueryValues = writable(new Map()); export const updated = writable(0); // Store for the queue list. -const storedQueueList = JSON.parse(sessionStorage.getItem('queueList')) || {}; +const storedQueueList = {}; export const queueList = writable(storedQueueList); -queueList.subscribe((val) => - sessionStorage.setItem('queueList', JSON.stringify(val)), -); export function addToQueue(tabId, project) { queueList.update((currentList) => { 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 35321cf6c7b5c55371ea2324f5c424300fcf28d1..081570f20346b7f22d30b5b8a1de65cad6b722ef 100644 --- a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php +++ b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/ProjectBrowserTestMock.php @@ -29,6 +29,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * id = "project_browser_test_mock", * label = @Translation("Project Browser Mock Plugin"), * description = @Translation("Gets project and filters information from a database"), + * local_task = { + * "title" = @Translation("Browse"), + * } * ) */ class ProjectBrowserTestMock extends ProjectBrowserSourceBase { diff --git a/tests/src/Functional/InstallerControllerTest.php b/tests/src/Functional/InstallerControllerTest.php index ccba786a502c38d9c0de8eb33df04e890cecf809..0779255457a3a65725ce5f2bfba5cccf7a9f9f66 100644 --- a/tests/src/Functional/InstallerControllerTest.php +++ b/tests/src/Functional/InstallerControllerTest.php @@ -439,10 +439,27 @@ class InstallerControllerTest extends BrowserTestBase { $this->doStart(); $this->doRequire(); + $request_options = [ + 'query' => ['source' => 'project_browser_test_mock'], + ]; + + $assert_unlock_response = function (string $response, string $expected_message): void { + $response = Json::decode($response); + $this->assertSame($expected_message, $response['message']); + + if ($response['unlock_url']) { + $this->assertStringEndsWith('/admin/modules/project_browser/install/unlock', parse_url($response['unlock_url'], PHP_URL_PATH)); + $query = parse_url($response['unlock_url'], PHP_URL_QUERY); + parse_str($query, $query); + $this->assertNotEmpty($query['token']); + $this->assertStringEndsWith('/admin/modules/browse/project_browser_test_mock', $query['destination']); + } + }; + // Check for mid install unlock offer message. - $this->drupalGet('admin/modules/project_browser/install-begin'); + $response = $this->drupalGet('admin/modules/project_browser/install-begin', $request_options); $this->assertSession()->statusCodeEquals(418); - $this->assertMatchesRegularExpression('/{"message":"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\].","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent()); + $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', @@ -453,27 +470,27 @@ class InstallerControllerTest extends BrowserTestBase { $this->assertFalse($this->installer->isAvailable()); $this->assertFalse($this->installer->isApplying()); TestTime::setFakeTimeByOffset("+800 seconds"); - $this->drupalGet('admin/modules/project_browser/install-begin'); + $response = $this->drupalGet('admin/modules/project_browser/install-begin', $request_options); $this->assertSession()->statusCodeEquals(418); $this->assertFalse($this->installer->isAvailable()); $this->assertFalse($this->installer->isApplying()); - $this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 13 minutes ago. Use \[\+ unlock link\] to unlock the process.","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent()); + $assert_unlock_response($response, "The process for adding the project was locked 13 minutes ago. Use [+ unlock link] to unlock the process."); $this->doApply(); TestTime::setFakeTimeByOffset('+800 seconds'); - $this->drupalGet('admin/modules/project_browser/install-begin'); + $response = $this->drupalGet('admin/modules/project_browser/install-begin', $request_options); $this->assertSession()->statusCodeEquals(418); $this->assertFalse($this->installer->isAvailable()); $this->assertTrue($this->installer->isApplying()); - $this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 13 minutes ago. It should not be unlocked while changes are being applied to the site.","unlock_url":""}/', $this->getSession()->getPage()->getContent()); + $assert_unlock_response($response, "The process for adding the project was locked 13 minutes ago. It should not be unlocked while changes are being applied to the site."); TestTime::setFakeTimeByOffset("+55 minutes"); - $this->drupalGet('admin/modules/project_browser/install-begin'); + $response = $this->drupalGet('admin/modules/project_browser/install-begin', $request_options); $this->assertSession()->statusCodeEquals(418); - $this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 55 minutes ago. It should not be unlocked while changes are being applied to the site.","unlock_url":""}/', $this->getSession()->getPage()->getContent()); + $assert_unlock_response($response, "The process for adding the project was locked 55 minutes ago. It should not be unlocked while changes are being applied to the site."); // Unlocking the stage becomes possible after 1 hour regardless of source. TestTime::setFakeTimeByOffset("+75 minutes"); - $this->drupalGet('admin/modules/project_browser/install-begin'); + $response = $this->drupalGet('admin/modules/project_browser/install-begin', $request_options); $this->assertSession()->statusCodeEquals(418); - $this->assertMatchesRegularExpression('/{"message":"The process for adding the project was locked 1 hours, 15 minutes ago. Use \[\+ unlock link\] to unlock the process.","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent()); + $assert_unlock_response($response, "The process for adding the project was locked 1 hours, 15 minutes ago. Use [+ unlock link] to unlock the process."); } /** @@ -487,15 +504,19 @@ class InstallerControllerTest extends BrowserTestBase { $this->doStart(); // Try beginning another install while one is in progress, but not yet in // the applying stage. - $content = $this->drupalGet('admin/modules/project_browser/install-begin'); + $content = $this->drupalGet('admin/modules/project_browser/install-begin', [ + 'query' => [ + 'source' => 'project_browser_test_mock', + ], + ]); $this->assertSession()->statusCodeEquals(418); $this->assertFalse($this->installer->isAvailable()); $this->assertFalse($this->installer->isApplying()); $json = Json::decode($content); $this->assertSame('The process for adding projects is locked, but that lock has expired. Use [+ unlock link] to unlock the process and try to add the project again.', $json['message']); - $path = explode('?', $json['unlock_url'])[0]; - $token = explode('=', $json['unlock_url'])[1]; - $unlock_content = $this->drupalGet($path, ['query' => ['token' => $token]]); + $unlock_url = parse_url($json['unlock_url']); + parse_str($unlock_url['query'], $unlock_url['query']); + $unlock_content = $this->drupalGet($unlock_url['path'], ['query' => $unlock_url['query']]); $this->assertSession()->statusCodeEquals(200); $this->assertTrue($this->installer->isAvailable()); $this->assertStringContainsString('Operation complete, you can add a new project again.', $unlock_content); @@ -511,15 +532,17 @@ class InstallerControllerTest extends BrowserTestBase { public function testCanBreakStageWithMissingProjectBrowserLock() { $this->doStart(); $this->container->get(InstallState::class)->deleteAll(); - $content = $this->drupalGet('admin/modules/project_browser/install-begin'); + $content = $this->drupalGet('admin/modules/project_browser/install-begin', [ + 'query' => ['source' => 'project_browser_test_mock'], + ]); $this->assertSession()->statusCodeEquals(418); $this->assertFalse($this->installer->isAvailable()); $this->assertFalse($this->installer->isApplying()); $json = Json::decode($content); $this->assertSame('The process for adding projects is locked, but that lock has expired. Use [+ unlock link] to unlock the process and try to add the project again.', $json['message']); - $path = explode('?', $json['unlock_url'])[0]; - $token = explode('=', $json['unlock_url'])[1]; - $unlock_content = $this->drupalGet($path, ['query' => ['token' => $token]]); + $unlock_url = parse_url($json['unlock_url']); + parse_str($unlock_url['query'], $unlock_url['query']); + $unlock_content = $this->drupalGet($unlock_url['path'], ['query' => $unlock_url['query']]); $this->assertSession()->statusCodeEquals(200); $this->assertTrue($this->installer->isAvailable()); $this->assertStringContainsString('Operation complete, you can add a new project again.', $unlock_content); diff --git a/tests/src/Functional/ProjectBrowserMenuTabsTest.php b/tests/src/Functional/ProjectBrowserMenuTabsTest.php index 76f48584143e6af9ee33acb460ee8fd9e968bac4..58336cea91ec84d54a2006f74e2e136177e3172d 100644 --- a/tests/src/Functional/ProjectBrowserMenuTabsTest.php +++ b/tests/src/Functional/ProjectBrowserMenuTabsTest.php @@ -41,7 +41,7 @@ class ProjectBrowserMenuTabsTest extends BrowserTestBase { */ public function testBrowseMenuPosition(): void { $this->drupalPlaceBlock('local_tasks_block'); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); // Assert that the second tab in the nav bar is the Browse tab. // @todo Use elementTextEquals() once support for Drupal <10.3 is dropped. // @see https://www.drupal.org/project/drupal/issues/3424746 diff --git a/tests/src/FunctionalJavascript/ProjectBrowserExamplePluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserExamplePluginTest.php index c33faf916295de86d3b038de91c40740f4e87463..8265c2911acb1f3bc0c990bd445a026870629feb 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserExamplePluginTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserExamplePluginTest.php @@ -48,7 +48,7 @@ class ProjectBrowserExamplePluginTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $this->getSession()->resizeWindow(1280, 960); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_source_example'); $this->svelteInitHelper('css', '#project-browser .pb-project--grid'); $this->assertEquals('Grid', $this->getElementText('#project-browser .pb-display__button[value="Grid"]')); $assert_session->waitForElementVisible('css', '#project-browser .pb-project'); diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index cb0008fc9fffb84251cf10d14904373a046b5282..471a786d90cb69284f2b0156593f17ad8793ceda 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -71,7 +71,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testSingleModuleAddAndInstall(): void { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.project__action_button"); @@ -99,7 +99,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testInstallModuleAlreadyInFilesystem() { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Pinky and the Brain'); $pinky_brain_selector = '#project-browser .pb-layout__main ul > li:nth-child(2)'; $action_button = $assert_session->waitForElementVisible('css', "$pinky_brain_selector button.project__action_button"); @@ -133,7 +133,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { ->set('enabled_sources', ['recipes']) ->save(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/recipes'); $this->svelteInitHelper('css', '.pb-projects-list'); $this->inputSearchField('image', TRUE); $assert_session->waitForElementVisible('css', ".search__search-submit")->click(); @@ -163,7 +163,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Pinky and the Brain'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; @@ -176,7 +176,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->submitForm([], 'Save'); $this->assertTrue($assert_session->waitForText('The configuration options have been saved.')); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $action_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.project__action_button"); $this->assertNotEmpty($action_button); @@ -192,13 +192,12 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - // Find a project we can install. - $project_id = $this->chooseProjectToInstall(); - // Start install begin. - $this->drupalGet('admin/modules/project_browser/install-begin'); + $this->drupalGet('admin/modules/project_browser/install-begin', [ + 'query' => ['source' => 'project_browser_test_mock'], + ]); $this->installState->deleteAll(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. @@ -221,7 +220,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $installed_action = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector .project_status-indicator", 30000); $assert_session->waitForText('✓ Cream cheese on a bagel is Installed'); $this->assertSame('✓ Cream cheese on a bagel is Installed', $installed_action->getText()); - } /** @@ -239,8 +237,10 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $project_id = $this->chooseProjectToInstall(['cream_cheese']); // Start install begin. - $this->drupalGet('admin/modules/project_browser/install-begin'); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/project_browser/install-begin', [ + 'query' => ['source' => 'project_browser_test_mock'], + ]); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in // the applying stage. @@ -272,7 +272,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { ->set('project_browser_test.simulated_result_severity', SystemManager::REQUIREMENT_ERROR); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $settings = $this->getDrupalSettings(); $this->assertTrue($settings['project_browser']['package_manager']['status_checked']); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); @@ -293,7 +293,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $assert_session->statusMessageContains("Simulate a warning message for the project browser.", 'warning'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; @@ -346,7 +346,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testMultipleModuleAddAndInstall(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $this->svelteInitHelper('text', 'Kangaroo'); $assert_session->buttonNotExists('Install selected projects'); @@ -398,7 +398,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testPluginSpecificQueue() { $assert_session = $this->assertSession(); $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $assert_session->buttonNotExists('Install selected projects'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; @@ -406,20 +406,13 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $select_button1->click(); $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); - $this->pressWithWait('random_data'); + $this->drupalGet('admin/modules/browse/random_data'); $assert_session->buttonNotExists('Install selected projects'); $random_data = '#project-browser .pb-layout__main ul > li:nth-child(2)'; $select_button2 = $assert_session->waitForElementVisible('css', "$random_data button.project__action_button"); $this->assertNotEmpty($select_button2); $select_button2->click(); $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); - - $this->pressWithWait('project_browser_test_mock'); - $select_button1 = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.project__action_button"); - $select_button1->click(); - $assert_session->buttonNotExists('Install selected projects'); - $this->pressWithWait('random_data'); - $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); } /** @@ -428,9 +421,11 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { public function testUnlockLinkMarkup(): void { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/project_browser/install-begin'); + $this->drupalGet('admin/modules/project_browser/install-begin', [ + 'query' => ['source' => 'project_browser_test_mock'], + ]); $this->installState->deleteAll(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; $download_button = $assert_session->waitForElementVisible('css', "$cream_cheese_module_selector button.project__action_button"); @@ -439,10 +434,12 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $download_button->click(); $this->assertNotEmpty($assert_session->waitForButton('Install selected projects')); $page->pressButton('Install selected projects'); - $this->assertMatchesRegularExpression( - '/.*\/admin\/modules\/project_browser\/install\/unlock\?token=[\w\-]+&destination=.*\/admin\/modules\/browse$/', - urldecode($assert_session->waitForElementVisible('css', "#unlock-link")->getAttribute('href')) - ); + $unlock_url = $assert_session->waitForElementVisible('css', "#unlock-link")->getAttribute('href'); + $this->assertStringEndsWith('/admin/modules/project_browser/install/unlock', parse_url($unlock_url, PHP_URL_PATH)); + $query = parse_url($unlock_url, PHP_URL_QUERY); + parse_str($query, $query); + $this->assertNotEmpty($query['token']); + $this->assertStringEndsWith('/admin/modules/browse/project_browser_test_mock', $query['destination']); } /** @@ -450,7 +447,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { */ public function testSelectDeselectToggleInModal(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $assert_session->waitForButton('Helvetica')?->click(); // Click select button in modal. diff --git a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php index 43c6508da7f752ab2dba620308d7e8b9b7bbea68..013f583bf2075d048801f9591d2cfab114731a4a 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php @@ -56,7 +56,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $this->getSession()->resizeWindow(1280, 960); - $this->drupalGet('admin/modules/browse'); + $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"]')); $assert_session->waitForElementVisible('css', '#project-browser .pb-project'); @@ -70,7 +70,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { public function testCategories(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); $this->svelteInitHelper('css', '.pb-filter__checkbox'); $assert_session->elementsCount('css', '.pb-filter__checkbox', 20); } @@ -85,7 +85,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); // Immediately clear filters so there are enough visible to enable paging. $this->svelteInitHelper('test', 'Clear Filters'); $this->svelteInitHelper('css', '.pager__item--next'); @@ -100,9 +100,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { * Tests advanced filtering. */ public function testAdvancedFiltering(): void { - $assert_session = $this->assertSession(); - - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); $this->svelteInitHelper('text', 'Results'); $this->assertEquals('Show projects covered by a security policy', $this->getElementText(self::SECURITY_OPTION_SELECTOR . self::OPTION_CHECKED)); @@ -136,7 +134,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { public function testBrokenImages(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); $this->svelteInitHelper('css', 'img[src$="images/puzzle-piece-placeholder.svg"]'); // RandomData always give an image URL. Sometimes it is a fake URL on @@ -149,7 +147,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { * Tests the not-compatible flag. */ public function testNotCompatibleText(): void { - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); $this->svelteInitHelper('css', '.project_status-indicator'); $this->assertEquals($this->getElementText('.project_status-indicator .visually-hidden') . ' Not compatible', $this->getElementText('.project_status-indicator')); } @@ -161,7 +159,7 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#project-browser .pb-project')); $this->assertTrue($assert_session->waitForText('Results')); diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php index ed47bea148c27a440886b128088fbf8ebc487741..1cb47bf99673a57ac39866b25047bd5af987f026 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php @@ -68,7 +68,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); $this->getSession()->resizeWindow(1250, 1000); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-project.pb-project--grid'); $assert_session->waitForElementVisible('css', '#pb-project-browser .pb-display__button[value="Grid"]'); $grid_text = $this->getElementText('#project-browser .pb-display__button[value="Grid"]'); @@ -97,7 +97,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testCategories(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-filter__checkbox'); $assert_session->elementsCount('css', '.pb-filter__checkbox', 19); } @@ -107,9 +107,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase { */ public function testClickableCategory(): void { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Dancing Queen'); // Click to open module page. @@ -120,10 +119,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests category filtering. */ public function testCategoryFiltering(): void { - $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-filter__multi-dropdown'); // Initial results count on page load. $this->assertTrue($assert_session->waitForText('10 Results')); @@ -194,9 +192,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests the Target blank functionality. */ public function testTargetBlank(): void { - $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Helvetica'); $assert_session->waitForButton('Helvetica')?->click(); } @@ -207,7 +204,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testReadonlyFields(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Helvetica'); $assert_session->waitForElementVisible('css', '.project__action_button'); @@ -235,7 +232,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', '10 Results'); $this->assertProjectsVisible([ @@ -323,7 +320,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-project.pb-project--list'); $this->pressWithWait('Clear filters'); $assert_session->waitForText('Modules per page'); @@ -339,9 +336,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase { */ public function testAdvancedFiltering(): void { $page = $this->getSession()->getPage(); - $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Astronaut Simulator'); $this->pressWithWait('Clear filters'); $this->pressWithWait('Recommended filters'); @@ -450,7 +446,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testSortingCriteria(): void { $assert_session = $this->assertSession(); // Clear filters. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Clear Filters'); $this->pressWithWait('Clear filters'); $assert_session->elementsCount('css', '#pb-sort option', 4); @@ -540,15 +536,11 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests search with strings that need URI encoding. */ public function testSearchForSpecialChar(): void { - // Clear filters. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', '10 Results'); $this->pressWithWait('Clear filters', '25 Results'); - // Tests for the presence of search bar placeholder text. - $search_field = $this->getSession()->getPage()->find('css', '#pb-text'); - // Fill in the search field. $this->inputSearchField('', TRUE); $this->inputSearchField('&', TRUE); @@ -601,8 +593,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { */ public function testDetailPage(): void { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Helvetica'); $assert_session->waitForButton('Helvetica')?->click(); // Check the detail modal displays. @@ -618,7 +609,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { */ public function testReopenDetailModal(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Helvetica'); $assert_session->waitForButton('Helvetica')?->click(); // Check the detail modal displays. @@ -641,9 +632,10 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests that filtering, sorting, paging persists. */ public function testPersistence(): void { + $this->markTestSkipped('Skipped because the persistence layer has been removed for now and needs to be rewritten.'); + $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Clear Filters'); $this->pressWithWait('Clear filters'); @@ -726,7 +718,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testRecommendedFilter(): void { $assert_session = $this->assertSession(); // Clear filters. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Clear Filters'); $this->pressWithWait('Clear filters', '25 Results'); $this->pressWithWait('Recommended filters'); @@ -742,6 +734,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * 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.'); + $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); // Enable module for extra source plugin. @@ -870,7 +864,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->getSession()->resizeWindow(1300, 1300); foreach ($viewSwitches as $selector) { - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', $selector['selector']); $this->getSession()->getPage()->pressButton($selector['value']); $this->svelteInitHelper('text', 'Helvetica'); @@ -878,7 +872,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->svelteInitHelper('text', 'Close'); $assert_session->waitForButton('Close')?->click(); $this->assertSession()->elementExists('css', $selector['selector'] . '.pb-display__button--selected'); - } } @@ -888,17 +881,18 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testTabledrag(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); - - $this->drupalGet('admin/modules/browse'); - $this->svelteInitHelper('text', 'Random data'); - // Count tabs. - $tab_count = $page->findAll('css', '.pb-tabs__link'); - $this->assertCount(2, $tab_count); + $this->container->get('module_installer')->install([ + 'block', + 'project_browser_devel', + ]); + $this->drupalPlaceBlock('local_tasks_block'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); + $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. - $first_tab = $page->find('css', '.pb-tabs__link:nth-child(1)'); - $this->assertEquals('project_browser_test_mock', $first_tab->getValue()); + $this->assertSame('Browse', $local_tasks[0]->getText()); // Re-order plugins. $this->drupalGet('admin/config/development/project_browser'); @@ -909,10 +903,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->submitForm([], 'Save'); // Verify that Random data is first tab. - $this->drupalGet('admin/modules/browse'); - $this->svelteInitHelper('text', 'Project Browser Mock Plugin'); - $first_tab = $page->find('css', '.pb-tabs__link:nth-child(1)'); - $this->assertEquals('random_data', $first_tab->getValue()); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); + $this->assertSame('Random data', $local_tasks[0]->getText()); // Disable the mock plugin. $this->drupalGet('admin/config/development/project_browser'); @@ -924,7 +916,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $assert_session->pageTextContains('The configuration options have been saved.'); // Verify that only Random data plugin is enabled. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/random_data'); $this->svelteInitHelper('css', '.pb-filter__checkbox'); $assert_session->elementsCount('css', '.pb-filter__checkbox', 20); @@ -934,7 +926,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->assertTrue($assert_session->optionExists('edit-enabled-sources-random-data-status', 'disabled')->isSelected()); // Verify that only the mock plugin is enabled. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-filter__checkbox'); $assert_session->elementsCount('css', '.pb-filter__checkbox', 19); } @@ -957,7 +949,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { // Check visibility of categories in each view. foreach ($view_options as $selector) { - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', $selector['selector']); $this->getSession()->getPage()->pressButton($selector['value']); $this->svelteInitHelper('text', 'Helvetica'); @@ -978,7 +970,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testPaginationWithFilters(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->pressWithWait('Clear filters'); $this->assertProjectsVisible([ 'Jazz', @@ -1019,7 +1011,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->config('project_browser.admin_settings') ->set('enabled_sources', ['drupal_core']) ->save(TRUE); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupal_core'); $this->svelteInitHelper('css', '.pb-project.pb-project--list'); $this->inputSearchField('inline form errors', TRUE); @@ -1059,7 +1051,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { */ public function testClearKeywordSearch() { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-search-results'); // Get the original result count. @@ -1086,7 +1078,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testSearchClearNoTabIndex(): void { $page = $this->getSession()->getPage(); $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-search-results'); // Search and confirm clear button has no focus after tabbing. @@ -1111,7 +1103,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { ->set('enabled_sources', ['recipes']) ->save(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/recipes'); $this->svelteInitHelper('css', '.pb-projects-list'); $this->inputSearchField('image', TRUE); $assert_session->waitForElementVisible('css', ".search__search-submit")->click(); @@ -1136,7 +1128,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testActiveInstallVisibility(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('css', '.pb-search-results'); $assert_session->waitForElementVisible('css', '.pb-project'); @@ -1182,7 +1174,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { public function testWrenchIcon(): void { $assert_session = $this->assertSession(); $this->getSession()->resizeWindow(1460, 960); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Helvetica'); // This asserts that status icon is present on the cards. $this->assertNotNull($assert_session->waitForElementVisible('css', '.pb-project__maintenance-icon .pb-project__status-icon-btn')); @@ -1206,7 +1198,7 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); // Ensure the project list is loaded. $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#project-browser .pb-project')); diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php index 27d373604b3a58215dd9062e28fa3ab26aba7fda..5d65055a79e5dbd5ae95717ca7041d1198f5a892 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php @@ -59,7 +59,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $page = $this->getSession()->getPage(); $this->getSession()->resizeWindow(1250, 1000); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('css', '.pb-project.pb-project--grid'); $assert_session->waitForElementVisible('css', '#project-browser .pb-display__button[value="Grid"]'); $grid_text = $this->getElementText('#project-browser .pb-display__button[value="Grid"]'); @@ -86,7 +86,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testCategories(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('css', '.pb-filter__multi-dropdown input[type="checkbox"]'); $assert_session->elementsCount('css', '.pb-filter__multi-dropdown input[type="checkbox"]', 19); } @@ -97,7 +97,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testClickableCategory(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Token'); $assert_session->waitForButton('Token')->click(); @@ -109,7 +109,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testCategoryFiltering(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('css', '.pb-filter__multi-dropdown'); // Initial results count on page load. $this->assertTrue($assert_session->waitForText(' Results')); @@ -162,7 +162,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { */ public function testTargetBlank(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Token'); $assert_session->waitForButton('Token')->click(); } @@ -174,7 +174,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', ' Results'); $assert_session->pageTextNotContains(' 0 Results'); $this->assertPagerItems(['1', '2', '3', '4', '5', '…', 'Next', 'Last']); @@ -220,7 +220,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testAdvancedFiltering(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Token'); $this->pressWithWait('Clear filters'); $this->pressWithWait('Recommended filters'); @@ -262,7 +262,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testSortingCriteria(): void { $assert_session = $this->assertSession(); // Clear filters. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Clear Filters'); $this->pressWithWait('Clear filters'); @@ -285,7 +285,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testDetailPage(): void { $assert_session = $this->assertSession(); - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Token'); $assert_session->waitForButton('Token')->click(); } @@ -303,7 +303,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testRecommendedFilter(): void { $assert_session = $this->assertSession(); // Clear filters. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Clear Filters'); $this->pressWithWait('Clear filters', 'Results'); $this->pressWithWait('Recommended filters'); @@ -326,16 +326,13 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $this->markTestSkipped('This test requires Drupal 10.3 or later.'); } $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); // Enable module for extra source plugin. $this->container->get('module_installer')->install(['project_browser_devel']); $this->config('project_browser.admin_settings') ->set('enabled_sources', ['recipes', 'project_browser_test_mock']) ->save(); - $this->drupalGet('admin/modules/browse'); - $this->assertTrue($assert_session->waitForText('Recipes')); - $page->pressButton('Recipes'); + $this->drupalGet('admin/modules/browse/recipes'); // Recipes doesn't define any filters so no filters are displayed. $this->assertNull($assert_session->waitForElementVisible('css', '.search__form-filters-container')); @@ -344,9 +341,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $filters_to_define = ['maintenanceStatus', 'securityCoverage']; \Drupal::state()->set('filters_to_define', $filters_to_define); - $this->drupalGet('admin/modules/browse'); - $this->assertTrue($assert_session->waitForText('Project Browser Mock Plugin')); - $page->pressButton('Project Browser Mock Plugin'); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); // Drupal.org test mock defines only two filters (actively maintained filter // and security coverage filter). $assert_session->waitForElementVisible('css', '.search__form-filters-container'); @@ -379,7 +374,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $this->getSession()->resizeWindow(1300, 1300); foreach ($viewSwitches as $selector) { - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('css', $selector['selector']); $this->getSession()->getPage()->pressButton($selector['value']); $this->svelteInitHelper('text', 'Token'); @@ -396,17 +391,19 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { public function testTabledrag(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); - $this->container->get('module_installer')->install(['project_browser_devel']); - - $this->drupalGet('admin/modules/browse'); + $this->container->get('module_installer')->install([ + 'block', + 'project_browser_devel', + ]); + $this->drupalPlaceBlock('local_tasks_block'); + + $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. + $this->assertSame('Browse', $local_tasks[0]->getText()); $assert_session->waitForElementVisible('css', '.pb-display__button'); - // Count tabs. - $tab_count = $page->findAll('css', '.pb-tabs__link'); - $this->assertCount(2, $tab_count); - - // Verify that the mock plugin is first tab. - $first_tab = $page->find('css', '.pb-tabs__link:nth-child(1)'); - $this->assertEquals('drupalorg_jsonapi', $first_tab->getValue()); // Re-order plugins. $this->drupalGet('admin/config/development/project_browser'); @@ -417,10 +414,10 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $this->submitForm([], 'Save'); // Verify that Random data is first tab. - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $assert_session->waitForElementVisible('css', '#project-browser .pb-project'); $first_tab = $page->find('css', '.pb-tabs__link:nth-child(1)'); - $this->assertEquals('random_data', $first_tab->getValue()); + $this->assertSame('Random data', $local_tasks[0]->getText()); // Disable the mock plugin. $this->drupalGet('admin/config/development/project_browser'); @@ -432,7 +429,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $assert_session->pageTextContains('The configuration options have been saved.'); // Verify that only Random data plugin is enabled. - $this->drupalGet('admin/modules/browse'); + $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); @@ -442,7 +439,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $this->assertTrue($assert_session->optionExists('edit-enabled-sources-random-data-status', 'disabled')->isSelected()); // Verify that only the mock plugin is enabled. - $this->drupalGet('admin/modules/browse'); + $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); } diff --git a/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php b/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php index 3dbf7c367bc6787acc2de9e1c346152d933b9a6e..73f292b4c0bc89b603f5e904d471cf9405795311 100644 --- a/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php +++ b/tests/src/FunctionalJavascript/TranslatedSvelteAppTest.php @@ -70,7 +70,7 @@ class TranslatedSvelteAppTest extends WebDriverTestBase { $translate_to = 'Soorch Foor Moodools'; - $this->drupalGet('admin/modules/browse'); + $this->drupalGet('admin/modules/browse/drupalorg_jsonapi'); $this->svelteInitHelper('text', 'Search'); $this->assertFalse($this->assertSession()->waitForText($translate_to)); @@ -88,7 +88,7 @@ class TranslatedSvelteAppTest extends WebDriverTestBase { $this->submitForm(['string' => 'Search'], 'Filter'); $edit = ['strings[' . $string->lid . '][translations][0]' => $translate_to]; $this->submitForm($edit, 'Save translations'); - $this->drupalGet("/$prefix/admin/modules/browse"); + $this->drupalGet("/$prefix/admin/modules/browse/drupalorg_jsonapi"); $this->svelteInitHelper('text', $translate_to); } diff --git a/tests/src/Nightwatch/Tests/consistentPagination.js b/tests/src/Nightwatch/Tests/consistentPagination.js index 532628b509a411797eb02ab56e5af1c9de605639..4874cac0bba05475154f1d4e87464018059f60f5 100644 --- a/tests/src/Nightwatch/Tests/consistentPagination.js +++ b/tests/src/Nightwatch/Tests/consistentPagination.js @@ -9,7 +9,7 @@ module.exports = { 'Test pagination consistency across tabs': function (browser) { browser.drupalLoginAsAdmin(() => { browser - .drupalRelativeURL('/admin/modules/browse') + .drupalRelativeURL('/admin/modules/browse/project_browser_test_mock') .waitForElementVisible('h1', 100) .assert.textContains('h1', 'Browse projects') .assert.visible('select.pagination__num-projects') @@ -17,7 +17,7 @@ module.exports = { browser .openNewWindow('tab') - .drupalRelativeURL('/admin/modules/browse') + .drupalRelativeURL('/admin/modules/browse/project_browser_test_mock') .waitForElementVisible('h1', 100) .assert.textContains('h1', 'Browse projects') .assert.visible('select.pagination__num-projects') diff --git a/tests/src/Nightwatch/Tests/keyboardTest.js b/tests/src/Nightwatch/Tests/keyboardTest.js index a6fcaeda277bfce364c7653e13ee7a7185ee8198..37795baafb446d7d38003922968af479e367aa10 100644 --- a/tests/src/Nightwatch/Tests/keyboardTest.js +++ b/tests/src/Nightwatch/Tests/keyboardTest.js @@ -64,7 +64,7 @@ module.exports = { // Open project browser. browser - .drupalRelativeURL('/admin/modules/browse') + .drupalRelativeURL('/admin/modules/browse/project_browser_test_mock') .waitForElementVisible('h1', delayInMilliseconds) .assert.textContains('h1', 'Browse projects') .waitForElementVisible(filterDropdownSelector);