[MNT-25411] Fix clearing filters when search input is empty (#4878)

* [MNT-25411] Fix clearing filters when search input is empty

* [MNT-25411] Fixed unit test

* [MNT-25411] Fixed unit test

* [MNT-25411] XAT-5589 fix

* [MNT-24511] fixes for XAT-5581 and XAT-5546 excluded

---------

Co-authored-by: Adam Świderski <adam.tomasz.swiderski@gmail.com>
This commit is contained in:
AleksanderSklorz
2025-11-04 10:51:02 +01:00
committed by GitHub
co-authored by Adam Świderski
parent bceaf89e6b
commit 9d3c80ffee
8 changed files with 98 additions and 13 deletions
@@ -1,4 +1,5 @@
{
"XAT-5523": "https://hyland.atlassian.net/browse/ACA-4697",
"XAT-5516": "https://hyland.atlassian.net/browse/ACS-9889"
"XAT-5516": "https://hyland.atlassian.net/browse/ACS-9889",
"XAT-5546": "https://hyland.atlassian.net/browse/ACS-10621"
}
@@ -23,9 +23,11 @@
*/
import { SearchInputControlComponent } from './search-input-control.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { AppTestingModule } from '../../../testing/app-testing.module';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ActivatedRoute, NavigationStart, Router } from '@angular/router';
import { of } from 'rxjs';
describe('SearchInputControlComponent', () => {
let fixture: ComponentFixture<SearchInputControlComponent>;
@@ -87,4 +89,43 @@ describe('SearchInputControlComponent', () => {
fixture.detectChanges();
expect(component.isTermTooShort()).toBe(false);
});
describe('ngOnInit', () => {
let route: ActivatedRoute;
let router: Router;
beforeEach(() => {
route = TestBed.inject(ActivatedRoute);
router = TestBed.inject(Router);
spyOnProperty(router, 'events').and.returnValue(of(new NavigationStart(1, '')));
});
it('should set * as value when url params has q parameter and input is empty', fakeAsync(() => {
spyOn(component.searchFieldFormControl, 'setValue');
route.queryParams = of({ q: 'someQueryParams' });
component.ngOnInit();
tick();
expect(component.searchFieldFormControl.setValue).toHaveBeenCalledWith('*');
}));
it('should not set * as value when url params has missing q parameter and input is empty', fakeAsync(() => {
spyOn(component.searchFieldFormControl, 'setValue');
route.queryParams = of({ otherQueryParam: 'someQueryParams' });
component.ngOnInit();
tick();
expect(component.searchFieldFormControl.setValue).not.toHaveBeenCalled();
}));
it('should not set * as value when url params has q parameter and input is not empty', fakeAsync(() => {
component.searchFieldFormControl.setValue('some value');
spyOn(component.searchFieldFormControl, 'setValue');
route.queryParams = of({ q: 'someQueryParams' });
component.ngOnInit();
tick();
expect(component.searchFieldFormControl.setValue).not.toHaveBeenCalled();
}));
});
});
@@ -45,6 +45,9 @@ import { MatInputModule } from '@angular/material/input';
import { FormControl, FormsModule, ReactiveFormsModule, StatusChangeEvent, TouchedChangeEvent, Validators } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { noWhitespaceValidator } from '@alfresco/aca-shared';
import { combineLatest } from 'rxjs';
import { filter, startWith } from 'rxjs/operators';
import { ActivatedRoute, NavigationStart, Router } from '@angular/router';
@Component({
imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule],
@@ -56,6 +59,8 @@ import { noWhitespaceValidator } from '@alfresco/aca-shared';
})
export class SearchInputControlComponent implements OnInit, OnChanges {
private readonly destroyRef = inject(DestroyRef);
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
/** Type of the input field to render, e.g. "search" or "text" (default). */
@Input()
@@ -115,6 +120,19 @@ export class SearchInputControlComponent implements OnInit, OnChanges {
}
}
});
combineLatest([
this.route.queryParams,
this.router.events.pipe(
filter((e): e is NavigationStart => e instanceof NavigationStart),
startWith(null)
)
])
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(([params]) => {
if (params['q'] && !this.searchFieldFormControl.value) {
setTimeout(() => this.searchFieldFormControl.setValue('*'));
}
});
}
ngOnChanges(changes: SimpleChanges): void {
@@ -271,12 +271,11 @@ describe('SearchComponent', () => {
expect(queryBuilder.userQuery).toBe(`((cm:tag:"orange*"))`);
});
it('should get initial saved search when url matches', fakeAsync(() => {
it('should get initial saved search when url matches', () => {
route.queryParams = of({ q: encodeQuery({ name: 'test' }) });
component.ngOnInit();
tick();
expect(component.initialSavedSearch).toEqual({ name: 'test', encodedUrl: encodeQuery({ name: 'test' }), order: 0 });
}));
});
it('should render a menu with 2 options when initial saved search is found', async () => {
route.queryParams = of({ q: encodeQuery({ name: 'test' }) });
@@ -369,5 +368,17 @@ describe('SearchComponent', () => {
expect(executeSpy).toHaveBeenCalledTimes(1);
}));
it('should format userQuery when url parameters changed and userQuery is not contained by url', () => {
routerEvents.next(new NavigationStart(1, ''));
queryParams.next({ q: encodeQuery('') });
expect(queryBuilder.userQuery).toBe('((cm:name:"*"))');
});
it('should not format userQuery when url parameters changed when userQuery is already contained by url', () => {
routerEvents.next(new NavigationStart(1, ''));
queryParams.next({ q: encodeQuery({ userQuery: 'test' }) });
expect(queryBuilder.userQuery).toBe('(test)');
});
testHeader(SearchResultsComponent, false);
});
@@ -251,6 +251,9 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
.subscribe((navigationStartEvent) => {
const shouldExecuteQuery = this.shouldExecuteQuery(navigationStartEvent, this.encodedQuery);
this.queryBuilder.userQuery = extractUserQueryFromEncodedQuery(this.encodedQuery);
if (!this.searchedWord && !this.queryBuilder.userQuery && this.encodedQuery) {
this.queryBuilder.userQuery = formatSearchTerm('*', this.searchConfig['app:fields']);
}
if (shouldExecuteQuery) {
this.queryBuilder.execute(false);
@@ -260,13 +263,18 @@ export class SearchResultsComponent extends PageComponent implements OnInit {
}
onSearchError(error: { message: any }) {
const { statusCode } = JSON.parse(error.message).error;
let message: string;
try {
const { statusCode } = JSON.parse(error.message).error;
const messageKey = `APP.BROWSE.SEARCH.ERRORS.${statusCode}`;
let message = this.translationService.instant(messageKey);
const messageKey = `APP.BROWSE.SEARCH.ERRORS.${statusCode}`;
message = this.translationService.instant(messageKey);
if (message === messageKey) {
message = this.translationService.instant(`APP.BROWSE.SEARCH.ERRORS.GENERIC`);
if (message === messageKey) {
message = this.translationService.instant(`APP.BROWSE.SEARCH.ERRORS.GENERIC`);
}
} catch {
message = error.message;
}
this.notificationService.showError(message);
@@ -65,7 +65,8 @@ export const getGlobalConfig: PlaywrightTestConfig = {
screenshot: 'only-on-failure',
launchOptions: {
devtools: false,
args: ['--no-sandbox', '--disable-site-isolation-trials']
args: ['--no-sandbox', '--disable-site-isolation-trials'],
slowMo: 300
}
},
@@ -24,7 +24,7 @@
import { SearchPage } from '../../../pages';
import { BaseComponent } from '../../base.component';
import { Page } from '@playwright/test';
import { Page, Locator } from '@playwright/test';
export class SearchFiltersTags extends BaseComponent {
private static rootElement = '.adf-search-filter-menu-card';
@@ -35,10 +35,14 @@ export class SearchFiltersTags extends BaseComponent {
public addOptionInput = this.getChild(`[data-automation-id$='adf-search-chip-autocomplete-input']`);
private searchOption(value: string): Locator {
return this.page.locator(`[data-automation-id="option-${value}"]`);
}
async filterByTag(page: SearchPage, tag: string): Promise<void> {
await page.searchFilters.tagsFilter.click();
await page.searchFiltersTags.addOptionInput.fill(tag);
await page.page.keyboard.press('Enter');
await this.searchOption(tag).click();
await page.searchFilters.menuCardApply.click();
await page.dataTable.progressBarWaitForReload();
}
@@ -89,6 +89,7 @@ export class SearchOverlayComponent extends BaseComponent {
}
async searchFor(input: string): Promise<void> {
await this.searchInput.clear();
await this.searchInput.fill(input);
await this.searchButton.click({ force: true });
}