Skip to content

Commit

Permalink
Merge branch 'release-10.1' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
demiankatz committed Jan 17, 2025
2 parents da72b24 + c81431a commit 051e656
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,26 @@ public static function applyFacetProvider(): array
];
}

/**
* Flip-flop the language to cause URL rewrites (useful for testing handling of
* arrays in query parameters).
*
* @param Element $page Current page object
*
* @return void
*/
protected function flipflopLanguage(Element $page): void
{
// Flip to German:
$this->clickCss($page, '.language.dropdown');
$this->clickCss($page, '.language.dropdown li:not(.active) a');
$this->waitForPageLoad($page);
// Flip back to English:
$this->clickCss($page, '.language.dropdown');
$this->clickCss($page, '.language.dropdown li:not(.active) a');
$this->waitForPageLoad($page);
}

/**
* Test applying a facet to filter results (deferred facet sidebar)
*
Expand Down Expand Up @@ -1432,4 +1452,80 @@ protected function assertNoResetFiltersButton(Element $page): void
$reset = $page->findAll('css', '.reset-filters-btn');
$this->assertCount(0, $reset);
}

/**
* Data provider for testMultiSelectOnAdvancedSearch()
*
* @return array[]
*/
public static function multiSelectOnAdvancedSearchProvider(): array
{
return [
'with language switch' => [true],
'without language switch' => [false],
];
}

