diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts index 0143377922..efb4a4a9f4 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-decorator.ts @@ -1,4 +1,6 @@ import { hasNoValue } from '../../shared/empty.util'; +import { InjectionToken } from '@angular/core'; +import { GenericConstructor } from '../../core/shared/generic-constructor'; export enum BrowseByType { Title = 'title', @@ -8,6 +10,11 @@ export enum BrowseByType { export const DEFAULT_BROWSE_BY_TYPE = BrowseByType.Metadata; +export const BROWSE_BY_COMPONENT_FACTORY = new InjectionToken<(browseByType) => GenericConstructor>('getComponentByBrowseByType', { + providedIn: 'root', + factory: () => getComponentByBrowseByType +}); + const map = new Map(); /** diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts index 545b766c88..f340237e26 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.spec.ts @@ -2,12 +2,11 @@ import { BrowseBySwitcherComponent } from './browse-by-switcher.component'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import * as decorator from './browse-by-decorator'; import { BehaviorSubject } from 'rxjs'; import { environment } from '../../../environments/environment'; -import createSpy = jasmine.createSpy; +import { BROWSE_BY_COMPONENT_FACTORY } from './browse-by-decorator'; -xdescribe('BrowseBySwitcherComponent', () => { +describe('BrowseBySwitcherComponent', () => { let comp: BrowseBySwitcherComponent; let fixture: ComponentFixture; @@ -23,7 +22,8 @@ xdescribe('BrowseBySwitcherComponent', () => { TestBed.configureTestingModule({ declarations: [BrowseBySwitcherComponent], providers: [ - { provide: ActivatedRoute, useValue: activatedRouteStub } + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: BROWSE_BY_COMPONENT_FACTORY, useValue: jasmine.createSpy('getComponentByBrowseByType').and.returnValue(null) } ], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); @@ -32,7 +32,6 @@ xdescribe('BrowseBySwitcherComponent', () => { beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(BrowseBySwitcherComponent); comp = fixture.componentInstance; - spyOnProperty(decorator, 'getComponentByBrowseByType').and.returnValue(createSpy('getComponentByItemType')); })); types.forEach((type) => { @@ -43,7 +42,7 @@ xdescribe('BrowseBySwitcherComponent', () => { }); it(`should call getComponentByBrowseByType with type "${type.type}"`, () => { - expect(decorator.getComponentByBrowseByType).toHaveBeenCalledWith(type.type); + expect((comp as any).getComponentByBrowseByType).toHaveBeenCalledWith(type.type); }); }); }); diff --git a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts index 6cfeebf796..043a4ce90a 100644 --- a/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts +++ b/src/app/+browse-by/+browse-by-switcher/browse-by-switcher.component.ts @@ -1,10 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { BrowseByTypeConfig } from '../../../config/browse-by-type-config.interface'; import { map } from 'rxjs/operators'; -import { getComponentByBrowseByType } from './browse-by-decorator'; +import { BROWSE_BY_COMPONENT_FACTORY } from './browse-by-decorator'; import { environment } from '../../../environments/environment'; +import { GenericConstructor } from '../../core/shared/generic-constructor'; @Component({ selector: 'ds-browse-by-switcher', @@ -20,7 +21,8 @@ export class BrowseBySwitcherComponent implements OnInit { */ browseByComponent: Observable; - public constructor(protected route: ActivatedRoute) { + public constructor(protected route: ActivatedRoute, + @Inject(BROWSE_BY_COMPONENT_FACTORY) private getComponentByBrowseByType: (browseByType) => GenericConstructor) { } /** @@ -32,7 +34,7 @@ export class BrowseBySwitcherComponent implements OnInit { const id = params.id; return environment.browseBy.types.find((config: BrowseByTypeConfig) => config.id === id); }), - map((config: BrowseByTypeConfig) => getComponentByBrowseByType(config.type)) + map((config: BrowseByTypeConfig) => this.getComponentByBrowseByType(config.type)) ); } diff --git a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts index a8e2136131..b4c3da2cdc 100644 --- a/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts +++ b/src/app/+item-page/simple/item-types/shared/item-relationships-utils.ts @@ -9,6 +9,12 @@ import { getFirstSucceededRemoteData } from '../../../../core/shared/operators'; import { hasValue } from '../../../../shared/empty.util'; +import { InjectionToken } from '@angular/core'; + +export const PAGINATED_RELATIONS_TO_ITEMS_OPERATOR = new InjectionToken<(thisId: string) => (source: Observable>>) => Observable>>>('paginatedRelationsToItems', { + providedIn: 'root', + factory: () => paginatedRelationsToItems +}); /** * Operator for comparing arrays using a mapping function diff --git a/src/app/core/cache/builders/build-decorators.ts b/src/app/core/cache/builders/build-decorators.ts index 1ea09b6684..b561ababde 100644 --- a/src/app/core/cache/builders/build-decorators.ts +++ b/src/app/core/cache/builders/build-decorators.ts @@ -8,6 +8,20 @@ import { TypedObject, getResourceTypeValueFor } from '../object-cache.reducer'; +import { InjectionToken } from '@angular/core'; + +export const DATA_SERVICE_FACTORY = new InjectionToken<(resourceType: ResourceType) => GenericConstructor>('getDataServiceFor', { + providedIn: 'root', + factory: () => getDataServiceFor +}); +export const LINK_DEFINITION_FACTORY = new InjectionToken<(source: GenericConstructor, linkName: keyof T['_links']) => LinkDefinition>('getLinkDefinition', { + providedIn: 'root', + factory: () => getLinkDefinition +}); +export const LINK_DEFINITION_MAP_FACTORY = new InjectionToken<(source: GenericConstructor) => Map>>('getLinkDefinitions', { + providedIn: 'root', + factory: () => getLinkDefinitions +}); const resolvedLinkKey = Symbol('resolvedLink'); diff --git a/src/app/core/cache/builders/link.service.spec.ts b/src/app/core/cache/builders/link.service.spec.ts index 1c41cfee86..a6d9c59492 100644 --- a/src/app/core/cache/builders/link.service.spec.ts +++ b/src/app/core/cache/builders/link.service.spec.ts @@ -5,15 +5,9 @@ import { FindListOptions } from '../../data/request.models'; import { HALLink } from '../../shared/hal-link.model'; import { HALResource } from '../../shared/hal-resource.model'; import { ResourceType } from '../../shared/resource-type'; -import * as decorators from './build-decorators'; import { LinkService } from './link.service'; - -const spyOnFunction = (obj: T, func: keyof T) => { - const spy = jasmine.createSpy(func as string); - spyOnProperty(obj, func, 'get').and.returnValue(spy); - - return spy; -}; +import { DATA_SERVICE_FACTORY, LINK_DEFINITION_FACTORY, LINK_DEFINITION_MAP_FACTORY } from './build-decorators'; +import { isEmpty } from 'rxjs/operators'; const TEST_MODEL = new ResourceType('testmodel'); let result: any; @@ -51,7 +45,7 @@ let testDataService: TestDataService; let testModel: TestModel; -xdescribe('LinkService', () => { +describe('LinkService', () => { let service: LinkService; beforeEach(() => { @@ -76,6 +70,30 @@ xdescribe('LinkService', () => { providers: [LinkService, { provide: TestDataService, useValue: testDataService + }, { + provide: DATA_SERVICE_FACTORY, + useValue: jasmine.createSpy('getDataServiceFor').and.returnValue(TestDataService), + }, { + provide: LINK_DEFINITION_FACTORY, + useValue: jasmine.createSpy('getLinkDefinition').and.returnValue({ + resourceType: TEST_MODEL, + linkName: 'predecessor', + propertyName: 'predecessor' + }), + }, { + provide: LINK_DEFINITION_MAP_FACTORY, + useValue: jasmine.createSpy('getLinkDefinitions').and.returnValue([ + { + resourceType: TEST_MODEL, + linkName: 'predecessor', + propertyName: 'predecessor', + }, + { + resourceType: TEST_MODEL, + linkName: 'successor', + propertyName: 'successor', + } + ]), }] }); service = TestBed.inject(LinkService); @@ -84,12 +102,6 @@ xdescribe('LinkService', () => { describe('resolveLink', () => { describe(`when the linkdefinition concerns a single object`, () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ - resourceType: TEST_MODEL, - linkName: 'predecessor', - propertyName: 'predecessor' - }); - spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService); service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor'))); }); it('should call dataservice.findByHref with the correct href and nested links', () => { @@ -98,13 +110,12 @@ xdescribe('LinkService', () => { }); describe(`when the linkdefinition concerns a list`, () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ + ((service as any).getLinkDefinition as jasmine.Spy).and.returnValue({ resourceType: TEST_MODEL, linkName: 'predecessor', propertyName: 'predecessor', isList: true }); - spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService); service.resolveLink(testModel, followLink('predecessor', { some: 'options ' } as any, true, true, true, followLink('successor'))); }); it('should call dataservice.findAllByHref with the correct href, findListOptions, and nested links', () => { @@ -113,21 +124,15 @@ xdescribe('LinkService', () => { }); describe('either way', () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ - resourceType: TEST_MODEL, - linkName: 'predecessor', - propertyName: 'predecessor' - }); - spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService); result = service.resolveLink(testModel, followLink('predecessor', {}, true, true, true, followLink('successor'))); }); it('should call getLinkDefinition with the correct model and link', () => { - expect(decorators.getLinkDefinition).toHaveBeenCalledWith(testModel.constructor as any, 'predecessor'); + expect((service as any).getLinkDefinition).toHaveBeenCalledWith(testModel.constructor as any, 'predecessor'); }); it('should call getDataServiceFor with the correct resource type', () => { - expect(decorators.getDataServiceFor).toHaveBeenCalledWith(TEST_MODEL); + expect((service as any).getDataServiceFor).toHaveBeenCalledWith(TEST_MODEL); }); it('should return the model with the resolved link', () => { @@ -140,7 +145,7 @@ xdescribe('LinkService', () => { describe(`when the specified link doesn't exist on the model's class`, () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue(undefined); + ((service as any).getLinkDefinition as jasmine.Spy).and.returnValue(undefined); }); it('should throw an error', () => { expect(() => { @@ -151,12 +156,7 @@ xdescribe('LinkService', () => { describe(`when there is no dataservice for the resourcetype in the link`, () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ - resourceType: TEST_MODEL, - linkName: 'predecessor', - propertyName: 'predecessor' - }); - spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(undefined); + ((service as any).getDataServiceFor as jasmine.Spy).and.returnValue(undefined); }); it('should throw an error', () => { expect(() => { @@ -188,18 +188,6 @@ xdescribe('LinkService', () => { beforeEach(() => { testModel.predecessor = 'predecessor value' as any; testModel.successor = 'successor value' as any; - spyOnFunction(decorators, 'getLinkDefinitions').and.returnValue([ - { - resourceType: TEST_MODEL, - linkName: 'predecessor', - propertyName: 'predecessor', - }, - { - resourceType: TEST_MODEL, - linkName: 'successor', - propertyName: 'successor', - } - ]); }); it('should return a new version of the object without any resolved links', () => { @@ -231,16 +219,10 @@ xdescribe('LinkService', () => { } } }); - spyOnFunction(decorators, 'getDataServiceFor').and.returnValue(TestDataService); }); describe('resolving the available link', () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ - resourceType: TEST_MODEL, - linkName: 'predecessor', - propertyName: 'predecessor' - }); result = service.resolveLinks(testModel, followLink('predecessor')); }); @@ -251,7 +233,7 @@ xdescribe('LinkService', () => { describe('resolving the missing link', () => { beforeEach(() => { - spyOnFunction(decorators, 'getLinkDefinition').and.returnValue({ + ((service as any).getLinkDefinition as jasmine.Spy).and.returnValue({ resourceType: TEST_MODEL, linkName: 'successor', propertyName: 'successor' @@ -259,8 +241,11 @@ xdescribe('LinkService', () => { result = service.resolveLinks(testModel, followLink('successor')); }); - it('should return the model with no resolved link', () => { - expect(result.successor).toBeUndefined(); + it('should resolve to an empty observable', (done) => { + result.successor.pipe(isEmpty()).subscribe((v) => { + expect(v).toEqual(true); + done(); + }); }); }); }); diff --git a/src/app/core/cache/builders/link.service.ts b/src/app/core/cache/builders/link.service.ts index 56a1154b77..29f8633da5 100644 --- a/src/app/core/cache/builders/link.service.ts +++ b/src/app/core/cache/builders/link.service.ts @@ -1,17 +1,18 @@ -import { Injectable, Injector } from '@angular/core'; +import { Inject, Injectable, Injector } from '@angular/core'; import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { GenericConstructor } from '../../shared/generic-constructor'; import { HALResource } from '../../shared/hal-resource.model'; import { - getDataServiceFor, - getLinkDefinition, - getLinkDefinitions, + DATA_SERVICE_FACTORY, + LINK_DEFINITION_FACTORY, + LINK_DEFINITION_MAP_FACTORY, LinkDefinition } from './build-decorators'; import { RemoteData } from '../../data/remote-data'; import { Observable } from 'rxjs/internal/Observable'; import { EMPTY } from 'rxjs'; +import { ResourceType } from '../../shared/resource-type'; /** * A Service to handle the resolving and removing @@ -24,6 +25,9 @@ export class LinkService { constructor( protected parentInjector: Injector, + @Inject(DATA_SERVICE_FACTORY) private getDataServiceFor: (resourceType: ResourceType) => GenericConstructor, + @Inject(LINK_DEFINITION_FACTORY) private getLinkDefinition: (source: GenericConstructor, linkName: keyof T['_links']) => LinkDefinition, + @Inject(LINK_DEFINITION_MAP_FACTORY) private getLinkDefinitions: (source: GenericConstructor) => Map>, ) { } @@ -49,12 +53,12 @@ export class LinkService { * @param linkToFollow the {@link FollowLinkConfig} to resolve */ public resolveLinkWithoutAttaching(model, linkToFollow: FollowLinkConfig): Observable> { - const matchingLinkDef = getLinkDefinition(model.constructor, linkToFollow.name); + const matchingLinkDef = this.getLinkDefinition(model.constructor, linkToFollow.name); if (hasNoValue(matchingLinkDef)) { throw new Error(`followLink('${linkToFollow.name}') was used for a ${model.constructor.name}, but there is no property on ${model.constructor.name} models with an @link() for ${linkToFollow.name}`); } else { - const provider = getDataServiceFor(matchingLinkDef.resourceType); + const provider = this.getDataServiceFor(matchingLinkDef.resourceType); if (hasNoValue(provider)) { throw new Error(`The @link() for ${linkToFollow.name} on ${model.constructor.name} models uses the resource type ${matchingLinkDef.resourceType.value.toUpperCase()}, but there is no service with an @dataService(${matchingLinkDef.resourceType.value.toUpperCase()}) annotation in order to retrieve it`); @@ -104,7 +108,7 @@ export class LinkService { */ public removeResolvedLinks(model: T): T { const result = Object.assign(new (model.constructor as GenericConstructor)(), model); - const linkDefs = getLinkDefinitions(model.constructor as GenericConstructor); + const linkDefs = this.getLinkDefinitions(model.constructor as GenericConstructor); if (isNotEmpty(linkDefs)) { linkDefs.forEach((linkDef: LinkDefinition) => { result[linkDef.propertyName] = undefined; diff --git a/src/app/core/data/relationship.service.spec.ts b/src/app/core/data/relationship.service.spec.ts index 0a8ec48464..0f7dd319c3 100644 --- a/src/app/core/data/relationship.service.spec.ts +++ b/src/app/core/data/relationship.service.spec.ts @@ -1,5 +1,4 @@ import { of as observableOf } from 'rxjs'; -import * as ItemRelationshipsUtils from '../../+item-page/simple/item-types/shared/item-relationships-utils'; import { followLink } from '../../shared/utils/follow-link-config.model'; import { ObjectCacheService } from '../cache/object-cache.service'; import { RelationshipType } from '../shared/item-relationships/relationship-type.model'; @@ -15,9 +14,9 @@ import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-servic import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { getMockRemoteDataBuildServiceHrefMap } from '../../shared/mocks/remote-data-build.service.mock'; import { getMockRequestService } from '../../shared/mocks/request.service.mock'; -import { createPaginatedList, spyOnOperator } from '../../shared/testing/utils.test'; +import { createPaginatedList } from '../../shared/testing/utils.test'; -xdescribe('RelationshipService', () => { +describe('RelationshipService', () => { let service: RelationshipService; let requestService: RequestService; @@ -132,7 +131,8 @@ xdescribe('RelationshipService', () => { null, null, null, - null + null, + jasmine.createSpy('paginatedRelationsToItems').and.returnValue((v) => v), ); } @@ -195,8 +195,6 @@ xdescribe('RelationshipService', () => { const rd$ = createSuccessfulRemoteDataObject$(relationsList); spyOn(service, 'getItemRelationshipsByLabel').and.returnValue(rd$); - - spyOnOperator(ItemRelationshipsUtils, 'paginatedRelationsToItems').and.returnValue((v) => v); }); it('should call getItemRelationshipsByLabel with the correct params', (done) => { @@ -225,7 +223,7 @@ xdescribe('RelationshipService', () => { mockLabel, mockOptions ).subscribe((result) => { - expect(ItemRelationshipsUtils.paginatedRelationsToItems).toHaveBeenCalledWith(mockItem.uuid); + expect((service as any).paginatedRelationsToItems).toHaveBeenCalledWith(mockItem.uuid); done(); }); }); diff --git a/src/app/core/data/relationship.service.ts b/src/app/core/data/relationship.service.ts index 95512be678..da1ff790df 100644 --- a/src/app/core/data/relationship.service.ts +++ b/src/app/core/data/relationship.service.ts @@ -1,11 +1,10 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Inject, Injectable } from '@angular/core'; import { MemoizedSelector, select, Store } from '@ngrx/store'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, filter, map, mergeMap, startWith, switchMap, take, tap } from 'rxjs/operators'; import { - compareArraysUsingIds, - paginatedRelationsToItems, + compareArraysUsingIds, PAGINATED_RELATIONS_TO_ITEMS_OPERATOR, relationsToItems } from '../../+item-page/simple/item-types/shared/item-relationships-utils'; import { AppState, keySelector } from '../../app.reducer'; @@ -87,7 +86,8 @@ export class RelationshipService extends DataService { protected notificationsService: NotificationsService, protected http: HttpClient, protected comparator: DefaultChangeAnalyzer, - protected appStore: Store) { + protected appStore: Store, + @Inject(PAGINATED_RELATIONS_TO_ITEMS_OPERATOR) private paginatedRelationsToItems: (thisId: string) => (source: Observable>>) => Observable>>) { super(); } @@ -254,7 +254,7 @@ export class RelationshipService extends DataService { * @param options */ getRelatedItemsByLabel(item: Item, label: string, options?: FindListOptions): Observable>> { - return this.getItemRelationshipsByLabel(item, label, options, true, true, followLink('leftItem'), followLink('rightItem'), followLink('relationshipType')).pipe(paginatedRelationsToItems(item.uuid)); + return this.getItemRelationshipsByLabel(item, label, options, true, true, followLink('leftItem'), followLink('rightItem'), followLink('relationshipType')).pipe(this.paginatedRelationsToItems(item.uuid)); } /** diff --git a/src/app/shared/cookies/browser-klaro.service.spec.ts b/src/app/shared/cookies/browser-klaro.service.spec.ts index 7cf8a190f4..2155fb1bad 100644 --- a/src/app/shared/cookies/browser-klaro.service.spec.ts +++ b/src/app/shared/cookies/browser-klaro.service.spec.ts @@ -1,5 +1,4 @@ import { TestBed } from '@angular/core/testing'; - import { BrowserKlaroService, COOKIE_MDFIELD } from './browser-klaro.service'; import { getMockTranslateService } from '../mocks/translate.service.mock'; import { of as observableOf } from 'rxjs'; @@ -13,7 +12,7 @@ import { getTestScheduler } from 'jasmine-marbles'; import { MetadataValue } from '../../core/shared/metadata.models'; import { cloneDeep } from 'lodash'; -xdescribe('BrowserKlaroService', () => { +describe('BrowserKlaroService', () => { let translateService; let ePersonService; let authService; @@ -81,7 +80,7 @@ xdescribe('BrowserKlaroService', () => { } } }, - apps: [{ + services: [{ name: appName, purposes: [purpose] }], diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts index bd5031ddc5..7edf1a700e 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.spec.ts @@ -1,15 +1,15 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { ChangeDetectionStrategy, ComponentFactoryResolver, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { Context } from '../../core/shared/context.model'; import { MetadataRepresentation, MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentationLoaderComponent } from './metadata-representation-loader.component'; -import { PlainTextMetadataListElementComponent } from '../object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component'; -import { spyOnExported } from '../testing/utils.test'; import { MetadataRepresentationDirective } from './metadata-representation.directive'; -import * as metadataRepresentationDecorator from './metadata-representation.decorator'; +import { METADATA_REPRESENTATION_COMPONENT_FACTORY } from './metadata-representation.decorator'; +import { ThemeService } from '../theme-support/theme.service'; +import { PlainTextMetadataListElementComponent } from '../object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component'; const testType = 'TestType'; const testContext = Context.Search; @@ -29,16 +29,30 @@ class TestType implements MetadataRepresentation { } } -xdescribe('MetadataRepresentationLoaderComponent', () => { +describe('MetadataRepresentationLoaderComponent', () => { let comp: MetadataRepresentationLoaderComponent; let fixture: ComponentFixture; + let themeService: ThemeService; + const themeName = 'test-theme'; beforeEach(waitForAsync(() => { + themeService = jasmine.createSpyObj('themeService', { + getThemeName: themeName, + }); TestBed.configureTestingModule({ imports: [], declarations: [MetadataRepresentationLoaderComponent, PlainTextMetadataListElementComponent, MetadataRepresentationDirective], schemas: [NO_ERRORS_SCHEMA], - providers: [ComponentFactoryResolver] + providers: [ + { + provide: METADATA_REPRESENTATION_COMPONENT_FACTORY, + useValue: jasmine.createSpy('getMetadataRepresentationComponent').and.returnValue(PlainTextMetadataListElementComponent) + }, + { + provide: ThemeService, + useValue: themeService, + } + ] }).overrideComponent(MetadataRepresentationLoaderComponent, { set: { changeDetection: ChangeDetectionStrategy.Default, @@ -53,15 +67,12 @@ xdescribe('MetadataRepresentationLoaderComponent', () => { comp.mdRepresentation = new TestType(); comp.context = testContext; - - spyOnExported(metadataRepresentationDecorator, 'getMetadataRepresentationComponent').and.returnValue(PlainTextMetadataListElementComponent); fixture.detectChanges(); - })); describe('When the component is rendered', () => { it('should call the getMetadataRepresentationComponent function with the right entity type, representation type and context', () => { - expect(metadataRepresentationDecorator.getMetadataRepresentationComponent).toHaveBeenCalledWith(testType, testRepresentationType, testContext); + expect((comp as any).getMetadataRepresentationComponent).toHaveBeenCalledWith(testType, testRepresentationType, testContext, themeName); }); }); }); diff --git a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts index 976dfdbda8..7077949809 100644 --- a/src/app/shared/metadata-representation/metadata-representation-loader.component.ts +++ b/src/app/shared/metadata-representation/metadata-representation-loader.component.ts @@ -1,6 +1,9 @@ -import { Component, ComponentFactoryResolver, Input, OnInit, ViewChild } from '@angular/core'; -import { MetadataRepresentation } from '../../core/shared/metadata-representation/metadata-representation.model'; -import { getMetadataRepresentationComponent } from './metadata-representation.decorator'; +import { Component, ComponentFactoryResolver, Inject, Input, OnInit, ViewChild } from '@angular/core'; +import { + MetadataRepresentation, + MetadataRepresentationType +} from '../../core/shared/metadata-representation/metadata-representation.model'; +import { METADATA_REPRESENTATION_COMPONENT_FACTORY } from './metadata-representation.decorator'; import { Context } from '../../core/shared/context.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { MetadataRepresentationListElementComponent } from '../object-list/metadata-representation-list-element/metadata-representation-list-element.component'; @@ -45,7 +48,8 @@ export class MetadataRepresentationLoaderComponent implements OnInit { constructor( private componentFactoryResolver: ComponentFactoryResolver, - private themeService: ThemeService + private themeService: ThemeService, + @Inject(METADATA_REPRESENTATION_COMPONENT_FACTORY) private getMetadataRepresentationComponent: (entityType: string, mdRepresentationType: MetadataRepresentationType, context: Context, theme: string) => GenericConstructor, ) { } @@ -68,6 +72,6 @@ export class MetadataRepresentationLoaderComponent implements OnInit { * @returns {string} */ private getComponent(): GenericConstructor { - return getMetadataRepresentationComponent(this.mdRepresentation.itemType, this.mdRepresentation.representationType, this.context, this.themeService.getThemeName()); + return this.getMetadataRepresentationComponent(this.mdRepresentation.itemType, this.mdRepresentation.representationType, this.context, this.themeService.getThemeName()); } } diff --git a/src/app/shared/metadata-representation/metadata-representation.decorator.ts b/src/app/shared/metadata-representation/metadata-representation.decorator.ts index 30bb507b49..0b5bea33d9 100644 --- a/src/app/shared/metadata-representation/metadata-representation.decorator.ts +++ b/src/app/shared/metadata-representation/metadata-representation.decorator.ts @@ -1,6 +1,13 @@ import { MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model'; import { hasNoValue, hasValue } from '../empty.util'; import { Context } from '../../core/shared/context.model'; +import { InjectionToken } from '@angular/core'; +import { GenericConstructor } from '../../core/shared/generic-constructor'; + +export const METADATA_REPRESENTATION_COMPONENT_FACTORY = new InjectionToken<(entityType: string, mdRepresentationType: MetadataRepresentationType, context: Context, theme: string) => GenericConstructor>('getMetadataRepresentationComponent', { + providedIn: 'root', + factory: () => getMetadataRepresentationComponent +}); export const map = new Map();