;
let component: SearchInputControlComponent;
-
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppTestingModule, SearchInputControlComponent],
schemas: [NO_ERRORS_SCHEMA]
});
-
fixture = TestBed.createComponent(SearchInputControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -54,6 +52,7 @@ describe('SearchInputControlComponent', () => {
it('should not emit submit event if form is invalid', () => {
component.searchTerm = '';
spyOn(component.submit, 'emit');
+
component.searchSubmit();
expect(component.submit.emit).not.toHaveBeenCalled();
@@ -63,14 +62,12 @@ describe('SearchInputControlComponent', () => {
let emittedSearchTerm = '';
component.searchChange.subscribe((searchTerm) => (emittedSearchTerm = searchTerm));
component.searchTerm = 'mock-search-term';
-
expect(emittedSearchTerm).toBe('mock-search-term');
});
it('should emit searchChange event on clear', () => {
let emittedSearchTerm: string = null;
component.searchChange.subscribe((searchTerm) => (emittedSearchTerm = searchTerm));
-
component.clear();
expect(emittedSearchTerm).toBe('');
});
@@ -78,18 +75,15 @@ describe('SearchInputControlComponent', () => {
it('should clear searchTerm', () => {
component.searchTerm = 'c';
fixture.detectChanges();
-
component.clear();
expect(component.searchTerm).toBe('');
});
it('should check if searchTerm has a length less than 2', () => {
expect(component.isTermTooShort()).toBe(false);
-
component.searchTerm = 'd';
fixture.detectChanges();
expect(component.isTermTooShort()).toBe(true);
-
component.searchTerm = 'dd';
fixture.detectChanges();
expect(component.isTermTooShort()).toBe(false);
diff --git a/projects/aca-content/src/lib/components/search/search-input-control/search-input-control.component.ts b/projects/aca-content/src/lib/components/search/search-input-control/search-input-control.component.ts
index d5ddbb57a..87512c2a0 100644
--- a/projects/aca-content/src/lib/components/search/search-input-control/search-input-control.component.ts
+++ b/projects/aca-content/src/lib/components/search/search-input-control/search-input-control.component.ts
@@ -31,6 +31,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { noWhitespaceValidator } from 'projects/aca-content/folder-rules/src/rule-details/validators/no-whitespace.validator';
@Component({
imports: [CommonModule, TranslatePipe, MatButtonModule, MatIconModule, MatFormFieldModule, MatInputModule, FormsModule, ReactiveFormsModule],
@@ -65,7 +66,7 @@ export class SearchInputControlComponent implements OnInit {
@ViewChild('searchInput', { static: true })
searchInput: ElementRef;
- searchFieldFormControl = new FormControl('', [Validators.required]);
+ searchFieldFormControl = new FormControl('', [Validators.required, noWhitespaceValidator()]);
get searchTerm(): string {
return this.searchFieldFormControl.value.replace('text:', 'TEXT:');
@@ -83,16 +84,14 @@ export class SearchInputControlComponent implements OnInit {
}
openDropdown() {
- setTimeout(() => {
- this.searchInput.nativeElement.focus();
- }, 0);
+ this.searchInput.nativeElement.focus();
}
searchSubmit() {
this.searchFieldFormControl.markAsTouched();
const trimmedTerm = this.searchTerm?.trim();
- if (this.searchFieldFormControl.valid && trimmedTerm?.length > 0) {
+ if (this.searchFieldFormControl.valid && trimmedTerm) {
this.submit.emit(trimmedTerm);
}
}
@@ -107,6 +106,6 @@ export class SearchInputControlComponent implements OnInit {
}
isTermTooShort() {
- return !!(this.searchTerm && this.searchTerm.length < 2);
+ return !!(this.searchTerm.trim() && this.searchTerm.trim().length < 2);
}
}
diff --git a/projects/aca-content/src/lib/components/search/search-input/search-input.component.html b/projects/aca-content/src/lib/components/search/search-input/search-input.component.html
index 5f647124f..983879f3d 100644
--- a/projects/aca-content/src/lib/components/search/search-input/search-input.component.html
+++ b/projects/aca-content/src/lib/components/search/search-input/search-input.component.html
@@ -36,14 +36,16 @@
(submit)="onSearchSubmit($event)"
(searchChange)="onSearchChange($event)"
/>
-
-
- {{ 'SEARCH.INPUT.HINT' | translate }}
-
-
- {{ 'SEARCH.INPUT.REQUIRED' | translate }}
-
-
+
+ {{ 'SEARCH.INPUT.HINT' | translate }}
+
+
+ {{ 'SEARCH.INPUT.REQUIRED' | translate }}
+
+
+ {{ 'SEARCH.INPUT.WHITESPACE' | translate }}
+
{
let fixture: ComponentFixture;
let component: SearchInputComponent;
let store: jasmine.SpyObj>;
+ let unitTestingUtils: UnitTestingUtils;
+ let loader: HarnessLoader;
+
+ function getFirstError(): string {
+ const error = unitTestingUtils.getByDirective(MatError);
+ return error?.nativeElement.textContent.trim();
+ }
+
+ async function openMenu() {
+ const menu = await loader.getHarness(MatMenuHarness);
+ await menu.open();
+ return menu;
+ }
+
+ async function getCheckbox(id: string) {
+ const overlayLoader = TestbedHarnessEnvironment.documentRootLoader(fixture);
+ return overlayLoader.getHarness(MatCheckboxHarness.with({ selector: `#${id}` }));
+ }
+
+ async function uncheckAllCheckboxes() {
+ const checkboxIds = ['libraries', 'folder', 'content'];
+ for (const id of checkboxIds) {
+ try {
+ const checkbox = await getCheckbox(id);
+ if (await checkbox.isChecked()) {
+ await checkbox.uncheck();
+ fixture.detectChanges();
+ }
+ } catch (err) {
+ fail(`Checkbox with id ${id} not found`);
+ }
+ }
+ }
beforeEach(async () => {
const storeSpy = jasmine.createSpyObj>('Store', ['dispatch', 'pipe']);
await TestBed.configureTestingModule({
- imports: [AppTestingModule, ReactiveFormsModule, SearchInputComponent, SearchInputControlComponent],
- providers: [{ provide: Store, useValue: storeSpy }],
- schemas: [NO_ERRORS_SCHEMA]
+ imports: [AppTestingModule, SearchInputComponent],
+ providers: [{ provide: Store, useValue: storeSpy }]
}).compileComponents();
fixture = TestBed.createComponent(SearchInputComponent);
@@ -53,21 +86,13 @@ describe('SearchInputComponent', () => {
store = TestBed.inject(Store) as jasmine.SpyObj>;
store.pipe.and.returnValue(of([]));
fixture.detectChanges();
+ unitTestingUtils = new UnitTestingUtils(fixture.debugElement);
+ loader = TestbedHarnessEnvironment.loader(fixture);
});
- function getFirstError(): string {
- const error = fixture.debugElement.query(By.directive(MatError));
- return error?.nativeElement.textContent.trim();
- }
+ it('should show required error when field is empty and touched', async () => {
+ await openMenu();
- function openSearchContainer(): void {
- const menuButton = fixture.debugElement.query(By.css('.app-search-container'));
- menuButton?.nativeElement.click();
- fixture.detectChanges();
- }
-
- it('should show required error when field is empty and touched', () => {
- openSearchContainer();
component.searchInputControl.searchFieldFormControl.setValue('');
component.searchInputControl.searchFieldFormControl.markAsTouched();
fixture.detectChanges();
@@ -75,47 +100,50 @@ describe('SearchInputComponent', () => {
expect(getFirstError()).toBe('SEARCH.INPUT.REQUIRED');
});
- it('should not show error when field has value', () => {
- openSearchContainer();
- component.searchInputControl.searchFieldFormControl.setValue('test');
+ it('should not show error when field has value', async () => {
+ await openMenu();
+
+ component.searchInputControl.searchFieldFormControl.setValue('not giving up');
component.searchInputControl.searchFieldFormControl.markAsTouched();
fixture.detectChanges();
- const error = fixture.debugElement.query(By.directive(MatError));
+ const error = unitTestingUtils.getByDirective(MatError);
expect(error).toBeNull();
});
- it('should not show error when field is untouched', () => {
- openSearchContainer();
+ it('should not show error when field is untouched', async () => {
+ await openMenu();
+
component.searchInputControl.searchFieldFormControl.setValue('');
component.searchInputControl.searchFieldFormControl.markAsUntouched();
fixture.detectChanges();
- const error = fixture.debugElement.query(By.directive(MatError));
+ const error = unitTestingUtils.getByDirective(MatError);
expect(error).toBeNull();
});
- it('should dispatch SearchByTermAction when libraries are checked and term is new', () => {
- spyOn(component as any, 'isLibrariesChecked').and.returnValue(true);
- spyOn(component as any, 'isFoldersChecked').and.returnValue(false);
- spyOn(component as any, 'isFilesChecked').and.returnValue(false);
-
- component.searchedWord = 'test';
- component.onSearchSubmit('Enter');
+ it('should dispatch action when Libraries checkbox selected and term is entered', async () => {
+ await openMenu();
+ const checkbox = await getCheckbox('libraries');
+ await checkbox.check();
+ fixture.detectChanges();
+ component.onSearchSubmit({ target: { value: 'happy faces only' } });
expect(store.dispatch).toHaveBeenCalled();
});
- it('should not dispatch SearchByTermAction when no checkboxes are selected and term is empty', () => {
+ it('should not dispatch SearchByTermAction when no checkboxes are selected and term is empty', async () => {
store.dispatch.calls.reset();
- spyOn(component as any, 'isLibrariesChecked').and.returnValue(false);
- spyOn(component as any, 'isFoldersChecked').and.returnValue(false);
- spyOn(component as any, 'isFilesChecked').and.returnValue(false);
+ await openMenu();
+ await uncheckAllCheckboxes();
+
+ expect(component.searchOptions.every((option) => !option.value)).toBeTrue();
component.searchedWord = '';
- component.onSearchSubmit({ target: { value: '' } });
+ fixture.detectChanges();
+ component.onSearchSubmit({ target: { value: '' } });
expect(store.dispatch).not.toHaveBeenCalled();
});
});
diff --git a/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts b/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts
index 9390bd215..9a9c13322 100644
--- a/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts
+++ b/projects/aca-content/src/lib/components/search/search-input/search-input.component.ts
@@ -73,6 +73,7 @@ export class SearchInputComponent implements OnInit, OnDestroy {
has400LibraryError = false;
hasLibrariesConstraint = false;
searchOnChange: boolean;
+ isTrimmedWordEmpty = false;
searchedWord: string = null;
searchOptions: Array = [
@@ -177,10 +178,15 @@ export class SearchInputComponent implements OnInit, OnDestroy {
*/
onSearchSubmit(event: any) {
const searchTerm = event.target ? (event.target as HTMLInputElement).value : event;
- if (searchTerm) {
- this.searchedWord = searchTerm;
+ const trimmedTerm = searchTerm.trim();
- this.searchByOption();
+ if (trimmedTerm) {
+ this.searchedWord = trimmedTerm;
+ if (this.isLibrariesChecked() && this.searchInputControl.isTermTooShort()) {
+ return;
+ } else {
+ this.searchByOption();
+ }
} else {
this.notificationService.showError('APP.BROWSE.SEARCH.EMPTY_SEARCH');
}