/**
* Test applying multi-facet selection to advanced search results, with or without changing the
* language setting first.
*
* @param bool $changeLanguage Should we change the language before applying the facets?
*
* @dataProvider multiSelectOnAdvancedSearchProvider
*
* @return void
*/
public function testMultiSelectOnAdvancedSearch(bool $changeLanguage): void
{
$this->changeConfigs(
[
'facets' => [
'Results_Settings' => [
'multiFacetsSelection' => true,
],
],
]
);
$path = '/Search/Advanced';
$session = $this->getMinkSession();
$session->visit($this->getVuFindUrl() . $path);
$page = $session->getPage();
$this->waitForPageLoad($page);
$this->findCssAndSetValue($page, '#search_lookfor0_0', 'test');
$this->findCssAndSetValue($page, '#search_lookfor0_1', 'history');
$this->findCss($page, '[type=submit]')->press();

if ($changeLanguage) {
$this->flipflopLanguage($page);
}

// Activate the first two facet values:
$this->clickCss($page, '.js-user-selection-multi-filters');
$this->clickCss($page, '.facet__list__item a');
$this->clickCss($page, '.facet__list__item a', index: 1);
$this->clickCss($page, '.js-apply-multi-facets-selection');

// A past bug would cause search terms to get duplicated after facets
// were applied; make sure the search remains as expected!
$this->assertEquals(
'(All Fields:test AND All Fields:history)',
$this->findCssAndGetText($page, '.adv_search_terms strong')
);

$this->assertCount(2, $page->findAll('css', '.facet.active'));

// If configured, flip-flop language again to potentially modify filter params:
if ($changeLanguage) {
$this->flipflopLanguage($page);
}

// Let's also confirm that we can now remove the filters:
$this->clickCss($page, '.js-user-selection-multi-filters');
$this->clickCss($page, '.facet.active');
$this->clickCss($page, '.facet.active');
$this->clickCss($page, '.js-apply-multi-facets-selection');

$this->assertCount(0, $page->findAll('css', '.facet.active'));
}
}
38 changes: 34 additions & 4 deletions themes/bootstrap3/js/facets.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,38 @@ VuFind.register('multiFacetsSelection', function multiFacetsSelection() {
return value.substring(0, p) + ':' + filterValue;
}

/**
* Normalize a single query key from a search
*
* @param {string} key Key name
*
* @returns string
*/
function normalizeSearchQueryKey(key) {
// We normally use open-ended brackets to signify array-based query parameters.
// However, some server-side processing can occasionally inject explicit index
// values. We want to normalize out the index values for consistency.
return key.replace(/(.+)\[\d+\]/, '$1[]');
}

/**
* Normalize key names in a set of search parameters
*
* @param {URLSearchParams} params Parameters to normalize
*
* @returns URLSearchParams
*/
function normalizeSearchQueryKeys(params) {
const normalized = new URLSearchParams();
for (const [key, value] of params) {
normalized.append(normalizeSearchQueryKey(key), value);
}
return normalized;
}


for (const [key, value] of (new URLSearchParams(window.location.search))) {
initialParams.append(key, normalizeValue(key, value));
initialParams.append(normalizeSearchQueryKey(key), normalizeValue(key, value));
}

/**
Expand Down Expand Up @@ -219,19 +249,19 @@ VuFind.register('multiFacetsSelection', function multiFacetsSelection() {
for (const elem of elems) {
const href = elem.getAttribute('href');
const p = href.indexOf('?');
const elemParams = new URLSearchParams(p >= 0 ? href.substring(p + 1) : '');
const elemParams = normalizeSearchQueryKeys(new URLSearchParams(p >= 0 ? href.substring(p + 1) : ''));

// Add parameters that did not initially exist:
for (const [key, value] of elemParams) {
// URLSearchParams.has(key, value) seems to be broken on iOS 16, so check with our own method:
if (!VuFind.inURLSearchParams(initialParams, key, value)) {
globalAddedParams.append(key, value);
globalAddedParams.append(normalizeSearchQueryKey(key), value);
}
}
// Remove parameters that this URL no longer has:
for (const [key, value] of initialParams) {
if (!VuFind.inURLSearchParams(elemParams, key, value)) {
globalRemovedParams.append(key, value);
globalRemovedParams.append(normalizeSearchQueryKey(key), value);
}
}
}
Expand Down
38 changes: 34 additions & 4 deletions themes/bootstrap5/js/facets.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,38 @@ VuFind.register('multiFacetsSelection', function multiFacetsSelection() {
return value.substring(0, p) + ':' + filterValue;
}

/**
* Normalize a single query key from a search
*
* @param {string} key Key name
*
* @returns string
*/
function normalizeSearchQueryKey(key) {
// We normally use open-ended brackets to signify array-based query parameters.
// However, some server-side processing can occasionally inject explicit index
// values. We want to normalize out the index values for consistency.
return key.replace(/(.+)\[\d+\]/, '$1[]');
}

/**
* Normalize key names in a set of search parameters
*
* @param {URLSearchParams} params Parameters to normalize
*
* @returns URLSearchParams
*/
function normalizeSearchQueryKeys(params) {
const normalized = new URLSearchParams();
for (const [key, value] of params) {
normalized.append(normalizeSearchQueryKey(key), value);
}
return normalized;
}


for (const [key, value] of (new URLSearchParams(window.location.search))) {
initialParams.append(key, normalizeValue(key, value));
initialParams.append(normalizeSearchQueryKey(key), normalizeValue(key, value));
}

/**
Expand Down Expand Up @@ -219,19 +249,19 @@ VuFind.register('multiFacetsSelection', function multiFacetsSelection() {
for (const elem of elems) {
const href = elem.getAttribute('href');
const p = href.indexOf('?');
const elemParams = new URLSearchParams(p >= 0 ? href.substring(p + 1) : '');
const elemParams = normalizeSearchQueryKeys(new URLSearchParams(p >= 0 ? href.substring(p + 1) : ''));

// Add parameters that did not initially exist:
for (const [key, value] of elemParams) {
// URLSearchParams.has(key, value) seems to be broken on iOS 16, so check with our own method:
if (!VuFind.inURLSearchParams(initialParams, key, value)) {
globalAddedParams.append(key, value);
globalAddedParams.append(normalizeSearchQueryKey(key), value);
}
}
// Remove parameters that this URL no longer has:
for (const [key, value] of initialParams) {
if (!VuFind.inURLSearchParams(elemParams, key, value)) {
globalRemovedParams.append(key, value);
globalRemovedParams.append(normalizeSearchQueryKey(key), value);
}
}
}
Expand Down

0 comments on commit 051e656

Please sign in to comment.