From 78a7b4766bd275f103ee2d9e2903e7f74a77feee Mon Sep 17 00:00:00 2001
From: Lauri Eskola <lauri.eskola@acquia.com>
Date: Thu, 12 Jan 2023 17:38:28 +0200
Subject: [PATCH] Issue #2715663 by ravi.shankar, andrewmacpherson, waako,
 yogeshmpawar, ankithashetty, karishmaamin, michielnugter, longwave, emcoward,
 smustgrave, alexpott, scuba_fly, BarisW, opdavies: Use Drupal.formatPlural
 for when announcing module-filter results for screenreader users

---
 core/modules/system/js/system.modules.js      |  8 +-
 .../FunctionalJavascript/ModuleFilterTest.php | 97 +++++++++++++++++++
 2 files changed, 102 insertions(+), 3 deletions(-)
 create mode 100644 core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php

diff --git a/core/modules/system/js/system.modules.js b/core/modules/system/js/system.modules.js
index f46b1226dcdd..03ac78996014 100644
--- a/core/modules/system/js/system.modules.js
+++ b/core/modules/system/js/system.modules.js
@@ -71,9 +71,11 @@
           $details.attr('open', true).each(hidePackageDetails);
 
           Drupal.announce(
-            Drupal.t('!modules modules are available in the modified list.', {
-              '!modules': $rowsAndDetails.find('tbody tr:visible').length,
-            }),
+            Drupal.formatPlural(
+              $rowsAndDetails.find('tbody tr:visible').length,
+              '1 module is available in the modified list.',
+              '@count modules are available in the modified list.',
+            ),
           );
         } else if (searching) {
           searching = false;
diff --git a/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php b/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php
new file mode 100644
index 000000000000..1dd3e48fdb87
--- /dev/null
+++ b/core/modules/system/tests/src/FunctionalJavascript/ModuleFilterTest.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\Tests\system\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * Tests the JavaScript functionality of the module filter.
+ *
+ * @group system
+ */
+class ModuleFilterTest extends WebDriverTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['user', 'system'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    $admin_user = $this->drupalCreateUser([
+      'administer modules',
+    ]);
+    $this->drupalLogin($admin_user);
+  }
+
+  /**
+   * Tests that filter results announcement has correct pluralization.
+   */
+  public function testModuleFilter() {
+
+    // Find the module filter field.
+    $this->drupalGet('admin/modules');
+    $assertSession = $this->assertSession();
+    $session = $this->getSession();
+    $page = $session->getPage();
+
+    $filter = $page->findField('edit-text');
+
+    // Get all module rows, for assertions later.
+    $module_rows = $page->findAll('css', '.package-listing tbody tr td.module');
+
+    // Test module filter reduces the number of visible rows.
+    $filter->setValue('test');
+    $session->wait(1000, 'jQuery("#module-node:visible").length == 0');
+    $visible_rows = $this->filterVisibleElements($module_rows);
+    // Test Drupal.announce() message when multiple matches are expected.
+    $expected_message = count($visible_rows) . ' modules are available in the modified list.';
+    $assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
+    self::assertGreaterThan(count($visible_rows), count($module_rows));
+    self::assertGreaterThan(1, count($visible_rows));
+
+    // Test Drupal.announce() message when one match is expected.
+    // Using a very specific module name, we expect only one row.
+    $filter->setValue('System dependency test');
+    $session->wait(1000, 'jQuery("#module-node:visible").length == 0');
+    $visible_rows = $this->filterVisibleElements($module_rows);
+    self::assertEquals(1, count($visible_rows));
+    $expected_message = '1 module is available in the modified list.';
+    $assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
+
+    // Test Drupal.announce() message when no matches are expected.
+    $filter->setValue('Pan-Galactic Gargle Blaster');
+    $session->wait(1000, 'jQuery("#module-node:visible").length == 0');
+    $visible_rows = $this->filterVisibleElements($module_rows);
+    self::assertEquals(0, count($visible_rows));
+
+    $expected_message = '0 modules are available in the modified list.';
+    $assertSession->elementTextContains('css', '#drupal-live-announce', $expected_message);
+  }
+
+  /**
+   * Removes any non-visible elements from the passed array.
+   *
+   * @param \Behat\Mink\Element\NodeElement[] $elements
+   *   An array of node elements.
+   *
+   * @return \Behat\Mink\Element\NodeElement[]
+   *   An array of node elements.
+   */
+  protected function filterVisibleElements(array $elements): array {
+    $elements = array_filter($elements, function ($element) {
+      return $element->isVisible();
+    });
+    return $elements;
+  }
+
+}
-- 
GitLab