mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
[CST-5307] Refactoring and adding missing unit tests
This commit is contained in:
@@ -78,15 +78,32 @@ describe(`DSONameService`, () => {
|
||||
});
|
||||
|
||||
describe(`factories.Person`, () => {
|
||||
beforeEach(() => {
|
||||
spyOn(mockPerson, 'firstMetadataValue').and.returnValues(...mockPersonName.split(', '));
|
||||
describe(`with person.familyName and person.givenName`, () => {
|
||||
beforeEach(() => {
|
||||
spyOn(mockPerson, 'firstMetadataValue').and.returnValues(...mockPersonName.split(', '));
|
||||
});
|
||||
|
||||
it(`should return 'person.familyName, person.givenName'`, () => {
|
||||
const result = (service as any).factories.Person(mockPerson);
|
||||
expect(result).toBe(mockPersonName);
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
|
||||
expect(mockPerson.firstMetadataValue).not.toHaveBeenCalledWith('dc.title');
|
||||
});
|
||||
});
|
||||
|
||||
it(`should return 'person.familyName, person.givenName'`, () => {
|
||||
const result = (service as any).factories.Person(mockPerson);
|
||||
expect(result).toBe(mockPersonName);
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
|
||||
describe(`without person.familyName and person.givenName`, () => {
|
||||
beforeEach(() => {
|
||||
spyOn(mockPerson, 'firstMetadataValue').and.returnValues(undefined, undefined, mockPersonName);
|
||||
});
|
||||
|
||||
it(`should return dc.title`, () => {
|
||||
const result = (service as any).factories.Person(mockPerson);
|
||||
expect(result).toBe(mockPersonName);
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.familyName');
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('person.givenName');
|
||||
expect(mockPerson.firstMetadataValue).toHaveBeenCalledWith('dc.title');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
290
src/app/core/profile/researcher-profile.service.spec.ts
Normal file
290
src/app/core/profile/researcher-profile.service.spec.ts
Normal file
@@ -0,0 +1,290 @@
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
|
||||
import { cold, getTestScheduler, hot } from 'jasmine-marbles';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { PageInfo } from '../shared/page-info.model';
|
||||
import { buildPaginatedList } from '../data/paginated-list.model';
|
||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
import { RestResponse } from '../cache/response.models';
|
||||
import { RequestEntry } from '../data/request-entry.model';
|
||||
import { ResearcherProfileService } from './researcher-profile.service';
|
||||
import { RouterMock } from '../../shared/mocks/router.mock';
|
||||
import { ResearcherProfile } from './model/researcher-profile.model';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { ReplaceOperation } from 'fast-json-patch';
|
||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
import { PostRequest } from '../data/request.models';
|
||||
|
||||
describe('ResearcherProfileService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: ResearcherProfileService;
|
||||
let serviceAsAny: any;
|
||||
let requestService: RequestService;
|
||||
let rdbService: RemoteDataBuildService;
|
||||
let objectCache: ObjectCacheService;
|
||||
let halService: HALEndpointService;
|
||||
let responseCacheEntry: RequestEntry;
|
||||
|
||||
const researcherProfileId = 'beef9946-rt56-479e-8f11-b90cbe9f7241';
|
||||
const itemId = 'beef9946-rt56-479e-8f11-b90cbe9f7241';
|
||||
const researcherProfileItem: Item = Object.assign(new Item(), {
|
||||
id: itemId,
|
||||
_links: {
|
||||
self: {
|
||||
href: `https://rest.api/rest/api/items/${itemId}`
|
||||
},
|
||||
}
|
||||
});
|
||||
const researcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), {
|
||||
id: researcherProfileId,
|
||||
visible: false,
|
||||
type: 'profile',
|
||||
_links: {
|
||||
item: {
|
||||
href: `https://rest.api/rest/api/profiles/${researcherProfileId}/item`
|
||||
},
|
||||
self: {
|
||||
href: `https://rest.api/rest/api/profiles/${researcherProfileId}`
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const researcherProfilePatched: ResearcherProfile = Object.assign(new ResearcherProfile(), {
|
||||
id: researcherProfileId,
|
||||
visible: true,
|
||||
type: 'profile',
|
||||
_links: {
|
||||
item: {
|
||||
href: `https://rest.api/rest/api/profiles/${researcherProfileId}/item`
|
||||
},
|
||||
self: {
|
||||
href: `https://rest.api/rest/api/profiles/${researcherProfileId}`
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
const researcherProfileId2 = 'agbf9946-f4ce-479e-8f11-b90cbe9f7241';
|
||||
const anotherResearcherProfile: ResearcherProfile = Object.assign(new ResearcherProfile(), {
|
||||
id: researcherProfileId2,
|
||||
visible: false,
|
||||
type: 'profile',
|
||||
_links: {
|
||||
self: {
|
||||
href: `https://rest.api/rest/api/profiles/${researcherProfileId2}`
|
||||
},
|
||||
}
|
||||
});
|
||||
const endpointURL = `https://rest.api/rest/api/profiles`;
|
||||
const sourceUri = `https://rest.api/rest/api/external-source/profile`;
|
||||
const requestURL = `https://rest.api/rest/api/profiles/${researcherProfileId}`;
|
||||
const requestUUID = '8b3c613a-5a4b-438b-9686-be1d5b4a1c5a';
|
||||
|
||||
const pageInfo = new PageInfo();
|
||||
const array = [researcherProfile, anotherResearcherProfile];
|
||||
const paginatedList = buildPaginatedList(pageInfo, array);
|
||||
const researcherProfileRD = createSuccessfulRemoteDataObject(researcherProfile);
|
||||
const paginatedListRD = createSuccessfulRemoteDataObject(paginatedList);
|
||||
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
|
||||
halService = jasmine.createSpyObj('halService', {
|
||||
getEndpoint: cold('a', { a: endpointURL })
|
||||
});
|
||||
|
||||
responseCacheEntry = new RequestEntry();
|
||||
responseCacheEntry.request = { href: 'https://rest.api/' } as any;
|
||||
responseCacheEntry.response = new RestResponse(true, 200, 'Success');
|
||||
|
||||
requestService = jasmine.createSpyObj('requestService', {
|
||||
generateRequestId: requestUUID,
|
||||
send: true,
|
||||
removeByHrefSubstring: {},
|
||||
getByHref: observableOf(responseCacheEntry),
|
||||
getByUUID: observableOf(responseCacheEntry),
|
||||
setStaleByHrefSubstring: jasmine.createSpy('setStaleByHrefSubstring')
|
||||
});
|
||||
rdbService = jasmine.createSpyObj('rdbService', {
|
||||
buildSingle: hot('a|', {
|
||||
a: researcherProfileRD
|
||||
}),
|
||||
buildList: hot('a|', {
|
||||
a: paginatedListRD
|
||||
}),
|
||||
buildFromRequestUUID: hot('a|', {
|
||||
a: researcherProfileRD
|
||||
})
|
||||
});
|
||||
objectCache = {} as ObjectCacheService;
|
||||
const notificationsService = {} as NotificationsService;
|
||||
const http = {} as HttpClient;
|
||||
const comparator = {} as any;
|
||||
const routerStub: any = new RouterMock();
|
||||
const itemService = jasmine.createSpyObj('ItemService', {
|
||||
findByHref: jasmine.createSpy('findByHref')
|
||||
});
|
||||
|
||||
service = new ResearcherProfileService(
|
||||
requestService,
|
||||
rdbService,
|
||||
objectCache,
|
||||
halService,
|
||||
notificationsService,
|
||||
http,
|
||||
routerStub,
|
||||
comparator,
|
||||
itemService
|
||||
);
|
||||
serviceAsAny = service;
|
||||
|
||||
spyOn((service as any).dataService, 'create').and.callThrough();
|
||||
spyOn((service as any).dataService, 'delete').and.callThrough();
|
||||
spyOn((service as any).dataService, 'update').and.callThrough();
|
||||
spyOn((service as any).dataService, 'findById').and.callThrough();
|
||||
spyOn((service as any).dataService, 'findByHref').and.callThrough();
|
||||
spyOn((service as any).dataService, 'searchBy').and.callThrough();
|
||||
spyOn((service as any).dataService, 'getLinkPath').and.returnValue(observableOf(endpointURL));
|
||||
|
||||
});
|
||||
|
||||
describe('findById', () => {
|
||||
it('should proxy the call to dataservice.findById with eperson UUID', () => {
|
||||
scheduler.schedule(() => service.findById(researcherProfileId));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).dataService.findById).toHaveBeenCalledWith(researcherProfileId, true, true);
|
||||
});
|
||||
|
||||
it('should return a ResearcherProfile object with the given id', () => {
|
||||
const result = service.findById(researcherProfileId);
|
||||
const expected = cold('a|', {
|
||||
a: researcherProfileRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should proxy the call to dataservice.create with eperson UUID', () => {
|
||||
scheduler.schedule(() => service.create());
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).dataService.create).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return the RemoteData<ResearcherProfile> created', () => {
|
||||
const result = service.create();
|
||||
const expected = cold('a|', {
|
||||
a: researcherProfileRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should proxy the call to dataservice.delete', () => {
|
||||
scheduler.schedule(() => service.delete(researcherProfile));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).dataService.delete).toHaveBeenCalledWith(researcherProfile.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findRelatedItemId', () => {
|
||||
describe('with a related item', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
(service as any).itemService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(researcherProfileItem));
|
||||
});
|
||||
|
||||
it('should proxy the call to dataservice.findById with eperson UUID', () => {
|
||||
scheduler.schedule(() => service.findRelatedItemId(researcherProfile));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).itemService.findByHref).toHaveBeenCalledWith(researcherProfile._links.item.href, false);
|
||||
});
|
||||
|
||||
it('should return a ResearcherProfile object with the given id', () => {
|
||||
const result = service.findRelatedItemId(researcherProfile);
|
||||
const expected = cold('(a|)', {
|
||||
a: itemId
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('without a related item', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
(service as any).itemService.findByHref.and.returnValue(createSuccessfulRemoteDataObject$(null));
|
||||
});
|
||||
|
||||
it('should proxy the call to dataservice.findById with eperson UUID', () => {
|
||||
scheduler.schedule(() => service.findRelatedItemId(researcherProfile));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).itemService.findByHref).toHaveBeenCalledWith(researcherProfile._links.item.href, false);
|
||||
});
|
||||
|
||||
it('should return a ResearcherProfile object with the given id', () => {
|
||||
const result = service.findRelatedItemId(researcherProfile);
|
||||
const expected = cold('(a|)', {
|
||||
a: undefined
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setVisibility', () => {
|
||||
let patchSpy;
|
||||
beforeEach(() => {
|
||||
spyOn((service as any), 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||
});
|
||||
|
||||
it('should proxy the call to dataservice.patch', () => {
|
||||
const replaceOperation: ReplaceOperation<boolean> = {
|
||||
path: '/visible',
|
||||
op: 'replace',
|
||||
value: true
|
||||
};
|
||||
|
||||
scheduler.schedule(() => service.setVisibility(researcherProfile, true));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).patch).toHaveBeenCalledWith(researcherProfile, [replaceOperation]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createFromExternalSource', () => {
|
||||
let patchSpy;
|
||||
beforeEach(() => {
|
||||
spyOn((service as any), 'patch').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||
spyOn((service as any), 'findById').and.returnValue(createSuccessfulRemoteDataObject$(researcherProfilePatched));
|
||||
});
|
||||
|
||||
it('should proxy the call to dataservice.patch', () => {
|
||||
const options: HttpOptions = Object.create({});
|
||||
let headers = new HttpHeaders();
|
||||
headers = headers.append('Content-Type', 'text/uri-list');
|
||||
options.headers = headers;
|
||||
const request = new PostRequest(requestUUID, endpointURL, sourceUri, options);
|
||||
|
||||
scheduler.schedule(() => service.createFromExternalSource(sourceUri));
|
||||
scheduler.flush();
|
||||
|
||||
expect((service as any).requestService.send).toHaveBeenCalledWith(request);
|
||||
expect((service as any).rdbService.buildFromRequestUUID).toHaveBeenCalledWith(requestUUID);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -2,27 +2,25 @@
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Operation, RemoveOperation, ReplaceOperation } from 'fast-json-patch';
|
||||
import { combineLatest, Observable, of as observableOf } from 'rxjs';
|
||||
import { Operation, ReplaceOperation } from 'fast-json-patch';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { catchError, find, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { environment } from '../../../environments/environment';
|
||||
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { dataService } from '../cache/builders/build-decorators';
|
||||
import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service';
|
||||
import { ObjectCacheService } from '../cache/object-cache.service';
|
||||
import { ConfigurationDataService } from '../data/configuration-data.service';
|
||||
import { DataService } from '../data/data.service';
|
||||
import { DefaultChangeAnalyzer } from '../data/default-change-analyzer.service';
|
||||
import { ItemDataService } from '../data/item-data.service';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { RequestService } from '../data/request.service';
|
||||
import { ConfigurationProperty } from '../shared/configuration-property.model';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { Item } from '../shared/item.model';
|
||||
import { NoContent } from '../shared/NoContent.model';
|
||||
import {
|
||||
getFinishedRemoteData,
|
||||
getAllCompletedRemoteData,
|
||||
getFirstCompletedRemoteData,
|
||||
getFirstSucceededRemoteDataPayload
|
||||
} from '../shared/operators';
|
||||
@@ -31,25 +29,26 @@ import { RESEARCHER_PROFILE } from './model/researcher-profile.resource-type';
|
||||
import { HttpOptions } from '../dspace-rest/dspace-rest.service';
|
||||
import { PostRequest } from '../data/request.models';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import {CoreState} from '../core-state.model';
|
||||
import { CoreState } from '../core-state.model';
|
||||
import { FollowLinkConfig } from '../../shared/utils/follow-link-config.model';
|
||||
|
||||
/**
|
||||
* A private DataService implementation to delegate specific methods to.
|
||||
*/
|
||||
class ResearcherProfileServiceImpl extends DataService<ResearcherProfile> {
|
||||
protected linkPath = 'profiles';
|
||||
protected linkPath = 'profiles';
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<ResearcherProfile>) {
|
||||
super();
|
||||
}
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<ResearcherProfile>) {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,100 +59,102 @@ class ResearcherProfileServiceImpl extends DataService<ResearcherProfile> {
|
||||
@dataService(RESEARCHER_PROFILE)
|
||||
export class ResearcherProfileService {
|
||||
|
||||
dataService: ResearcherProfileServiceImpl;
|
||||
dataService: ResearcherProfileServiceImpl;
|
||||
|
||||
responseMsToLive: number = 10 * 1000;
|
||||
responseMsToLive: number = 10 * 1000;
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected router: Router,
|
||||
protected comparator: DefaultChangeAnalyzer<ResearcherProfile>,
|
||||
protected itemService: ItemDataService,
|
||||
protected configurationService: ConfigurationDataService ) {
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected router: Router,
|
||||
protected comparator: DefaultChangeAnalyzer<ResearcherProfile>,
|
||||
protected itemService: ItemDataService) {
|
||||
|
||||
this.dataService = new ResearcherProfileServiceImpl(requestService, rdbService, store, objectCache, halService,
|
||||
notificationsService, http, comparator);
|
||||
this.dataService = new ResearcherProfileServiceImpl(requestService, rdbService, null, objectCache, halService,
|
||||
notificationsService, http, comparator);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the researcher profile with the given uuid.
|
||||
*
|
||||
* @param uuid the profile uuid
|
||||
*/
|
||||
findById(uuid: string): Observable<ResearcherProfile> {
|
||||
return this.dataService.findById(uuid, false)
|
||||
.pipe ( getFinishedRemoteData(),
|
||||
map((remoteData) => remoteData.payload));
|
||||
}
|
||||
/**
|
||||
* Find the researcher profile with the given uuid.
|
||||
*
|
||||
* @param uuid the profile uuid
|
||||
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
|
||||
* no valid cached version. Defaults to true
|
||||
* @param reRequestOnStale Whether or not the request should automatically be re-
|
||||
* requested after the response becomes stale
|
||||
* @param linksToFollow List of {@link FollowLinkConfig} that indicate which
|
||||
* {@link HALLink}s should be automatically resolved
|
||||
*/
|
||||
public findById(uuid: string, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<ResearcherProfile>[]): Observable<RemoteData<ResearcherProfile>> {
|
||||
return this.dataService.findById(uuid, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow).pipe(
|
||||
getAllCompletedRemoteData(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new researcher profile for the current user.
|
||||
*/
|
||||
create(): Observable<RemoteData<ResearcherProfile>> {
|
||||
return this.dataService.create( new ResearcherProfile());
|
||||
}
|
||||
/**
|
||||
* Create a new researcher profile for the current user.
|
||||
*/
|
||||
public create(): Observable<RemoteData<ResearcherProfile>> {
|
||||
return this.dataService.create(new ResearcherProfile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a researcher profile.
|
||||
*
|
||||
* @param researcherProfile the profile to delete
|
||||
*/
|
||||
delete(researcherProfile: ResearcherProfile): Observable<boolean> {
|
||||
return this.dataService.delete(researcherProfile.id).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
tap((response: RemoteData<NoContent>) => {
|
||||
if (response.isSuccess) {
|
||||
this.requestService.setStaleByHrefSubstring(researcherProfile._links.self.href);
|
||||
}
|
||||
/**
|
||||
* Delete a researcher profile.
|
||||
*
|
||||
* @param researcherProfile the profile to delete
|
||||
*/
|
||||
public delete(researcherProfile: ResearcherProfile): Observable<boolean> {
|
||||
return this.dataService.delete(researcherProfile.id).pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
tap((response: RemoteData<NoContent>) => {
|
||||
if (response.isSuccess) {
|
||||
this.requestService.setStaleByHrefSubstring(researcherProfile._links.self.href);
|
||||
}
|
||||
}),
|
||||
map((response: RemoteData<NoContent>) => response.isSuccess)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the item id related to the given researcher profile.
|
||||
*
|
||||
* @param researcherProfile the profile to find for
|
||||
*/
|
||||
public findRelatedItemId(researcherProfile: ResearcherProfile): Observable<string> {
|
||||
return this.itemService.findByHref(researcherProfile._links.item.href, false)
|
||||
.pipe(getFirstSucceededRemoteDataPayload(),
|
||||
catchError((error) => {
|
||||
console.debug(error);
|
||||
return observableOf(null);
|
||||
}),
|
||||
map((response: RemoteData<NoContent>) => response.isSuccess)
|
||||
map((item) => item?.id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the item id related to the given researcher profile.
|
||||
*
|
||||
* @param researcherProfile the profile to find for
|
||||
*/
|
||||
findRelatedItemId( researcherProfile: ResearcherProfile ): Observable<string> {
|
||||
return this.itemService.findByHref(researcherProfile._links.item.href, false)
|
||||
.pipe (getFirstSucceededRemoteDataPayload(),
|
||||
catchError((error) => {
|
||||
console.debug(error);
|
||||
return observableOf(null);
|
||||
}),
|
||||
map((item) => item != null ? item.id : null ));
|
||||
}
|
||||
/**
|
||||
* Change the visibility of the given researcher profile setting the given value.
|
||||
*
|
||||
* @param researcherProfile the profile to update
|
||||
* @param visible the visibility value to set
|
||||
*/
|
||||
public setVisibility(researcherProfile: ResearcherProfile, visible: boolean): Observable<ResearcherProfile> {
|
||||
|
||||
/**
|
||||
* Change the visibility of the given researcher profile setting the given value.
|
||||
*
|
||||
* @param researcherProfile the profile to update
|
||||
* @param visible the visibility value to set
|
||||
*/
|
||||
setVisibility(researcherProfile: ResearcherProfile, visible: boolean): Observable<ResearcherProfile> {
|
||||
const replaceOperation: ReplaceOperation<boolean> = {
|
||||
path: '/visible',
|
||||
op: 'replace',
|
||||
value: visible
|
||||
};
|
||||
|
||||
const replaceOperation: ReplaceOperation<boolean> = {
|
||||
path: '/visible',
|
||||
op: 'replace',
|
||||
value: visible
|
||||
};
|
||||
|
||||
return this.patch(researcherProfile, [replaceOperation]).pipe (
|
||||
switchMap( ( ) => this.findById(researcherProfile.id))
|
||||
);
|
||||
}
|
||||
|
||||
patch(researcherProfile: ResearcherProfile, operations: Operation[]): Observable<RemoteData<ResearcherProfile>> {
|
||||
return this.dataService.patch(researcherProfile, operations);
|
||||
}
|
||||
return this.patch(researcherProfile, [replaceOperation]).pipe(
|
||||
switchMap(() => this.findById(researcherProfile.id)),
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a researcher profile starting from an external source URI
|
||||
@@ -179,4 +180,7 @@ export class ResearcherProfileService {
|
||||
return this.rdbService.buildFromRequestUUID(requestId);
|
||||
}
|
||||
|
||||
private patch(researcherProfile: ResearcherProfile, operations: Operation[]): Observable<RemoteData<ResearcherProfile>> {
|
||||
return this.dataService.patch(researcherProfile, operations);
|
||||
}
|
||||
}
|
||||
|
215
src/app/profile-page/profile-claim/profile-claim.service.spec.ts
Normal file
215
src/app/profile-page/profile-claim/profile-claim.service.spec.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TestScheduler } from 'rxjs/testing';
|
||||
|
||||
import { ProfileClaimService } from './profile-claim.service';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
import { ItemSearchResult } from '../../shared/object-collection/shared/item-search-result.model';
|
||||
import { SearchObjects } from '../../shared/search/models/search-objects.model';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
|
||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||
|
||||
describe('ProfileClaimService', () => {
|
||||
let scheduler: TestScheduler;
|
||||
let service: ProfileClaimService;
|
||||
let serviceAsAny: any;
|
||||
let searchService: jasmine.SpyObj<SearchService>;
|
||||
|
||||
const eperson: EPerson = Object.assign(new EPerson(), {
|
||||
id: 'id',
|
||||
metadata: {
|
||||
'eperson.firstname': [
|
||||
{
|
||||
value: 'John'
|
||||
}
|
||||
],
|
||||
'eperson.lastname': [
|
||||
{
|
||||
value: 'Doe'
|
||||
},
|
||||
],
|
||||
},
|
||||
email: 'fake@email.com'
|
||||
});
|
||||
const item1: Item = Object.assign(new Item(), {
|
||||
uuid: 'e1c51c69-896d-42dc-8221-1d5f2ad5516e',
|
||||
metadata: {
|
||||
'person.email': [
|
||||
{
|
||||
value: 'fake@email.com'
|
||||
}
|
||||
],
|
||||
'person.familyName': [
|
||||
{
|
||||
value: 'Doe'
|
||||
}
|
||||
],
|
||||
'person.givenName': [
|
||||
{
|
||||
value: 'John'
|
||||
}
|
||||
]
|
||||
},
|
||||
_links: {
|
||||
self: {
|
||||
href: 'item-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
const item2: Item = Object.assign(new Item(), {
|
||||
uuid: 'c8279647-1acc-41ae-b036-951d5f65649b',
|
||||
metadata: {
|
||||
'person.email': [
|
||||
{
|
||||
value: 'fake2@email.com'
|
||||
}
|
||||
],
|
||||
'dc.title': [
|
||||
{
|
||||
value: 'John, Doe'
|
||||
}
|
||||
]
|
||||
},
|
||||
_links: {
|
||||
self: {
|
||||
href: 'item-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
const item3: Item = Object.assign(new Item(), {
|
||||
uuid: 'c8279647-1acc-41ae-b036-951d5f65649b',
|
||||
metadata: {
|
||||
'person.email': [
|
||||
{
|
||||
value: 'fake3@email.com'
|
||||
}
|
||||
],
|
||||
'dc.title': [
|
||||
{
|
||||
value: 'John, Doe'
|
||||
}
|
||||
]
|
||||
},
|
||||
_links: {
|
||||
self: {
|
||||
href: 'item-href'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const searchResult1 = Object.assign(new ItemSearchResult(), { indexableObject: item1 });
|
||||
const searchResult2 = Object.assign(new ItemSearchResult(), { indexableObject: item2 });
|
||||
const searchResult3 = Object.assign(new ItemSearchResult(), { indexableObject: item3 });
|
||||
|
||||
const searchResult = Object.assign(new SearchObjects(), {
|
||||
page: [searchResult1, searchResult2, searchResult3]
|
||||
});
|
||||
const emptySearchResult = Object.assign(new SearchObjects(), {
|
||||
page: []
|
||||
});
|
||||
const searchResultRD = createSuccessfulRemoteDataObject(searchResult);
|
||||
const emptyRearchResultRD = createSuccessfulRemoteDataObject(emptySearchResult);
|
||||
|
||||
beforeEach(() => {
|
||||
scheduler = getTestScheduler();
|
||||
|
||||
searchService = jasmine.createSpyObj('SearchService', {
|
||||
search: jasmine.createSpy('search')
|
||||
});
|
||||
|
||||
service = new ProfileClaimService(searchService);
|
||||
serviceAsAny = service;
|
||||
});
|
||||
|
||||
describe('hasProfilesToSuggest', () => {
|
||||
|
||||
describe('when has suggestions', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(service, 'search').and.returnValue(observableOf(searchResultRD));
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
const result = service.hasProfilesToSuggest(eperson);
|
||||
const expected = cold('(a|)', {
|
||||
a: true
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when has not suggestions', () => {
|
||||
beforeEach(() => {
|
||||
spyOn(service, 'search').and.returnValue(observableOf(emptyRearchResultRD));
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
const result = service.hasProfilesToSuggest(eperson);
|
||||
const expected = cold('(a|)', {
|
||||
a: false
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when has not valid eperson', () => {
|
||||
it('should return false', () => {
|
||||
const result = service.hasProfilesToSuggest(null);
|
||||
const expected = cold('(a|)', {
|
||||
a: false
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
|
||||
describe('when has search results', () => {
|
||||
beforeEach(() => {
|
||||
searchService.search.and.returnValue(observableOf(searchResultRD));
|
||||
});
|
||||
|
||||
it('should return the proper search object', () => {
|
||||
const result = service.search(eperson);
|
||||
const expected = cold('(a|)', {
|
||||
a: searchResultRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when has not suggestions', () => {
|
||||
beforeEach(() => {
|
||||
searchService.search.and.returnValue(observableOf(emptyRearchResultRD));
|
||||
});
|
||||
|
||||
it('should return null', () => {
|
||||
const result = service.search(eperson);
|
||||
const expected = cold('(a|)', {
|
||||
a: emptyRearchResultRD
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when has not valid eperson', () => {
|
||||
it('should return null', () => {
|
||||
const result = service.search(null);
|
||||
const expected = cold('(a|)', {
|
||||
a: null
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@@ -1,16 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { mergeMap, take } from 'rxjs/operators';
|
||||
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
|
||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { SearchService } from '../../core/shared/search/search.service';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { PaginatedSearchOptions } from '../../shared/search/models/paginated-search-options.model';
|
||||
import { SearchResult } from '../../shared/search/models/search-result.model';
|
||||
import { getFirstSucceededRemoteData } from './../../core/shared/operators';
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
import { SearchObjects } from '../../shared/search/models/search-objects.model';
|
||||
|
||||
/**
|
||||
* Service that handle profiles claim.
|
||||
@@ -18,8 +18,7 @@ import { getFirstSucceededRemoteData } from './../../core/shared/operators';
|
||||
@Injectable()
|
||||
export class ProfileClaimService {
|
||||
|
||||
constructor(private searchService: SearchService,
|
||||
private configurationService: ConfigurationDataService) {
|
||||
constructor(private searchService: SearchService) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,27 +26,21 @@ export class ProfileClaimService {
|
||||
*
|
||||
* @param eperson the eperson
|
||||
*/
|
||||
canClaimProfiles(eperson: EPerson): Observable<boolean> {
|
||||
|
||||
const query = this.personQueryData(eperson);
|
||||
|
||||
if (!hasValue(query) || query.length === 0) {
|
||||
return of(false);
|
||||
}
|
||||
|
||||
return this.lookup(query).pipe(
|
||||
mergeMap((rd: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>) => of(rd.payload.totalElements > 0))
|
||||
hasProfilesToSuggest(eperson: EPerson): Observable<boolean> {
|
||||
return this.search(eperson).pipe(
|
||||
map((rd: RemoteData<SearchObjects<DSpaceObject>>) => {
|
||||
return isNotEmpty(rd) && rd.hasSucceeded && rd.payload?.page?.length > 0;
|
||||
})
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns profiles that could be associated with the given user.
|
||||
* @param eperson the user
|
||||
*/
|
||||
search(eperson: EPerson): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
|
||||
search(eperson: EPerson): Observable<RemoteData<SearchObjects<DSpaceObject>>> {
|
||||
const query = this.personQueryData(eperson);
|
||||
if (!hasValue(query) || query.length === 0) {
|
||||
if (isEmpty(query)) {
|
||||
return of(null);
|
||||
}
|
||||
return this.lookup(query);
|
||||
@@ -57,21 +50,31 @@ export class ProfileClaimService {
|
||||
* Search object by the given query.
|
||||
* @param query the query for the search
|
||||
*/
|
||||
private lookup(query: string): Observable<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> {
|
||||
if (!hasValue(query)) {
|
||||
private lookup(query: string): Observable<RemoteData<SearchObjects<DSpaceObject>>> {
|
||||
if (isEmpty(query)) {
|
||||
return of(null);
|
||||
}
|
||||
return this.searchService.search(new PaginatedSearchOptions({
|
||||
configuration: 'eperson_claims',
|
||||
query: query
|
||||
}))
|
||||
.pipe(
|
||||
getFirstSucceededRemoteData(),
|
||||
take(1));
|
||||
})).pipe(
|
||||
getFirstCompletedRemoteData()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search query for person lookup, from the given eperson
|
||||
*
|
||||
* @param eperson The eperson to use for the lookup
|
||||
*/
|
||||
private personQueryData(eperson: EPerson): string {
|
||||
return 'dc.title:' + eperson.name;
|
||||
if (eperson) {
|
||||
const firstname = eperson.firstMetadataValue('eperson.firstname');
|
||||
const lastname = eperson.firstMetadataValue('eperson.lastname');
|
||||
return 'dc.title:' + eperson.name + ' OR (person.familyName:' + lastname + ' AND person.givenName:' + firstname + ')';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ import { ProfilePageResearcherFormComponent } from './profile-page-researcher-fo
|
||||
import { ProfileClaimService } from '../profile-claim/profile-claim.service';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { AuthService } from 'src/app/core/auth/auth.service';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
|
||||
describe('ProfilePageResearcherFormComponent', () => {
|
||||
|
||||
@@ -51,7 +52,7 @@ describe('ProfilePageResearcherFormComponent', () => {
|
||||
});
|
||||
|
||||
researcherProfileService = jasmine.createSpyObj('researcherProfileService', {
|
||||
findById: observableOf(profile),
|
||||
findById: createSuccessfulRemoteDataObject$(profile),
|
||||
create: observableOf(profile),
|
||||
setVisibility: observableOf(profile),
|
||||
delete: observableOf(true),
|
||||
@@ -61,7 +62,7 @@ describe('ProfilePageResearcherFormComponent', () => {
|
||||
notificationsServiceStub = new NotificationsServiceStub();
|
||||
|
||||
profileClaimService = jasmine.createSpyObj('profileClaimService', {
|
||||
canClaimProfiles: observableOf(false),
|
||||
hasProfilesToSuggest: observableOf(false),
|
||||
});
|
||||
|
||||
}
|
||||
@@ -91,7 +92,7 @@ describe('ProfilePageResearcherFormComponent', () => {
|
||||
});
|
||||
|
||||
it('should search the researcher profile for the current user', () => {
|
||||
expect(researcherProfileService.findById).toHaveBeenCalledWith(user.id);
|
||||
expect(researcherProfileService.findById).toHaveBeenCalledWith(user.id, false);
|
||||
});
|
||||
|
||||
describe('createProfile', () => {
|
||||
|
@@ -4,17 +4,18 @@ import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { filter, mergeMap, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { mergeMap, switchMap, take, tap } from 'rxjs/operators';
|
||||
|
||||
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
|
||||
import { ClaimItemSelectorComponent } from '../../shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component';
|
||||
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||
import {
|
||||
ClaimItemSelectorComponent
|
||||
} from '../../shared/dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { AuthService } from '../../core/auth/auth.service';
|
||||
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||
import { ResearcherProfile } from '../../core/profile/model/researcher-profile.model';
|
||||
import { ResearcherProfileService } from '../../core/profile/researcher-profile.service';
|
||||
import { ProfileClaimService } from '../profile-claim/profile-claim.service';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-profile-page-researcher-form',
|
||||
@@ -77,10 +78,10 @@ export class ProfilePageResearcherFormComponent implements OnInit {
|
||||
this.processingCreate$.next(true);
|
||||
|
||||
this.authService.getAuthenticatedUserFromStore().pipe(
|
||||
switchMap((currentUser) => this.profileClaimService.canClaimProfiles(currentUser)))
|
||||
.subscribe((canClaimProfiles) => {
|
||||
switchMap((currentUser) => this.profileClaimService.hasProfilesToSuggest(currentUser)))
|
||||
.subscribe((hasProfilesToSuggest) => {
|
||||
|
||||
if (canClaimProfiles) {
|
||||
if (hasProfilesToSuggest) {
|
||||
this.processingCreate$.next(false);
|
||||
const modal = this.modalService.open(ClaimItemSelectorComponent);
|
||||
modal.componentInstance.dso = this.user;
|
||||
@@ -174,9 +175,8 @@ export class ProfilePageResearcherFormComponent implements OnInit {
|
||||
* Initializes the researcherProfile and researcherProfileItemId attributes using the profile of the current user.
|
||||
*/
|
||||
private initResearchProfile(): void {
|
||||
this.researcherProfileService.findById(this.user.id).pipe(
|
||||
take(1),
|
||||
filter((researcherProfile) => isNotEmpty(researcherProfile)),
|
||||
this.researcherProfileService.findById(this.user.id, false).pipe(
|
||||
getFirstSucceededRemoteDataPayload(),
|
||||
tap((researcherProfile) => this.researcherProfile$.next(researcherProfile)),
|
||||
mergeMap((researcherProfile) => this.researcherProfileService.findRelatedItemId(researcherProfile)),
|
||||
).subscribe((itemId: string) => {
|
||||
|
@@ -11,17 +11,17 @@ import { AuthTokenInfo } from '../core/auth/models/auth-token-info.model';
|
||||
import { EPersonDataService } from '../core/eperson/eperson-data.service';
|
||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||
import { authReducer } from '../core/auth/auth.reducer';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||
import { createFailedRemoteDataObject$, createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||
import { createPaginatedList } from '../shared/testing/utils.test';
|
||||
import { BehaviorSubject, of as observableOf } from 'rxjs';
|
||||
import { AuthService } from '../core/auth/auth.service';
|
||||
import { RestResponse } from '../core/cache/response.models';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||
import { getTestScheduler } from 'jasmine-marbles';
|
||||
import { cold, getTestScheduler } from 'jasmine-marbles';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import {ConfigurationDataService} from '../core/data/configuration-data.service';
|
||||
import {ConfigurationProperty} from '../core/shared/configuration-property.model';
|
||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||
|
||||
describe('ProfilePageComponent', () => {
|
||||
let component: ProfilePageComponent;
|
||||
@@ -30,16 +30,28 @@ describe('ProfilePageComponent', () => {
|
||||
let initialState: any;
|
||||
|
||||
let authService;
|
||||
let authorizationService;
|
||||
let epersonService;
|
||||
let notificationsService;
|
||||
let configurationService;
|
||||
|
||||
const canChangePassword = new BehaviorSubject(true);
|
||||
const validConfiguration = Object.assign(new ConfigurationProperty(), {
|
||||
name: 'researcher-profile.entity-type',
|
||||
values: [
|
||||
'Person'
|
||||
]
|
||||
});
|
||||
const emptyConfiguration = Object.assign(new ConfigurationProperty(), {
|
||||
name: 'researcher-profile.entity-type',
|
||||
values: []
|
||||
});
|
||||
|
||||
function init() {
|
||||
user = Object.assign(new EPerson(), {
|
||||
id: 'userId',
|
||||
groups: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
_links: {self: {href: 'test.com/uuid/1234567654321'}}
|
||||
_links: { self: { href: 'test.com/uuid/1234567654321' } }
|
||||
});
|
||||
initialState = {
|
||||
core: {
|
||||
@@ -54,7 +66,7 @@ describe('ProfilePageComponent', () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
authorizationService = jasmine.createSpyObj('authorizationService', { isAuthorized: canChangePassword });
|
||||
authService = jasmine.createSpyObj('authService', {
|
||||
getAuthenticatedUserFromStore: observableOf(user)
|
||||
});
|
||||
@@ -67,6 +79,9 @@ describe('ProfilePageComponent', () => {
|
||||
error: {},
|
||||
warning: {}
|
||||
});
|
||||
configurationService = jasmine.createSpyObj('configurationDataService', {
|
||||
findByPropertyName: jasmine.createSpy('findByPropertyName')
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
@@ -82,15 +97,8 @@ describe('ProfilePageComponent', () => {
|
||||
{ provide: EPersonDataService, useValue: epersonService },
|
||||
{ provide: NotificationsService, useValue: notificationsService },
|
||||
{ provide: AuthService, useValue: authService },
|
||||
{ provide: ConfigurationDataService, useValue: jasmine.createSpyObj('configurationDataService', {
|
||||
findByPropertyName: createSuccessfulRemoteDataObject$(Object.assign(new ConfigurationProperty(), {
|
||||
name: 'researcher-profile.entity-type',
|
||||
values: [
|
||||
'Person'
|
||||
]
|
||||
}))
|
||||
})},
|
||||
{ provide: AuthorizationDataService, useValue: jasmine.createSpyObj('authorizationService', { isAuthorized: canChangePassword }) },
|
||||
{ provide: ConfigurationDataService, useValue: configurationService },
|
||||
{ provide: AuthorizationDataService, useValue: authorizationService },
|
||||
provideMockStore({ initialState }),
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
@@ -100,148 +108,206 @@ describe('ProfilePageComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProfilePageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('updateProfile', () => {
|
||||
describe('when the metadata form returns false and the security form returns true', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: false
|
||||
describe('', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(validConfiguration));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('updateProfile', () => {
|
||||
describe('when the metadata form returns false and the security form returns true', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: false
|
||||
});
|
||||
spyOn(component, 'updateSecurity').and.returnValue(true);
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should not display a warning', () => {
|
||||
expect(notificationsService.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
spyOn(component, 'updateSecurity').and.returnValue(true);
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should not display a warning', () => {
|
||||
expect(notificationsService.warning).not.toHaveBeenCalled();
|
||||
describe('when the metadata form returns true and the security form returns false', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: true
|
||||
});
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should not display a warning', () => {
|
||||
expect(notificationsService.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadata form returns true and the security form returns true', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: true
|
||||
});
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should not display a warning', () => {
|
||||
expect(notificationsService.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadata form returns false and the security form returns false', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: false
|
||||
});
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should display a warning', () => {
|
||||
expect(notificationsService.warning).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadata form returns true and the security form returns false', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: true
|
||||
describe('updateSecurity', () => {
|
||||
describe('when no password value present', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
component.setPasswordValue('');
|
||||
|
||||
result = component.updateSecurity();
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not call epersonService.patch', () => {
|
||||
expect(epersonService.patch).not.toHaveBeenCalled();
|
||||
});
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should not display a warning', () => {
|
||||
expect(notificationsService.warning).not.toHaveBeenCalled();
|
||||
describe('when password is filled in, but the password is invalid', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
component.setPasswordValue('test');
|
||||
component.setInvalid(true);
|
||||
result = component.updateSecurity();
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(result).toEqual(true);
|
||||
expect(epersonService.patch).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when password is filled in, and is valid', () => {
|
||||
let result;
|
||||
let operations;
|
||||
|
||||
beforeEach(() => {
|
||||
component.setPasswordValue('testest');
|
||||
component.setInvalid(false);
|
||||
|
||||
operations = [{ op: 'add', path: '/password', value: 'testest' }];
|
||||
result = component.updateSecurity();
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return call epersonService.patch', () => {
|
||||
expect(epersonService.patch).toHaveBeenCalledWith(user, operations);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadata form returns true and the security form returns true', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: true
|
||||
describe('canChangePassword$', () => {
|
||||
describe('when the user is allowed to change their password', () => {
|
||||
beforeEach(() => {
|
||||
canChangePassword.next(true);
|
||||
});
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should not display a warning', () => {
|
||||
expect(notificationsService.warning).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadata form returns false and the security form returns false', () => {
|
||||
beforeEach(() => {
|
||||
component.metadataForm = jasmine.createSpyObj('metadataForm', {
|
||||
updateProfile: false
|
||||
it('should contain true', () => {
|
||||
getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: true });
|
||||
});
|
||||
|
||||
it('should show the security section on the page', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('.security-section'))).not.toBeNull();
|
||||
});
|
||||
component.updateProfile();
|
||||
});
|
||||
|
||||
it('should display a warning', () => {
|
||||
expect(notificationsService.warning).toHaveBeenCalled();
|
||||
describe('when the user is not allowed to change their password', () => {
|
||||
beforeEach(() => {
|
||||
canChangePassword.next(false);
|
||||
});
|
||||
|
||||
it('should contain false', () => {
|
||||
getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: false });
|
||||
});
|
||||
|
||||
it('should not show the security section on the page', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('.security-section'))).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateSecurity', () => {
|
||||
describe('when no password value present', () => {
|
||||
let result;
|
||||
describe('isResearcherProfileEnabled', () => {
|
||||
|
||||
describe('when configuration service return values', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.setPasswordValue('');
|
||||
configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(validConfiguration));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
result = component.updateSecurity();
|
||||
it('should return true', () => {
|
||||
const result = component.isResearcherProfileEnabled();
|
||||
const expected = cold('a', {
|
||||
a: true
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when configuration service return no values', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
configurationService.findByPropertyName.and.returnValue(createSuccessfulRemoteDataObject$(emptyConfiguration));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it('should not call epersonService.patch', () => {
|
||||
expect(epersonService.patch).not.toHaveBeenCalled();
|
||||
const result = component.isResearcherProfileEnabled();
|
||||
const expected = cold('a', {
|
||||
a: false
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when password is filled in, but the password is invalid', () => {
|
||||
let result;
|
||||
describe('when configuration service return an error', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.setPasswordValue('test');
|
||||
component.setInvalid(true);
|
||||
result = component.updateSecurity();
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(result).toEqual(true);
|
||||
expect(epersonService.patch).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when password is filled in, and is valid', () => {
|
||||
let result;
|
||||
let operations;
|
||||
|
||||
beforeEach(() => {
|
||||
component.setPasswordValue('testest');
|
||||
component.setInvalid(false);
|
||||
|
||||
operations = [{ op: 'add', path: '/password', value: 'testest' }];
|
||||
result = component.updateSecurity();
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(result).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return call epersonService.patch', () => {
|
||||
expect(epersonService.patch).toHaveBeenCalledWith(user, operations);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('canChangePassword$', () => {
|
||||
describe('when the user is allowed to change their password', () => {
|
||||
beforeEach(() => {
|
||||
canChangePassword.next(true);
|
||||
});
|
||||
|
||||
it('should contain true', () => {
|
||||
getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: true });
|
||||
});
|
||||
|
||||
it('should show the security section on the page', () => {
|
||||
configurationService.findByPropertyName.and.returnValue(createFailedRemoteDataObject$());
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('.security-section'))).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the user is not allowed to change their password', () => {
|
||||
beforeEach(() => {
|
||||
canChangePassword.next(false);
|
||||
});
|
||||
|
||||
it('should contain false', () => {
|
||||
getTestScheduler().expectObservable(component.canChangePassword$).toBe('(a)', { a: false });
|
||||
});
|
||||
|
||||
it('should not show the security section on the page', () => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.query(By.css('.security-section'))).toBeNull();
|
||||
it('should return false', () => {
|
||||
const result = component.isResearcherProfileEnabled();
|
||||
const expected = cold('a', {
|
||||
a: false
|
||||
});
|
||||
expect(result).toBeObservable(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { EPerson } from '../core/eperson/models/eperson.model';
|
||||
import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component';
|
||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||
@@ -9,18 +9,15 @@ import { RemoteData } from '../core/data/remote-data';
|
||||
import { PaginatedList } from '../core/data/paginated-list.model';
|
||||
import { filter, switchMap, tap } from 'rxjs/operators';
|
||||
import { EPersonDataService } from '../core/eperson/eperson-data.service';
|
||||
import {
|
||||
getAllSucceededRemoteData,
|
||||
getRemoteDataPayload,
|
||||
getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload
|
||||
} from '../core/shared/operators';
|
||||
import { getAllSucceededRemoteData, getFirstCompletedRemoteData, getRemoteDataPayload } from '../core/shared/operators';
|
||||
import { hasValue, isNotEmpty } from '../shared/empty.util';
|
||||
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||
import { AuthService } from '../core/auth/auth.service';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { AuthorizationDataService } from '../core/data/feature-authorization/authorization-data.service';
|
||||
import { FeatureID } from '../core/data/feature-authorization/feature-id';
|
||||
import {ConfigurationDataService} from '../core/data/configuration-data.service';
|
||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-profile-page',
|
||||
@@ -94,8 +91,10 @@ export class ProfilePageComponent implements OnInit {
|
||||
this.canChangePassword$ = this.user$.pipe(switchMap((user: EPerson) => this.authorizationService.isAuthorized(FeatureID.CanChangePassword, user._links.self.href)));
|
||||
|
||||
this.configurationService.findByPropertyName('researcher-profile.entity-type').pipe(
|
||||
getFirstSucceededRemoteDataPayload()
|
||||
).subscribe(() => this.isResearcherProfileEnabled$.next(true));
|
||||
getFirstCompletedRemoteData()
|
||||
).subscribe((configRD: RemoteData<ConfigurationProperty>) => {
|
||||
this.isResearcherProfileEnabled$.next(configRD.hasSucceeded && configRD.payload.values.length > 0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,8 +174,8 @@ export class ProfilePageComponent implements OnInit {
|
||||
/**
|
||||
* Returns true if the researcher profile feature is enabled, false otherwise.
|
||||
*/
|
||||
isResearcherProfileEnabled(){
|
||||
return this.isResearcherProfileEnabled$;
|
||||
isResearcherProfileEnabled(): Observable<boolean> {
|
||||
return this.isResearcherProfileEnabled$.asObservable();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -8884,11 +8884,6 @@ ngx-ui-switch@^11.0.1:
|
||||
resolved "https://registry.yarnpkg.com/ngx-ui-switch/-/ngx-ui-switch-11.0.1.tgz#c7f1e97ebe698f827a26f49951b50492b22c7839"
|
||||
integrity sha512-N8QYT/wW+xJdyh/aeebTSLPA6Sgrwp69H6KAcW0XZueg/LF+FKiqyG6Po/gFHq2gDhLikwyJEMpny8sudTI08w==
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
nice-napi@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b"
|
||||
|
Reference in New Issue
Block a user