added tests and docs

This commit is contained in:
lotte
2020-02-26 16:53:10 +01:00
parent 4ae8997ada
commit b554d40e9c
15 changed files with 152 additions and 28 deletions

View File

@@ -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(),

View File

@@ -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<Breadcrumb[]> {
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;

View File

@@ -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<T> {
/**
* 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<Breadcrumb[]>;
}

View File

@@ -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<Collection> {

View File

@@ -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<Community> {

View File

@@ -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<Collection>;
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})
});
});
});

View File

@@ -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<T extends ChildHALResource & DSpaceObject> implements Resolve<BreadcrumbConfig<T>> {
@@ -18,7 +18,7 @@ export class DSOBreadcrumbResolver<T extends ChildHALResource & DSpaceObject> 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

View File

@@ -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
}
});

View File

@@ -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<ChildHALResource & DSpaceObject> {
constructor(
@@ -21,6 +24,12 @@ export class DSOBreadcrumbsService implements BreadcrumbsService<ChildHALResourc
}
/**
* Method to recursively calculate the breadcrumbs
* This method returns the name and url of the key and all its parent DSO's recursively, top down
* @param key The key (a DSpaceObject) used to resolve the breadcrumb
* @param url The url to use as a link for this breadcrumb
*/
getBreadcrumbs(key: ChildHALResource & DSpaceObject, url: string): Observable<Breadcrumb[]> {
const label = this.dsoNameService.getName(key);
const crumb = new Breadcrumb(label, url);

View File

@@ -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();
});
});
});

View File

@@ -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<BreadcrumbConfig<string>> {

View File

@@ -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;

View File

@@ -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<string> {
/**
* 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<Breadcrumb[]> {
return observableOf([new Breadcrumb(key + BREADCRUMB_MESSAGE_POSTFIX, url)]);
}

View File

@@ -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<Item> {

View File

@@ -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<OrgUnitItemMetadataListElementComponent>;