Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/app/shared/search/search.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
} from '@dspace/core/shared/search/search-filters/search-config.model';
import { SidebarServiceStub } from '@dspace/core/testing/sidebar-service.stub';
import {
createFailedRemoteDataObject$,
createSuccessfulRemoteDataObject,
createSuccessfulRemoteDataObject$,
} from '@dspace/core/utilities/remote-data.utils';
Expand Down Expand Up @@ -200,6 +201,7 @@ export function configureSearchComponentTestingModule(compType, additionalDeclar
getConfigurationSortOptions: sortOptionsList,
getConfig: filtersConfigRD$,
getConfigurationSearchConfig: of(searchConfig),
getSearchConfigurationFor: createSuccessfulRemoteDataObject$(searchConfig),
getCurrentConfiguration: of('default'),
getCurrentScope: of('test-id'),
getCurrentSort: of(sortOptionsList[0]),
Expand Down Expand Up @@ -450,6 +452,30 @@ describe('SearchComponent', () => {
//Check that the last method from which the search depend upon is being called
expect(searchManagerStub.search).toHaveBeenCalled();
}));

describe('when the search configuration request fails', () => {
beforeEach(() => {
searchConfigurationServiceStub.getSearchConfigurationFor.and.returnValue(
createFailedRemoteDataObject$('Error fetching search configuration', 500),
);
});

it('should still initialize, so the search results error can be rendered', fakeAsync(() => {
comp.ngOnInit();
tick(100);

let initialized: boolean;
comp.initialized$.subscribe((res) => initialized = res);
expect(initialized).toBeTrue();
}));

it('should still request results, whose failure is what the user is shown', fakeAsync(() => {
comp.ngOnInit();
tick(100);

expect(searchManagerStub.search).toHaveBeenCalled();
}));
});
});
});
});
21 changes: 18 additions & 3 deletions src/app/shared/search/search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ import { DSpaceObject } from '@dspace/core/shared/dspace-object.model';
import { followLink } from '@dspace/core/shared/follow-link-config.model';
import { Item } from '@dspace/core/shared/item.model';
import { ListableObject } from '@dspace/core/shared/object-collection/listable-object.model';
import { getFirstCompletedRemoteData } from '@dspace/core/shared/operators';
import {
getAllCompletedRemoteData,
getFirstCompletedRemoteData,
} from '@dspace/core/shared/operators';
import { PaginatedSearchOptions } from '@dspace/core/shared/search/models/paginated-search-options.model';
import { SearchFilterConfig } from '@dspace/core/shared/search/models/search-filter-config.model';
import { SearchObjects } from '@dspace/core/shared/search/models/search-objects.model';
Expand Down Expand Up @@ -401,9 +404,21 @@ export class SearchComponent implements OnDestroy, OnInit {
// Determinate PaginatedSearchOptions and listen to any update on it
const configuration$: Observable<string> = this.searchConfigService
.getCurrentConfiguration(this.configuration).pipe(distinctUntilChanged());
// A failed search-configuration request must not stall the whole component.
// getConfigurationSearchConfig() pipes through
// getAllSucceededRemoteDataPayload(), so on failure it never emits: the
// combineLatest below never fires, initialized$ stays false, and the entire
// template is skipped — leaving a blank page in which not even
// ds-search-results (which renders the error) is created. Treat a failed
// configuration as "no sort options" so the component still initializes and
// the search results error can surface.
const searchSortOptions$: Observable<SortOptions[]> = combineLatest([configuration$, this.currentScope$]).pipe(
switchMap(([configuration, scope]: [string, string]) => this.searchConfigService.getConfigurationSearchConfig(configuration, scope)),
map((searchConfig: SearchConfig) => this.searchConfigService.getConfigurationSortOptions(searchConfig)),
switchMap(([configuration, scope]: [string, string]) => this.searchConfigService.getSearchConfigurationFor(scope, configuration).pipe(
getAllCompletedRemoteData(),
map((searchConfigRD: RemoteData<SearchConfig>) => searchConfigRD.hasSucceeded
? this.searchConfigService.getConfigurationSortOptions(searchConfigRD.payload)
: []),
)),
distinctUntilChanged(),
);
const sortOption$: Observable<SortOptions> = searchSortOptions$.pipe(
Expand Down
Loading