diff --git a/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts b/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts index 8a07f5d235..19bdd2951e 100644 --- a/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts +++ b/src/app/+collection-page/collection-item-mapper/collection-item-mapper.component.spec.ts @@ -83,7 +83,7 @@ describe('CollectionItemMapperComponent', () => { const itemDataServiceStub = { mapToCollection: () => of(new RestResponse(true, 200, 'OK')) }; - const activatedRouteStub = new ActivatedRouteStub({}, { collection: mockCollectionRD }); + const activatedRouteStub = new ActivatedRouteStub({}, { dso: mockCollectionRD }); const translateServiceStub = { get: () => of('test-message of collection ' + mockCollection.name), onLangChange: new EventEmitter(), diff --git a/src/app/breadcrumbs/breadcrumbs.component.ts b/src/app/breadcrumbs/breadcrumbs.component.ts index 19b77fe59c..861fda844b 100644 --- a/src/app/breadcrumbs/breadcrumbs.component.ts +++ b/src/app/breadcrumbs/breadcrumbs.component.ts @@ -5,14 +5,28 @@ import { hasNoValue, hasValue, isNotUndefined } from '../shared/empty.util'; import { filter, map, switchMap, tap } from 'rxjs/operators'; import { combineLatest, Observable, Subscription, of as observableOf } from 'rxjs'; +/** + * Component representing the breadcrumbs of a page + */ @Component({ selector: 'ds-breadcrumbs', templateUrl: './breadcrumbs.component.html', styleUrls: ['./breadcrumbs.component.scss'] }) export class BreadcrumbsComponent implements OnInit, OnDestroy { + /** + * List of breadcrumbs for this page + */ breadcrumbs: Breadcrumb[]; + + /** + * Whether or not to show breadcrumbs on this page + */ showBreadcrumbs: boolean; + + /** + * Subscription to unsubscribe from on destroy + */ subscription: Subscription; constructor( @@ -21,6 +35,9 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy { ) { } + /** + * Sets the breadcrumbs on init for this page + */ ngOnInit(): void { this.subscription = this.router.events.pipe( filter((e): e is NavigationEnd => e instanceof NavigationEnd), @@ -32,6 +49,10 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy { ) } + /** + * Method that recursively resolves breadcrumbs + * @param route The route to get the breadcrumb from + */ resolveBreadcrumbs(route: ActivatedRoute): Observable { const data = route.snapshot.data; const routeConfig = route.snapshot.routeConfig; @@ -56,12 +77,18 @@ export class BreadcrumbsComponent implements OnInit, OnDestroy { return !last ? this.resolveBreadcrumbs(route.firstChild) : observableOf([]); } + /** + * Unsubscribe from subscription + */ ngOnDestroy(): void { if (hasValue(this.subscription)) { this.subscription.unsubscribe(); } } + /** + * Resets the state of the breadcrumbs + */ reset() { this.breadcrumbs = []; this.showBreadcrumbs = true; diff --git a/src/app/core/breadcrumbs/breadcrumbs.service.ts b/src/app/core/breadcrumbs/breadcrumbs.service.ts index 30c6c44cf7..f274485d5d 100644 --- a/src/app/core/breadcrumbs/breadcrumbs.service.ts +++ b/src/app/core/breadcrumbs/breadcrumbs.service.ts @@ -1,6 +1,15 @@ import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; import { Observable } from 'rxjs'; +/** + * Service to calculate breadcrumbs for a single part of the route + */ export interface BreadcrumbsService { + + /** + * Method to calculate the breadcrumbs for a part of the route + * @param key The key used to resolve the breadcrumb + * @param url The url to use as a link for this breadcrumb + */ getBreadcrumbs(key: T, url: string): Observable; } diff --git a/src/app/core/breadcrumbs/collection-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/collection-breadcrumb.resolver.ts index 78f90f149a..c662ead129 100644 --- a/src/app/core/breadcrumbs/collection-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/collection-breadcrumb.resolver.ts @@ -5,7 +5,7 @@ import { Collection } from '../shared/collection.model'; import { CollectionDataService } from '../data/collection-data.service'; /** - * The class that resolve the BreadcrumbConfig object for a route + * The class that resolves the BreadcrumbConfig object for a Collection */ @Injectable() export class CollectionBreadcrumbResolver extends DSOBreadcrumbResolver { diff --git a/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts index 3080823b97..1e4959f9e5 100644 --- a/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/community-breadcrumb.resolver.ts @@ -5,7 +5,7 @@ import { CommunityDataService } from '../data/community-data.service'; import { Community } from '../shared/community.model'; /** - * The class that resolve the BreadcrumbConfig object for a route + * The class that resolves the BreadcrumbConfig object for a Community */ @Injectable() export class CommunityBreadcrumbResolver extends DSOBreadcrumbResolver { diff --git a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts new file mode 100644 index 0000000000..774fcd04d5 --- /dev/null +++ b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.spec.ts @@ -0,0 +1,34 @@ +import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver'; +import { Collection } from '../shared/collection.model'; +import { createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils'; +import { getTestScheduler } from 'jasmine-marbles'; + +describe('DSOBreadcrumbResolver', () => { + describe('resolve', () => { + let resolver: DSOBreadcrumbResolver; + let collectionService: any; + let dsoBreadcrumbService: any; + let testCollection: Collection; + let uuid; + let breadcrumbUrl; + let currentUrl; + + beforeEach(() => { + uuid = '1234-65487-12354-1235'; + breadcrumbUrl = '/collections/' + uuid; + currentUrl = breadcrumbUrl + '/edit'; + testCollection = Object.assign(new Collection(), { uuid }); + dsoBreadcrumbService = {}; + collectionService = { + findById: (id: string) => createSuccessfulRemoteDataObject$(testCollection) + }; + resolver = new DSOBreadcrumbResolver(dsoBreadcrumbService, collectionService); + }); + + it('should resolve a breadcrumb config for the correct DSO', () => { + const resolvedConfig = resolver.resolve({ params: { id: uuid } } as any, { url: currentUrl } as any); + const expectedConfig = { provider: dsoBreadcrumbService, key: testCollection, url: breadcrumbUrl }; + getTestScheduler().expectObservable(resolvedConfig).toBe('(a|)', { a: expectedConfig}) + }); + }); +}); diff --git a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts index 2f4138d144..78ba349a5c 100644 --- a/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/dso-breadcrumb.resolver.ts @@ -10,7 +10,7 @@ import { DSpaceObject } from '../shared/dspace-object.model'; import { ChildHALResource } from '../shared/child-hal-resource.model'; /** - * The class that resolve the BreadcrumbConfig object for a route + * The class that resolves the BreadcrumbConfig object for a DSpaceObject */ @Injectable() export class DSOBreadcrumbResolver implements Resolve> { @@ -18,7 +18,7 @@ export class DSOBreadcrumbResolver im } /** - * Method for resolving a site object + * Method for resolving a breadcrumb config object * @param {ActivatedRouteSnapshot} route The current ActivatedRouteSnapshot * @param {RouterStateSnapshot} state The current RouterStateSnapshot * @returns BreadcrumbConfig object diff --git a/src/app/core/breadcrumbs/dso-breadcrumbs.service.spec.ts b/src/app/core/breadcrumbs/dso-breadcrumbs.service.spec.ts index dc6195071a..101545cb14 100644 --- a/src/app/core/breadcrumbs/dso-breadcrumbs.service.spec.ts +++ b/src/app/core/breadcrumbs/dso-breadcrumbs.service.spec.ts @@ -5,20 +5,15 @@ import { LinkService } from '../cache/builders/link.service'; import { Item } from '../shared/item.model'; import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/testing/utils'; import { DSpaceObject } from '../shared/dspace-object.model'; -import { map } from 'rxjs/operators'; import { of as observableOf } from 'rxjs'; -import { RemoteData } from '../data/remote-data'; -import { hasValue } from '../../shared/empty.util'; import { Community } from '../shared/community.model'; import { Collection } from '../shared/collection.model'; import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; -import { getItemPageRoute } from '../../+item-page/item-page-routing.module'; -import { getCommunityPageRoute } from '../../+community-page/community-page-routing.module'; -import { getCollectionPageRoute } from '../../+collection-page/collection-page-routing.module'; -import { cold, getTestScheduler } from 'jasmine-marbles'; +import { getTestScheduler } from 'jasmine-marbles'; import { getDSOPath } from '../../app-routing.module'; +import { DSONameService } from './dso-name.service'; -fdescribe('DSOBreadcrumbsService', () => { +describe('DSOBreadcrumbsService', () => { let service: DSOBreadcrumbsService; let linkService: any; let testItem; @@ -33,7 +28,7 @@ fdescribe('DSOBreadcrumbsService', () => { let collectionUUID; let communityUUID; - let objects: DSpaceObject[]; + let dsoNameService; function init() { itemPath = '/items/'; @@ -47,7 +42,9 @@ fdescribe('DSOBreadcrumbsService', () => { testCommunity = Object.assign(new Community(), { type: 'community', - name: 'community', + metadata: { + 'dc.title': [{value: 'community'}] + }, uuid: communityUUID, parentCommunity: observableOf(Object.assign(createSuccessfulRemoteDataObject(undefined), { statusCode: 204 })), @@ -61,7 +58,9 @@ fdescribe('DSOBreadcrumbsService', () => { testCollection = Object.assign(new Collection(), { type: 'collection', - name: 'collection', + metadata: { + 'dc.title': [{value: 'collection'}] + }, uuid: collectionUUID, parentCommunity: createSuccessfulRemoteDataObject$(testCommunity), _links: { @@ -74,7 +73,9 @@ fdescribe('DSOBreadcrumbsService', () => { testItem = Object.assign(new Item(), { type: 'item', - name: 'item', + metadata: { + 'dc.title': [{value: 'item'}] + }, uuid: itemUUID, owningCollection: createSuccessfulRemoteDataObject$(testCollection), _links: { @@ -84,15 +85,15 @@ fdescribe('DSOBreadcrumbsService', () => { } ); - objects = [testItem, testCollection, testCommunity]; - + dsoNameService = { getName: (dso) => getName(dso) } } beforeEach(async(() => { init(); TestBed.configureTestingModule({ providers: [ - { provide: LinkService, useValue: getMockLinkService() } + { provide: LinkService, useValue: getMockLinkService() }, + { provide: DSONameService, useValue: dsoNameService } ] }).compileComponents(); })); @@ -100,18 +101,22 @@ fdescribe('DSOBreadcrumbsService', () => { beforeEach(() => { linkService = TestBed.get(LinkService); linkService.resolveLink.and.callFake((object, link) => object); - service = new DSOBreadcrumbsService(linkService); + service = new DSOBreadcrumbsService(linkService, dsoNameService); }); describe('getBreadcrumbs', () => { it('should return the breadcrumbs based on an Item', () => { const breadcrumbs = service.getBreadcrumbs(testItem, testItem._links.self); const expectedCrumbs = [ - new Breadcrumb(testCommunity.name, getDSOPath(testCommunity)), - new Breadcrumb(testCollection.name, getDSOPath(testCollection)), - new Breadcrumb(testItem.name, getDSOPath(testItem)), + new Breadcrumb(getName(testCommunity), getDSOPath(testCommunity)), + new Breadcrumb(getName(testCollection), getDSOPath(testCollection)), + new Breadcrumb(getName(testItem), getDSOPath(testItem)), ]; getTestScheduler().expectObservable(breadcrumbs).toBe('(a|)', { a: expectedCrumbs }); }) }); + + function getName(dso: DSpaceObject): string { + return dso.metadata['dc.title'][0].value + } }); diff --git a/src/app/core/breadcrumbs/dso-breadcrumbs.service.ts b/src/app/core/breadcrumbs/dso-breadcrumbs.service.ts index e4a22f1bf5..3cb73be876 100644 --- a/src/app/core/breadcrumbs/dso-breadcrumbs.service.ts +++ b/src/app/core/breadcrumbs/dso-breadcrumbs.service.ts @@ -12,6 +12,9 @@ import { RemoteData } from '../data/remote-data'; import { hasValue } from '../../shared/empty.util'; import { Injectable } from '@angular/core'; +/** + * Service to calculate DSpaceObject breadcrumbs for a single part of the route + */ @Injectable() export class DSOBreadcrumbsService implements BreadcrumbsService { constructor( @@ -21,6 +24,12 @@ export class DSOBreadcrumbsService implements BreadcrumbsService { const label = this.dsoNameService.getName(key); const crumb = new Breadcrumb(label, url); diff --git a/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.spec.ts b/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.spec.ts new file mode 100644 index 0000000000..d34d6d8a9b --- /dev/null +++ b/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.spec.ts @@ -0,0 +1,28 @@ +import { I18nBreadcrumbResolver } from './i18n-breadcrumb.resolver'; + +describe('I18nBreadcrumbResolver', () => { + describe('resolve', () => { + let resolver: I18nBreadcrumbResolver; + let i18nBreadcrumbService: any; + let i18nKey: string; + let path: string; + beforeEach(() => { + i18nKey = 'example.key'; + path = 'rest.com/path/to/breadcrumb'; + i18nBreadcrumbService = {}; + resolver = new I18nBreadcrumbResolver(i18nBreadcrumbService); + }); + + it('should resolve the breadcrumb config', () => { + const resolvedConfig = resolver.resolve({ data: { breadcrumbKey: i18nKey }, url: [path] } as any, {} as any); + const expectedConfig = { provider: i18nBreadcrumbService, key: i18nKey, url: path }; + expect(resolvedConfig).toEqual(expectedConfig); + }); + + it('should resolve throw an error when no breadcrumbKey is defined', () => { + expect(() => { + resolver.resolve({ data: {} } as any, undefined) + }).toThrow(); + }); + }); +}); diff --git a/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.ts index 800a7b75d0..0978648ba3 100644 --- a/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/i18n-breadcrumb.resolver.ts @@ -5,7 +5,7 @@ import { I18nBreadcrumbsService } from './i18n-breadcrumbs.service'; import { hasNoValue } from '../../shared/empty.util'; /** - * The class that resolve the BreadcrumbConfig object for a route + * The class that resolves a BreadcrumbConfig object with an i18n key string for a route */ @Injectable() export class I18nBreadcrumbResolver implements Resolve> { diff --git a/src/app/core/breadcrumbs/i18n-breadcrumbs.service.spec.ts b/src/app/core/breadcrumbs/i18n-breadcrumbs.service.spec.ts index f29907312b..274389db3b 100644 --- a/src/app/core/breadcrumbs/i18n-breadcrumbs.service.spec.ts +++ b/src/app/core/breadcrumbs/i18n-breadcrumbs.service.spec.ts @@ -3,7 +3,7 @@ import { Breadcrumb } from '../../breadcrumbs/breadcrumb/breadcrumb.model'; import { getTestScheduler } from 'jasmine-marbles'; import { BREADCRUMB_MESSAGE_POSTFIX, I18nBreadcrumbsService } from './i18n-breadcrumbs.service'; -fdescribe('I18nBreadcrumbsService', () => { +describe('I18nBreadcrumbsService', () => { let service: I18nBreadcrumbsService; let exampleString; let exampleURL; diff --git a/src/app/core/breadcrumbs/i18n-breadcrumbs.service.ts b/src/app/core/breadcrumbs/i18n-breadcrumbs.service.ts index 97ff89479a..e07d9ed541 100644 --- a/src/app/core/breadcrumbs/i18n-breadcrumbs.service.ts +++ b/src/app/core/breadcrumbs/i18n-breadcrumbs.service.ts @@ -3,10 +3,22 @@ import { BreadcrumbsService } from './breadcrumbs.service'; import { Observable, of as observableOf } from 'rxjs'; import { Injectable } from '@angular/core'; +/** + * The postfix for i18n breadcrumbs + */ export const BREADCRUMB_MESSAGE_POSTFIX = '.breadcrumbs'; +/** + * Service to calculate i18n breadcrumbs for a single part of the route + */ @Injectable() export class I18nBreadcrumbsService implements BreadcrumbsService { + + /** + * Method to calculate the breadcrumbs + * @param key The key used to resolve the breadcrumb + * @param url The url to use as a link for this breadcrumb + */ getBreadcrumbs(key: string, url: string): Observable { return observableOf([new Breadcrumb(key + BREADCRUMB_MESSAGE_POSTFIX, url)]); } diff --git a/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts b/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts index fdfd3c11f9..c447f7de2a 100644 --- a/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts +++ b/src/app/core/breadcrumbs/item-breadcrumb.resolver.ts @@ -5,7 +5,7 @@ import { Item } from '../shared/item.model'; import { DSOBreadcrumbResolver } from './dso-breadcrumb.resolver'; /** - * The class that resolve the BreadcrumbConfig object for a route + * The class that resolves the BreadcrumbConfig object for an Item */ @Injectable() export class ItemBreadcrumbResolver extends DSOBreadcrumbResolver { diff --git a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts index 37cbe47c72..0354469372 100644 --- a/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts +++ b/src/app/entity-groups/research-entities/metadata-representations/org-unit/org-unit-item-metadata-list-element.component.spec.ts @@ -13,7 +13,7 @@ const mockItem = Object.assign(new Item(), { metadata: { 'dc.description': [{ va const virtMD = Object.assign(new MetadataValue(), { value: organisation }); const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(virtMD), mockItem); -describe('OrgUnitItemMetadataListElementComponent', () => { +fdescribe('OrgUnitItemMetadataListElementComponent', () => { let comp: OrgUnitItemMetadataListElementComponent; let fixture: ComponentFixture;