diff --git a/karma.conf.js b/karma.conf.js index 073ce7040b..456c2ecd99 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -51,7 +51,7 @@ module.exports = function (config) { */ files: [{ pattern: './spec-bundle.js', - watched: false + watched: false, }], /* diff --git a/src/app/+search-page/search-service/search.service.spec.ts b/src/app/+search-page/search-service/search.service.spec.ts index a0d4b1f049..f8a1d73ae1 100644 --- a/src/app/+search-page/search-service/search.service.spec.ts +++ b/src/app/+search-page/search-service/search.service.spec.ts @@ -10,59 +10,111 @@ import { ViewMode } from '../../+search-page/search-options.model'; import { RouteService } from '../../shared/route.service'; import { GLOBAL_CONFIG } from '../../../config'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { RequestService } from '../../core/data/request.service'; import { ResponseCacheService } from '../../core/cache/response-cache.service'; import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; +import { RouterStub } from '../../shared/testing/router-stub'; @Component({ template: '' }) -class DummyComponent { } +class DummyComponent { +} describe('SearchService', () => { - let searchService: SearchService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - CommonModule, - RouterTestingModule.withRoutes([ - { path: 'search', component: DummyComponent, pathMatch: 'full' }, - ]) - ], - declarations: [ - DummyComponent - ], - providers: [ - { provide: ItemDataService, useValue: {} }, - { provide: RouteService, useValue: {} }, - { provide: ResponseCacheService, useValue: {} }, - { provide: RequestService, useValue: {} }, - { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, - { provide: RemoteDataBuildService, useValue: {} }, - { provide: GLOBAL_CONFIG, useValue: {} }, - SearchService - ], + describe('By default', () => { + let searchService: SearchService; + const router = new RouterStub(); + const route = new ActivatedRouteStub(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + RouterTestingModule.withRoutes([ + { path: 'search', component: DummyComponent, pathMatch: 'full' }, + ]) + ], + declarations: [ + DummyComponent + ], + providers: [ + { provide: ItemDataService, useValue: {} }, + { provide: RouteService, useValue: {} }, + { provide: ResponseCacheService, useValue: {} }, + { provide: RequestService, useValue: {} }, + { provide: ActivatedRoute, useValue: route }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: GLOBAL_CONFIG, useValue: {} }, + { provide: Router, useValue: router }, + SearchService + ], + }); + searchService = TestBed.get(SearchService); }); - searchService = TestBed.get(SearchService); - }); - it('should return list view mode by default', () => { - searchService.getViewMode().subscribe((viewMode) => { - expect(viewMode).toBe(ViewMode.List); + it('should return list view mode', () => { + searchService.getViewMode().subscribe((viewMode) => { + expect(viewMode).toBe(ViewMode.List); + }); }); }); + describe('', () => { + let searchService: SearchService; + const router = new RouterStub(); + const route = new ActivatedRouteStub(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + RouterTestingModule.withRoutes([ + { path: 'search', component: DummyComponent, pathMatch: 'full' }, + ]) + ], + declarations: [ + DummyComponent + ], + providers: [ + { provide: ItemDataService, useValue: {} }, + { provide: RouteService, useValue: {} }, + { provide: ResponseCacheService, useValue: {} }, + { provide: RequestService, useValue: {} }, + { provide: ActivatedRoute, useValue: route }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: GLOBAL_CONFIG, useValue: {} }, + { provide: Router, useValue: router }, + SearchService + ], + }); + searchService = TestBed.get(SearchService); + }); - it('should return the view mode set through setViewMode', fakeAsync(() => { - searchService.setViewMode(ViewMode.Grid) - tick(); - let viewMode = ViewMode.List; - searchService.getViewMode().subscribe((mode) => viewMode = mode); - expect(viewMode).toBe(ViewMode.Grid); + it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => { + searchService.setViewMode(ViewMode.List); + expect(router.navigate).toHaveBeenCalledWith(['/search'], { + queryParams: { view: ViewMode.List }, + queryParamsHandling: 'merge' + }); + }); - searchService.setViewMode(ViewMode.List) - tick(); - searchService.getViewMode().subscribe((mode) => viewMode = mode); - expect(viewMode).toBe(ViewMode.List); - })); + it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => { + searchService.setViewMode(ViewMode.Grid); + expect(router.navigate).toHaveBeenCalledWith(['/search'], { + queryParams: { view: ViewMode.Grid }, + queryParamsHandling: 'merge' + }); + }); + it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => { + let viewMode = ViewMode.Grid; + route.testParams = { view: ViewMode.List }; + searchService.getViewMode().subscribe((mode) => viewMode = mode); + expect(viewMode).toEqual(ViewMode.List); + }); + + it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => { + let viewMode = ViewMode.List; + route.testParams = { view: ViewMode.Grid }; + searchService.getViewMode().subscribe((mode) => viewMode = mode); + expect(viewMode).toEqual(ViewMode.Grid); + }); + }); }); diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 2dac0bd340..66e125bc58 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -88,13 +88,13 @@ export class SearchService extends HALEndpointService implements OnDestroy { // searchOptions: BehaviorSubject; searchOptions: SearchOptions; - constructor(protected responseCache: ResponseCacheService, + constructor(private router: Router, + private route: ActivatedRoute, + protected responseCache: ResponseCacheService, protected requestService: RequestService, @Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, private routeService: RouteService, - private route: ActivatedRoute, - private rdb: RemoteDataBuildService, - private router: Router) { + private rdb: RemoteDataBuildService,) { super(); const pagination: PaginationComponentOptions = new PaginationComponentOptions(); pagination.id = 'search-results-pagination'; @@ -228,9 +228,10 @@ export class SearchService extends HALEndpointService implements OnDestroy { const value = searchFilterConfigName + ' ' + (i + 1); if (!selectedValues.includes(value)) { payload.push({ - value: value, - count: Math.floor(Math.random() * 20) + 20 * (totalFilters - i), // make sure first results have the highest (random) count - search: (decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value)} + value: value, + count: Math.floor(Math.random() * 20) + 20 * (totalFilters - i), // make sure first results have the highest (random) count + search: (decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value) + } ); } } @@ -271,12 +272,12 @@ export class SearchService extends HALEndpointService implements OnDestroy { getClearFiltersQueryParams(): any { const params = {}; this.sub = this.route.queryParamMap - .subscribe((map) => { - map.keys + .subscribe((pmap) => { + pmap.keys .filter((key) => this.config .findIndex((conf: SearchFilterConfig) => conf.paramName === key) < 0) .forEach((key) => { - params[key] = map.get(key); + params[key] = pmap.get(key); }) }); return params; diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index e9d200e15d..21df69b3a2 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -154,13 +154,6 @@ export class EndpointMapRequest extends GetRequest { } } -export class RootEndpointRequest extends EndpointMapRequest { - constructor(uuid: string, EnvConfig: GlobalConfig) { - const href = new RESTURLCombiner(EnvConfig, '/').toString(); - super(uuid, href); - } -} - export class BrowseEndpointRequest extends GetRequest { constructor(uuid: string, href: string) { super(uuid, href); diff --git a/src/app/core/shared/hal-endpoint.service.spec.ts b/src/app/core/shared/hal-endpoint.service.spec.ts index 39652d6e7c..7c73e15fab 100644 --- a/src/app/core/shared/hal-endpoint.service.spec.ts +++ b/src/app/core/shared/hal-endpoint.service.spec.ts @@ -2,9 +2,9 @@ import { cold, hot } from 'jasmine-marbles'; import { GlobalConfig } from '../../../config/global-config.interface'; import { getMockRequestService } from '../../shared/mocks/mock-request.service'; import { ResponseCacheService } from '../cache/response-cache.service'; -import { RootEndpointRequest } from '../data/request.models'; import { RequestService } from '../data/request.service'; import { HALEndpointService } from './hal-endpoint.service'; +import { EndpointMapRequest } from '../data/request.models'; describe('HALEndpointService', () => { let service: HALEndpointService; @@ -32,7 +32,7 @@ describe('HALEndpointService', () => { describe('getRootEndpointMap', () => { beforeEach(() => { responseCache = jasmine.createSpyObj('responseCache', { - get: hot('--a-', { + get: hot('a-', { a: { response: { endpointMap: endpointMap } } @@ -52,45 +52,51 @@ describe('HALEndpointService', () => { ); }); - it('should configure a new RootEndpointRequest', () => { + it('should configure a new EndpointMapRequest', () => { (service as any).getRootEndpointMap(); - const expected = new RootEndpointRequest(requestService.generateRequestId(), envConfig); + const expected = new EndpointMapRequest(requestService.generateRequestId(), envConfig.rest.baseUrl); expect(requestService.configure).toHaveBeenCalledWith(expected); }); it('should return an Observable of the endpoint map', () => { const result = (service as any).getRootEndpointMap(); - const expected = cold('--b-', { b: endpointMap }); + const expected = cold('b-', { b: endpointMap }); expect(result).toBeObservable(expected); }); }); describe('getEndpoint', () => { + beforeEach(() => { + envConfig = { + rest: { baseUrl: 'https://rest.api/' } + } as any; + service = new TestService( responseCache, requestService, envConfig ); - - spyOn(service as any, 'getRootEndpointMap').and - .returnValue(hot('--a-', { a: endpointMap })); }); it('should return the endpoint URL for the service\'s linkPath', () => { + spyOn(service as any, 'getEndpointAt').and + .returnValue(hot('a-', { a: 'https://rest.api/test' })); const result = service.getEndpoint(); - const expected = cold('--b-', { b: endpointMap.test }); + + const expected = cold('b-', { b: endpointMap.test }); expect(result).toBeObservable(expected); }); it('should return undefined for a linkPath that isn\'t in the endpoint map', () => { (service as any).linkPath = 'unknown'; + spyOn(service as any, 'getEndpointAt').and + .returnValue(hot('a-', { a: undefined })); const result = service.getEndpoint(); - const expected = cold('--b-', { b: undefined }); + const expected = cold('b-', { b: undefined }); expect(result).toBeObservable(expected); }); - }); describe('isEnabledOnRestApi', () => { @@ -127,7 +133,7 @@ describe('HALEndpointService', () => { (service as any).linkPath = 'unknown'; const result = service.isEnabledOnRestApi(); - const expected = cold('b-c-', { b: undefined, c: false }); + const expected = cold('b-c-', { b: undefined, c: false }); expect(result).toBeObservable(expected); }); diff --git a/src/app/core/shared/hal-endpoint.service.ts b/src/app/core/shared/hal-endpoint.service.ts index c6c3ff284d..743179b23c 100644 --- a/src/app/core/shared/hal-endpoint.service.ts +++ b/src/app/core/shared/hal-endpoint.service.ts @@ -4,7 +4,7 @@ import { RequestService } from '../data/request.service'; import { ResponseCacheService } from '../cache/response-cache.service'; import { GlobalConfig } from '../../../config/global-config.interface'; import { EndpointMap, EndpointMapSuccessResponse } from '../cache/response-cache.models'; -import { EndpointMapRequest, RootEndpointRequest } from '../data/request.models'; +import { EndpointMapRequest } from '../data/request.models'; import { ResponseCacheEntry } from '../cache/response-cache.reducer'; import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts index 5d814ac7e5..7f8a3bb9fd 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts @@ -1,8 +1,6 @@ import { Component } from '@angular/core'; import { renderElementsFor} from '../../../object-collection/shared/dso-element-decorator'; - - import { SearchResultGridElementComponent } from '../search-result-grid-element.component'; import { Collection } from '../../../../core/shared/collection.model'; import { ViewMode } from '../../../../+search-page/search-options.model'; diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts index f011e49eda..7c34207f5e 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts @@ -1,6 +1,4 @@ import { Component } from '@angular/core'; - - import { Community } from '../../../../core/shared/community.model'; import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator'; import { SearchResultGridElementComponent } from '../search-result-grid-element.component'; diff --git a/src/app/shared/testing/active-router-stub.ts b/src/app/shared/testing/active-router-stub.ts index 9959f38292..35c966d72a 100644 --- a/src/app/shared/testing/active-router-stub.ts +++ b/src/app/shared/testing/active-router-stub.ts @@ -1,16 +1,16 @@ -import { Params } from '@angular/router'; +import { convertToParamMap, ParamMap, Params } from '@angular/router'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; export class ActivatedRouteStub { private _testParams?: any; - // ActivatedRoute.params is Observable private subject?: BehaviorSubject = new BehaviorSubject(this.testParams); params = this.subject.asObservable(); queryParams = this.subject.asObservable(); + queryParamMap = this.subject.asObservable().map((params: Params) => convertToParamMap(params)); constructor(params?: Params) { if (params) { diff --git a/src/app/shared/testing/search-service-stub.ts b/src/app/shared/testing/search-service-stub.ts new file mode 100644 index 0000000000..23b2004827 --- /dev/null +++ b/src/app/shared/testing/search-service-stub.ts @@ -0,0 +1,32 @@ +import { Observable } from 'rxjs/Observable'; +import { ViewMode } from '../../+search-page/search-options.model'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +export class SearchServiceStub { + + private _viewMode: ViewMode; + private subject?: BehaviorSubject = new BehaviorSubject(this.testViewMode); + + viewMode = this.subject.asObservable(); + + constructor() { + this.setViewMode(ViewMode.List); + } + + getViewMode(): Observable { + return this.viewMode; + } + + setViewMode(viewMode: ViewMode) { + this.testViewMode = viewMode; + } + + get testViewMode(): ViewMode { + return this._viewMode; + } + + set testViewMode(viewMode: ViewMode) { + this._viewMode = viewMode; + this.subject.next(viewMode); + } +} diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts index 3923fec0a6..1b4bf6da46 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts @@ -4,19 +4,12 @@ import { By } from '@angular/platform-browser'; import { MockTranslateLoader } from '../mocks/mock-translate-loader'; import { RouterTestingModule } from '@angular/router/testing'; -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { SearchService } from '../../+search-page/search-service/search.service'; -import { ItemDataService } from './../../core/data/item-data.service'; import { ViewModeSwitchComponent } from './view-mode-switch.component'; import { ViewMode } from '../../+search-page/search-options.model'; -import { RouteService } from '../route.service'; -import { ResponseCacheService } from '../../core/cache/response-cache.service'; -import { RequestService } from '../../core/data/request.service'; -import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; -import { ActivatedRoute } from '@angular/router'; -import { GLOBAL_CONFIG } from '../../../config'; -import { ActivatedRouteStub } from '../testing/active-router-stub'; +import { SearchServiceStub } from '../testing/search-service-stub'; @Component({ template: '' }) class DummyComponent { } @@ -24,10 +17,9 @@ class DummyComponent { } describe('ViewModeSwitchComponent', () => { let comp: ViewModeSwitchComponent; let fixture: ComponentFixture; - let searchService: SearchService; + const searchService = new SearchServiceStub(); let listButton: HTMLElement; let gridButton: HTMLElement; - let route = new ActivatedRouteStub(); beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ @@ -46,15 +38,10 @@ describe('ViewModeSwitchComponent', () => { DummyComponent ], providers: [ - { provide: ItemDataService, useValue: {} }, - { provide: RouteService, useValue: {} }, - { provide: ResponseCacheService, useValue: {} }, - { provide: RequestService, useValue: {} }, - { provide: ActivatedRoute, useValue: route }, - { provide: RemoteDataBuildService, useValue: {} }, - { provide: GLOBAL_CONFIG, useValue: {} }, - SearchService + { provide: SearchService, useValue: searchService }, ], + }).overrideComponent(ViewModeSwitchComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); @@ -65,12 +52,10 @@ describe('ViewModeSwitchComponent', () => { const debugElements = fixture.debugElement.queryAll(By.css('a')); listButton = debugElements[0].nativeElement; gridButton = debugElements[1].nativeElement; - searchService = fixture.debugElement.injector.get(SearchService); }); it('should set list button as active when on list mode', fakeAsync(() => { searchService.setViewMode(ViewMode.List); - route = new ActivatedRouteStub([{view: ViewMode.List}]) tick(); fixture.detectChanges(); expect(comp.currentMode).toBe(ViewMode.List); @@ -80,7 +65,6 @@ describe('ViewModeSwitchComponent', () => { it('should set grid button as active when on grid mode', fakeAsync(() => { searchService.setViewMode(ViewMode.Grid); - route = new ActivatedRouteStub([{view: ViewMode.Grid}]) tick(); fixture.detectChanges(); expect(comp.currentMode).toBe(ViewMode.Grid);