mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 02:24:11 +00:00
Added tests and comments
This commit is contained in:
@@ -72,6 +72,12 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
||||
this.isAdmin$ = this.roleService.isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of available configuration depend on the user role
|
||||
*
|
||||
* @return {Observable<MyDSpaceConfigurationValueType[]>}
|
||||
* Emits the available configuration list
|
||||
*/
|
||||
public getAvailableConfigurationTypes(): Observable<MyDSpaceConfigurationValueType[]> {
|
||||
return combineLatest(this.isSubmitter$, this.isController$, this.isAdmin$).pipe(
|
||||
first(),
|
||||
@@ -87,6 +93,12 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the select options for the available configuration list
|
||||
*
|
||||
* @return {Observable<SearchConfigurationOption[]>}
|
||||
* Emits the select options list
|
||||
*/
|
||||
public getAvailableConfigurationOptions(): Observable<SearchConfigurationOption[]> {
|
||||
return this.getAvailableConfigurationTypes().pipe(
|
||||
first(),
|
||||
|
@@ -18,5 +18,8 @@ import { MyDSpaceGuard } from './my-dspace.guard';
|
||||
])
|
||||
]
|
||||
})
|
||||
/**
|
||||
* This module defines the default component to load when navigating to the mydspace page path.
|
||||
*/
|
||||
export class MyDspacePageRoutingModule {
|
||||
}
|
||||
|
@@ -60,6 +60,10 @@ import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'
|
||||
PoolMyDSpaceResultDetailElementComponent
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* This module handles all components that are necessary for the mydspace page
|
||||
*/
|
||||
export class MyDSpacePageModule {
|
||||
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import { Component, Input } from '@angular/core';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
|
||||
import { SortOptions } from '../../core/cache/models/sort-options.model';
|
||||
import { MyDSpaceResult } from '../my-dspace-result.model';
|
||||
import { SearchOptions } from '../../+search-page/search-options.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
@@ -11,9 +10,7 @@ import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
import { isEmpty } from '../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* This component renders a simple item page.
|
||||
* The route parameter 'id' is used to request the item it represents.
|
||||
* All fields of the item that should be displayed, are defined in its template.
|
||||
* Component that represents all results for mydspace page
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-my-dspace-results',
|
||||
@@ -24,13 +21,30 @@ import { isEmpty } from '../../shared/empty.util';
|
||||
]
|
||||
})
|
||||
export class MyDSpaceResultsComponent {
|
||||
|
||||
/**
|
||||
* The actual search result objects
|
||||
*/
|
||||
@Input() searchResults: RemoteData<PaginatedList<MyDSpaceResult<DSpaceObject>>>;
|
||||
|
||||
/**
|
||||
* The current configuration of the search
|
||||
*/
|
||||
@Input() searchConfig: SearchOptions;
|
||||
@Input() sortConfig: SortOptions;
|
||||
|
||||
/**
|
||||
* The current view mode for the search results
|
||||
*/
|
||||
@Input() viewMode: ViewMode;
|
||||
|
||||
/**
|
||||
* A boolean representing if search results entry are separated by a line
|
||||
*/
|
||||
hasBorder = true;
|
||||
|
||||
/**
|
||||
* Check if mydspace search results are loading
|
||||
*/
|
||||
isLoading() {
|
||||
return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading;
|
||||
}
|
||||
|
@@ -1,4 +1,15 @@
|
||||
/**
|
||||
* Represents a search configuration select option
|
||||
*/
|
||||
export interface SearchConfigurationOption {
|
||||
|
||||
/**
|
||||
* The select option value
|
||||
*/
|
||||
value: string;
|
||||
|
||||
/**
|
||||
* The select option label
|
||||
*/
|
||||
label: string;
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<select class="form-control"
|
||||
[compareWith]="compare"
|
||||
[(ngModel)]="selectedOption"
|
||||
(change)="onSelect($event)">
|
||||
(change)="onSelect()">
|
||||
<option *ngFor="let option of configurationList;" [ngValue]="option.value">
|
||||
{{option.label | translate}}
|
||||
</option>
|
||||
|
@@ -1,103 +1,109 @@
|
||||
// import { SearchService } from '../../search-service/search.service';
|
||||
// import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
// import { SearchSettingsComponent } from '../../search-settings/search-settings.component';
|
||||
// import { Observable } from 'rxjs/Observable';
|
||||
// import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
|
||||
// import { SortOptions } from '../../../core/cache/models/sort-options.model';
|
||||
// import { TranslateModule } from '@ngx-translate/core';
|
||||
// import { RouterTestingModule } from '@angular/router/testing';
|
||||
// import { ActivatedRoute } from '@angular/router';
|
||||
// import { SearchSidebarService } from '../../search-sidebar/search-sidebar.service';
|
||||
// import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
// import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe';
|
||||
// import { By } from '@angular/platform-browser';
|
||||
//
|
||||
// describe('SearchSettingsComponent', () => {
|
||||
//
|
||||
// let comp: SearchSettingsComponent;
|
||||
// let fixture: ComponentFixture<SearchSettingsComponent>;
|
||||
// let searchServiceObject: SearchService;
|
||||
//
|
||||
// const pagination: PaginationComponentOptions = new PaginationComponentOptions();
|
||||
// pagination.id = 'search-results-pagination';
|
||||
// pagination.currentPage = 1;
|
||||
// pagination.pageSize = 10;
|
||||
// const sort: SortOptions = new SortOptions();
|
||||
// const mockResults = [ 'test', 'data' ];
|
||||
// const searchServiceStub = {
|
||||
// searchOptions: { pagination: pagination, sort: sort },
|
||||
// search: () => mockResults
|
||||
// };
|
||||
// const queryParam = 'test query';
|
||||
// const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f';
|
||||
// const activatedRouteStub = {
|
||||
// queryParams: Observable.of({
|
||||
// query: queryParam,
|
||||
// scope: scopeParam
|
||||
// })
|
||||
// };
|
||||
//
|
||||
// const sidebarService = {
|
||||
// isCollapsed: Observable.of(true),
|
||||
// collapse: () => this.isCollapsed = Observable.of(true),
|
||||
// expand: () => this.isCollapsed = Observable.of(false)
|
||||
// }
|
||||
//
|
||||
// beforeEach(async(() => {
|
||||
// TestBed.configureTestingModule({
|
||||
// imports: [ TranslateModule.forRoot(), RouterTestingModule.withRoutes([]) ],
|
||||
// declarations: [ SearchSettingsComponent, EnumKeysPipe ],
|
||||
// providers: [
|
||||
// { provide: SearchService, useValue: searchServiceStub },
|
||||
//
|
||||
// { provide: ActivatedRoute, useValue: activatedRouteStub },
|
||||
// {
|
||||
// provide: SearchSidebarService,
|
||||
// useValue: sidebarService
|
||||
// },
|
||||
// ],
|
||||
// schemas: [ NO_ERRORS_SCHEMA ]
|
||||
// }).compileComponents();
|
||||
// }));
|
||||
//
|
||||
// beforeEach(() => {
|
||||
// fixture = TestBed.createComponent(SearchSettingsComponent);
|
||||
// comp = fixture.componentInstance;
|
||||
//
|
||||
// // SearchPageComponent test instance
|
||||
// fixture.detectChanges();
|
||||
// searchServiceObject = (comp as any).service;
|
||||
// spyOn(comp, 'reloadRPP');
|
||||
// spyOn(comp, 'reloadOrder');
|
||||
// spyOn(searchServiceObject, 'search').and.callThrough();
|
||||
//
|
||||
// });
|
||||
//
|
||||
// it('it should show the order settings with the respective selectable options', () => {
|
||||
// const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings'));
|
||||
// expect(orderSetting).toBeDefined();
|
||||
// const childElements = orderSetting.query(By.css('.form-control')).children;
|
||||
// expect(childElements.length).toEqual(2);
|
||||
//
|
||||
// });
|
||||
//
|
||||
// it('it should show the size settings with the respective selectable options', () => {
|
||||
// const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings'));
|
||||
// expect(pageSizeSetting).toBeDefined();
|
||||
// const childElements = pageSizeSetting.query(By.css('.form-control')).children;
|
||||
// expect(childElements.length).toEqual(7);
|
||||
// });
|
||||
//
|
||||
// it('should have the proper order value selected by default', () => {
|
||||
// const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings'));
|
||||
// const childElementToBeSelected = orderSetting.query(By.css('.form-control option[value="0"][selected="selected"]'))
|
||||
// expect(childElementToBeSelected).toBeDefined();
|
||||
// });
|
||||
//
|
||||
// it('should have the proper rpp value selected by default', () => {
|
||||
// const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings'));
|
||||
// const childElementToBeSelected = pageSizeSetting.query(By.css('.form-control option[value="10"][selected="selected"]'))
|
||||
// expect(childElementToBeSelected).toBeDefined();
|
||||
// });
|
||||
//
|
||||
// });
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { SearchSwitchConfigurationComponent } from './search-switch-configuration.component';
|
||||
import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub';
|
||||
import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
|
||||
import { NavigationExtras, Router } from '@angular/router';
|
||||
import { RouterStub } from '../../shared/testing/router-stub';
|
||||
import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type';
|
||||
import { SearchService } from '../search-service/search.service';
|
||||
|
||||
describe('SearchSwitchConfigurationComponent', () => {
|
||||
|
||||
let comp: SearchSwitchConfigurationComponent;
|
||||
let fixture: ComponentFixture<SearchSwitchConfigurationComponent>;
|
||||
let searchConfService: SearchConfigurationServiceStub;
|
||||
let select: any;
|
||||
|
||||
const searchServiceStub = jasmine.createSpyObj('SearchService', {
|
||||
getSearchLink: jasmine.createSpy('getSearchLink')
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [ SearchSwitchConfigurationComponent ],
|
||||
providers: [
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: SearchService, useValue: searchServiceStub },
|
||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
||||
],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SearchSwitchConfigurationComponent);
|
||||
comp = fixture.componentInstance;
|
||||
searchConfService = TestBed.get(SEARCH_CONFIG_SERVICE);
|
||||
|
||||
spyOn(searchConfService, 'getCurrentConfiguration').and.returnValue(observableOf(MyDSpaceConfigurationValueType.Workspace));
|
||||
|
||||
comp.configurationList = [
|
||||
{
|
||||
value: MyDSpaceConfigurationValueType.Workspace,
|
||||
label: 'workspace'
|
||||
},
|
||||
{
|
||||
value: MyDSpaceConfigurationValueType.Workflow,
|
||||
label: 'workflow'
|
||||
},
|
||||
];
|
||||
|
||||
// SearchSwitchConfigurationComponent test instance
|
||||
fixture.detectChanges();
|
||||
|
||||
});
|
||||
|
||||
it('should init the current configuration name', () => {
|
||||
expect(comp.selectedOption).toBe(MyDSpaceConfigurationValueType.Workspace);
|
||||
});
|
||||
|
||||
it('should display select field properly', () => {
|
||||
const selectField = fixture.debugElement.query(By.css('.form-control'));
|
||||
expect(selectField).toBeDefined();
|
||||
|
||||
const childElements = selectField.children;
|
||||
expect(childElements.length).toEqual(comp.configurationList.length);
|
||||
});
|
||||
|
||||
it('should call onSelect method when selecting an option', () => {
|
||||
fixture.whenStable().then(() => {
|
||||
spyOn(comp, 'onSelect');
|
||||
select = fixture.debugElement.query(By.css('select'));
|
||||
const selectEl = select.nativeElement;
|
||||
selectEl.value = selectEl.options[1].value; // <-- select a new value
|
||||
selectEl.dispatchEvent(new Event('change'));
|
||||
fixture.detectChanges();
|
||||
expect(comp.onSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should navigate to the route when selecting an option', () => {
|
||||
(comp as any).searchService.getSearchLink.and.returnValue(MYDSPACE_ROUTE);
|
||||
comp.selectedOption = MyDSpaceConfigurationValueType.Workflow;
|
||||
const navigationExtras: NavigationExtras = {
|
||||
queryParams: {configuration: MyDSpaceConfigurationValueType.Workflow},
|
||||
};
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
comp.onSelect();
|
||||
|
||||
expect((comp as any).router.navigate).toHaveBeenCalledWith([MYDSPACE_ROUTE], navigationExtras);
|
||||
});
|
||||
});
|
||||
|
@@ -4,16 +4,20 @@ import { NavigationExtras, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||
import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component';
|
||||
import { SearchConfigurationService } from '../search-service/search-configuration.service';
|
||||
import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type';
|
||||
import { SearchConfigurationOption } from './search-configuration-option.model';
|
||||
import { SearchService } from '../search-service/search.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search-switch-configuration',
|
||||
styleUrls: ['./search-switch-configuration.component.scss'],
|
||||
templateUrl: './search-switch-configuration.component.html',
|
||||
})
|
||||
/**
|
||||
* Represents a select that allow to switch over available search configurations
|
||||
*/
|
||||
export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit {
|
||||
|
||||
/**
|
||||
@@ -21,31 +25,53 @@ export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit {
|
||||
*/
|
||||
@Input() configurationList: SearchConfigurationOption[] = [];
|
||||
|
||||
/**
|
||||
* The selected option
|
||||
*/
|
||||
public selectedOption: string;
|
||||
|
||||
/**
|
||||
* Subscription to unsubscribe from
|
||||
*/
|
||||
private sub: Subscription;
|
||||
|
||||
constructor(private router: Router,
|
||||
private searchService: SearchService,
|
||||
@Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Init current configuration
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.searchConfigService.getCurrentConfiguration('default')
|
||||
.subscribe((currentConfiguration) => this.selectedOption = currentConfiguration);
|
||||
}
|
||||
|
||||
onSelect(event: Event) {
|
||||
/**
|
||||
* Init current configuration
|
||||
*/
|
||||
onSelect() {
|
||||
const navigationExtras: NavigationExtras = {
|
||||
queryParams: {configuration: this.selectedOption},
|
||||
};
|
||||
|
||||
this.router.navigate([MYDSPACE_ROUTE], navigationExtras);
|
||||
this.router.navigate([this.searchService.getSearchLink()], navigationExtras);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the select 'compareWith' method to tell Angular how to compare the values
|
||||
*
|
||||
* @param item1
|
||||
* @param item2
|
||||
*/
|
||||
compare(item1: MyDSpaceConfigurationValueType, item2: MyDSpaceConfigurationValueType) {
|
||||
return item1 === item2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the subscription is unsubscribed from when this component is destroyed
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
if (hasValue(this.sub)) {
|
||||
this.sub.unsubscribe();
|
||||
|
@@ -47,7 +47,9 @@ export class EPerson extends DSpaceObject {
|
||||
*/
|
||||
public selfRegistered: boolean;
|
||||
|
||||
/** Getter to retrieve the EPerson's full name as a string */
|
||||
/**
|
||||
* Getter to retrieve the EPerson's full name as a string
|
||||
*/
|
||||
get name(): string {
|
||||
return this.firstMetadataValue('eperson.firstname') + ' ' + this.firstMetadataValue('eperson.lastname');
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { autoserialize, inheritSerialization } from 'cerialize';
|
||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
||||
|
||||
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
|
||||
import { WorkspaceitemSectionsObject } from './workspaceitem-sections.model';
|
||||
@@ -17,6 +17,12 @@ export class NormalizedSubmissionObject<T extends DSpaceObject> extends Normaliz
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The workspaceitem/workflowitem identifier
|
||||
*/
|
||||
@autoserializeAs(String, 'id')
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* The workspaceitem/workflowitem last modified date
|
||||
*/
|
||||
|
@@ -25,6 +25,11 @@ export abstract class SubmissionObject extends DSpaceObject implements Cacheable
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The workspaceitem/workflowitem identifier
|
||||
*/
|
||||
uuid: string;
|
||||
|
||||
/**
|
||||
* The workspaceitem/workflowitem last modified date
|
||||
*/
|
||||
|
@@ -0,0 +1,151 @@
|
||||
import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Store, StoreModule } from '@ngrx/store';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { UserMenuComponent } from './user-menu.component';
|
||||
import { authReducer, AuthState } from '../../../core/auth/auth.reducer';
|
||||
import { AuthTokenInfo } from '../../../core/auth/models/auth-token-info.model';
|
||||
import { EPersonMock } from '../../testing/eperson-mock';
|
||||
import { AppState } from '../../../app.reducer';
|
||||
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('UserMenuComponent', () => {
|
||||
|
||||
let component: UserMenuComponent;
|
||||
let fixture: ComponentFixture<UserMenuComponent>;
|
||||
let deUserMenu: DebugElement;
|
||||
let authState: AuthState;
|
||||
let authStateLoading: AuthState;
|
||||
|
||||
function init() {
|
||||
authState = {
|
||||
authenticated: true,
|
||||
loaded: true,
|
||||
loading: false,
|
||||
authToken: new AuthTokenInfo('test_token'),
|
||||
user: EPersonMock
|
||||
};
|
||||
authStateLoading = {
|
||||
authenticated: true,
|
||||
loaded: true,
|
||||
loading: true,
|
||||
authToken: null,
|
||||
user: EPersonMock
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
StoreModule.forRoot(authReducer),
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [
|
||||
UserMenuComponent
|
||||
],
|
||||
schemas: [
|
||||
NO_ERRORS_SCHEMA
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
describe('when auth state is loading', () => {
|
||||
beforeEach(inject([Store], (store: Store<AppState>) => {
|
||||
store
|
||||
.subscribe((state) => {
|
||||
(state as any).core = Object.create({});
|
||||
(state as any).core.auth = authStateLoading;
|
||||
});
|
||||
|
||||
// create component and test fixture
|
||||
fixture = TestBed.createComponent(UserMenuComponent);
|
||||
|
||||
// get test component from the fixture
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
deUserMenu = fixture.debugElement.query(By.css('div'));
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should init component properly', () => {
|
||||
expect(component).toBeDefined();
|
||||
|
||||
expect(component.loading$).toBeObservable(cold('b', {
|
||||
b: true
|
||||
}));
|
||||
|
||||
expect(component.user$).toBeObservable(cold('c', {
|
||||
c: EPersonMock
|
||||
}));
|
||||
|
||||
expect(deUserMenu).toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('when auth state is not loading', () => {
|
||||
beforeEach(inject([Store], (store: Store<AppState>) => {
|
||||
store
|
||||
.subscribe((state) => {
|
||||
(state as any).core = Object.create({});
|
||||
(state as any).core.auth = authState;
|
||||
});
|
||||
|
||||
// create component and test fixture
|
||||
fixture = TestBed.createComponent(UserMenuComponent);
|
||||
|
||||
// get test component from the fixture
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
deUserMenu = fixture.debugElement.query(By.css('div'));
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should init component properly', () => {
|
||||
expect(component).toBeDefined();
|
||||
|
||||
expect(component.loading$).toBeObservable(cold('b', {
|
||||
b: false
|
||||
}));
|
||||
|
||||
expect(component.user$).toBeObservable(cold('c', {
|
||||
c: EPersonMock
|
||||
}));
|
||||
|
||||
expect(deUserMenu).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display user name and email', () => {
|
||||
const user = 'User Test (test@test.com)';
|
||||
const span = deUserMenu.query(By.css('.dropdown-item-text'));
|
||||
expect(span).toBeDefined();
|
||||
expect(span.nativeElement.innerHTML).toBe(user);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
});
|
@@ -8,6 +8,9 @@ import { AppState } from '../../../app.reducer';
|
||||
import { getAuthenticatedUser, isAuthenticationLoading } from '../../../core/auth/selectors';
|
||||
import { MYDSPACE_ROUTE } from '../../../+my-dspace-page/my-dspace-page.component';
|
||||
|
||||
/**
|
||||
* This component represents the user nav menu.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-user-menu',
|
||||
templateUrl: './user-menu.component.html',
|
||||
@@ -36,6 +39,9 @@ export class UserMenuComponent implements OnInit {
|
||||
constructor(private store: Store<AppState>) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all instance variables
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
|
||||
// set loading
|
||||
|
@@ -2,7 +2,7 @@
|
||||
[className]="'btn btn-success ' + wrapperClass"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.approve_help' | translate}}"
|
||||
[disabled]="processingApprove"
|
||||
(click)="click()">
|
||||
(click)="confirmApprove()">
|
||||
<span *ngIf="processingApprove"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!processingApprove"><i class="fa fa-thumbs-up"></i> {{'submission.workflow.tasks.claimed.approve' | translate}}</span>
|
||||
</button>
|
||||
|
@@ -0,0 +1,65 @@
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { ClaimedTaskActionsApproveComponent } from './claimed-task-actions-approve.component';
|
||||
import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
|
||||
|
||||
let component: ClaimedTaskActionsApproveComponent;
|
||||
let fixture: ComponentFixture<ClaimedTaskActionsApproveComponent>;
|
||||
|
||||
describe('ClaimedTaskActionsApproveComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [ClaimedTaskActionsApproveComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ClaimedTaskActionsApproveComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClaimedTaskActionsApproveComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
});
|
||||
|
||||
it('should display approve button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('.btn-success'));
|
||||
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display spin icon when approve is pending', () => {
|
||||
component.processingApprove = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const span = fixture.debugElement.query(By.css('.btn-success .fa-spin'));
|
||||
|
||||
expect(span).toBeDefined();
|
||||
});
|
||||
|
||||
it('should emit approve event', () => {
|
||||
spyOn(component.approve, 'emit');
|
||||
|
||||
component.confirmApprove();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.approve.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
@@ -7,13 +7,26 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
})
|
||||
|
||||
export class ClaimedTaskActionsApproveComponent {
|
||||
|
||||
/**
|
||||
* A boolean representing if a reject operation is pending
|
||||
*/
|
||||
@Input() processingApprove: boolean;
|
||||
@Input() taskId: string;
|
||||
|
||||
/**
|
||||
* CSS classes to append to reject button
|
||||
*/
|
||||
@Input() wrapperClass: string;
|
||||
|
||||
/**
|
||||
* An event fired when a approve action is confirmed.
|
||||
*/
|
||||
@Output() approve: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
click() {
|
||||
/**
|
||||
* Emit approve event
|
||||
*/
|
||||
confirmApprove() {
|
||||
this.approve.emit();
|
||||
}
|
||||
}
|
||||
|
@@ -8,23 +8,16 @@
|
||||
</a>
|
||||
|
||||
<ds-claimed-task-actions-approve [processingApprove]="(processingApprove$ | async)"
|
||||
[taskId]="object.id"
|
||||
[wrapperClass]="'mt-1 mb-3'"
|
||||
(approve)="approve()"></ds-claimed-task-actions-approve>
|
||||
|
||||
<ds-claimed-task-actions-reject [processingReject]="(processingReject$ | async)"
|
||||
[taskId]="object.id"
|
||||
[wrapperClass]="'mt-1 mb-3'"
|
||||
(reject)="reject($event)"></ds-claimed-task-actions-reject>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-secondary mt-1 mb-3"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.return_help' | translate}}"
|
||||
[disabled]="(processingReturnToPool$ | async)"
|
||||
(click)="returnToPool()">
|
||||
<span *ngIf="(processingReturnToPool$ | async)"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!(processingReturnToPool$ | async)"><i class="fa fa-undo"></i> {{'submission.workflow.tasks.claimed.return' | translate}}</span>
|
||||
</button>
|
||||
<ds-claimed-task-actions-return-to-pool [processingReturnToPool]="(processingReturnToPool$ | async)"
|
||||
[wrapperClass]="'mt-1 mb-3'"
|
||||
(returnToPool)="returnToPool()"></ds-claimed-task-actions-return-to-pool>
|
||||
|
||||
<ds-message-board
|
||||
[dso]="workflowitem$ | async"
|
||||
|
@@ -0,0 +1,269 @@
|
||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||
import { RouterStub } from '../../testing/router-stub';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
|
||||
import { ClaimedTaskActionsComponent } from './claimed-task-actions.component';
|
||||
import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
|
||||
import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
|
||||
|
||||
let component: ClaimedTaskActionsComponent;
|
||||
let fixture: ComponentFixture<ClaimedTaskActionsComponent>;
|
||||
|
||||
let mockObject: ClaimedTask;
|
||||
let notificationsServiceStub: NotificationsServiceStub;
|
||||
let router: RouterStub;
|
||||
|
||||
const mockDataService = jasmine.createSpyObj('PoolTaskDataService', {
|
||||
approveTask: jasmine.createSpy('approveTask'),
|
||||
rejectTask: jasmine.createSpy('rejectTask'),
|
||||
returnToPoolTask: jasmine.createSpy('returnToPoolTask'),
|
||||
});
|
||||
|
||||
const item = Object.assign(new Item(), {
|
||||
bitstreams: observableOf({}),
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'This is just another title'
|
||||
}
|
||||
],
|
||||
'dc.type': [
|
||||
{
|
||||
language: null,
|
||||
value: 'Article'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
language: null,
|
||||
value: '2015-06-26'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
const rdItem = new RemoteData(false, false, true, null, item);
|
||||
const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
|
||||
const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
|
||||
mockObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' });
|
||||
|
||||
describe('ClaimedTaskActionsComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [ClaimedTaskActionsComponent],
|
||||
providers: [
|
||||
{ provide: Injector, useValue: {} },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: ClaimedTaskDataService, useValue: mockDataService },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ClaimedTaskActionsComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClaimedTaskActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = mockObject;
|
||||
notificationsServiceStub = TestBed.get(NotificationsService);
|
||||
router = TestBed.get(Router);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
});
|
||||
|
||||
it('should init objects properly', () => {
|
||||
component.object = null;
|
||||
component.initObjects(mockObject);
|
||||
|
||||
expect(component.object).toEqual(mockObject);
|
||||
|
||||
expect(component.workflowitem$).toBeObservable(cold('(b|)', {
|
||||
b: rdWorkflowitem.payload
|
||||
}))
|
||||
});
|
||||
|
||||
it('should display edit task button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('.btn-info'));
|
||||
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call approveTask method when approving a task', fakeAsync(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.approve();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(mockDataService.approveTask).toHaveBeenCalledWith(mockObject.id);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should display a success notification on approve success', async(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.approve();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reload page on approve success', async(() => {
|
||||
spyOn(router, 'navigateByUrl');
|
||||
router.url = 'test.url/test';
|
||||
mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.approve();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display an error notification on approve failure', async(() => {
|
||||
mockDataService.approveTask.and.returnValue(observableOf({hasSucceeded: false}));
|
||||
|
||||
component.approve();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call rejectTask method when rejecting a task', fakeAsync(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.reject('test reject');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(mockDataService.rejectTask).toHaveBeenCalledWith('test reject', mockObject.id);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should display a success notification on reject success', async(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.reject('test reject');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reload page on reject success', async(() => {
|
||||
spyOn(router, 'navigateByUrl');
|
||||
router.url = 'test.url/test';
|
||||
mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.reject('test reject');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display an error notification on reject failure', async(() => {
|
||||
mockDataService.rejectTask.and.returnValue(observableOf({hasSucceeded: false}));
|
||||
|
||||
component.reject('test reject');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call returnToPoolTask method when returning a task to pool', fakeAsync(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.returnToPool();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(mockDataService.returnToPoolTask).toHaveBeenCalledWith( mockObject.id);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should display a success notification on return to pool success', async(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.returnToPool();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reload page on return to pool success', async(() => {
|
||||
spyOn(router, 'navigateByUrl');
|
||||
router.url = 'test.url/test';
|
||||
mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.returnToPool();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display an error notification on return to pool failure', async(() => {
|
||||
mockDataService.returnToPoolTask.and.returnValue(observableOf({hasSucceeded: false}));
|
||||
|
||||
component.returnToPool();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
});
|
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectorRef, Component, Injector, Input, OnInit } from '@angular/core';
|
||||
import { Component, Injector, Input, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
@@ -8,41 +8,75 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service';
|
||||
import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model';
|
||||
import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationOptions } from '../../notifications/models/notification-options.model';
|
||||
import { isNotUndefined } from '../../empty.util';
|
||||
import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { MyDSpaceActionsComponent } from '../mydspace-actions';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
|
||||
/**
|
||||
* This component represents mydspace actions related to ClaimedTask object.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-claimed-task-actions',
|
||||
styleUrls: ['./claimed-task-actions.component.scss'],
|
||||
templateUrl: './claimed-task-actions.component.html',
|
||||
})
|
||||
|
||||
export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<ClaimedTask, ClaimedTaskDataService> implements OnInit {
|
||||
|
||||
/**
|
||||
* The ClaimedTask object
|
||||
*/
|
||||
@Input() object: ClaimedTask;
|
||||
|
||||
/**
|
||||
* The workflowitem object that belonging to the ClaimedTask object
|
||||
*/
|
||||
public workflowitem$: Observable<Workflowitem>;
|
||||
|
||||
/**
|
||||
* A boolean representing if an approve operation is pending
|
||||
*/
|
||||
public processingApprove$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* A boolean representing if a reject operation is pending
|
||||
*/
|
||||
public processingReject$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* A boolean representing if a return to pool operation is pending
|
||||
*/
|
||||
public processingReturnToPool$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {Injector} injector
|
||||
* @param {Router} router
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(protected injector: Injector,
|
||||
protected router: Router,
|
||||
private cd: ChangeDetectorRef,
|
||||
private notificationsService: NotificationsService,
|
||||
private translate: TranslateService) {
|
||||
super(ResourceType.ClaimedTask, injector, router)
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService) {
|
||||
super(ResourceType.ClaimedTask, injector, router, notificationsService, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize objects
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.initObjects(this.object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the ClaimedTask and Workflowitem objects
|
||||
*
|
||||
* @param {PoolTask} object
|
||||
*/
|
||||
initObjects(object: ClaimedTask) {
|
||||
this.object = object;
|
||||
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<Workflowitem>>).pipe(
|
||||
@@ -50,44 +84,40 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
||||
map((rd: RemoteData<Workflowitem>) => rd.payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve the task.
|
||||
*/
|
||||
approve() {
|
||||
this.processingApprove$.next(true);
|
||||
this.objectDataService.approveTask(this.object.id)
|
||||
.subscribe((res: ProcessTaskResponse) => {
|
||||
this.responseHandle(res);
|
||||
this.processingApprove$.next(false);
|
||||
this.handleActionResponse(res.hasSucceeded);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject the task.
|
||||
*/
|
||||
reject(reason) {
|
||||
this.processingReject$.next(true);
|
||||
this.objectDataService.rejectTask(reason, this.object.id)
|
||||
.subscribe((res: ProcessTaskResponse) => {
|
||||
this.responseHandle(res);
|
||||
this.processingReject$.next(false);
|
||||
this.handleActionResponse(res.hasSucceeded);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return task to the pool.
|
||||
*/
|
||||
returnToPool() {
|
||||
this.processingReturnToPool$.next(true);
|
||||
this.objectDataService.returnToPoolTask(this.object.id)
|
||||
.subscribe((res: ProcessTaskResponse) => {
|
||||
this.responseHandle(res);
|
||||
this.processingReturnToPool$.next(false);
|
||||
this.handleActionResponse(res.hasSucceeded);
|
||||
});
|
||||
}
|
||||
|
||||
private responseHandle(res: ProcessTaskResponse) {
|
||||
this.processingApprove$.next(false);
|
||||
this.processingReject$.next(false);
|
||||
this.processingReturnToPool$.next(false);
|
||||
if (res.hasSucceeded) {
|
||||
this.reload();
|
||||
this.notificationsService.success(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.success'),
|
||||
new NotificationOptions(5000, false));
|
||||
} else {
|
||||
this.notificationsService.error(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.error'),
|
||||
new NotificationOptions(20000, true));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -21,14 +21,12 @@
|
||||
<div class="alert alert-info" role="alert">
|
||||
{{'submission.workflow.tasks.claimed.reject.reason.info' | translate}}
|
||||
</div>
|
||||
<form (ngSubmit)="click(rejectModal);" [formGroup]="rejectForm" >
|
||||
<textarea
|
||||
style="width: 100%"
|
||||
<form (ngSubmit)="confirmReject(rejectModal);" [formGroup]="rejectForm" >
|
||||
<textarea style="width: 100%"
|
||||
formControlName="reason"
|
||||
rows="4"
|
||||
placeholder="{{'submission.workflow.tasks.claimed.reject.reason.placeholder' | translate}}"></textarea>
|
||||
<button
|
||||
id="btn-chat"
|
||||
<button id="btn-chat"
|
||||
class="btn btn-danger btn-lg btn-block mt-3"
|
||||
[disabled]="!rejectForm.valid || processingReject"
|
||||
type="submit">
|
||||
|
@@ -0,0 +1,108 @@
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { ClaimedTaskActionsRejectComponent } from './claimed-task-actions-reject.component';
|
||||
import { MockTranslateLoader } from '../../../mocks/mock-translate-loader';
|
||||
|
||||
let component: ClaimedTaskActionsRejectComponent;
|
||||
let fixture: ComponentFixture<ClaimedTaskActionsRejectComponent>;
|
||||
let formBuilder: FormBuilder;
|
||||
let modalService: NgbModal;
|
||||
|
||||
describe('ClaimedTaskActionsRejectComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NgbModule.forRoot(),
|
||||
ReactiveFormsModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [ClaimedTaskActionsRejectComponent],
|
||||
providers: [
|
||||
FormBuilder,
|
||||
NgbModal
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ClaimedTaskActionsRejectComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClaimedTaskActionsRejectComponent);
|
||||
component = fixture.componentInstance;
|
||||
formBuilder = TestBed.get(FormBuilder);
|
||||
modalService = TestBed.get(NgbModal);
|
||||
component.modalRef = modalService.open('ok');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
modalService = null;
|
||||
formBuilder = null;
|
||||
});
|
||||
|
||||
it('should init reject form properly', () => {
|
||||
expect(component.rejectForm).toBeDefined();
|
||||
expect(component.rejectForm instanceof FormGroup).toBeTruthy();
|
||||
expect(component.rejectForm.controls.reason).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display reject button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('.btn-danger'));
|
||||
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display spin icon when reject is pending', () => {
|
||||
component.processingReject = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const span = fixture.debugElement.query(By.css('.btn-danger .fa-spin'));
|
||||
|
||||
expect(span).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call openRejectModal on reject button click', fakeAsync(() => {
|
||||
spyOn(component.rejectForm, 'reset');
|
||||
const btn = fixture.debugElement.query(By.css('.btn-danger'));
|
||||
btn.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.rejectForm.reset).toHaveBeenCalled();
|
||||
expect(component.modalRef).toBeDefined();
|
||||
|
||||
component.modalRef.close()
|
||||
}));
|
||||
|
||||
it('should call confirmReject on form submit', fakeAsync(() => {
|
||||
spyOn(component.reject, 'emit');
|
||||
|
||||
const btn = fixture.debugElement.query(By.css('.btn-danger'));
|
||||
btn.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.modalRef).toBeDefined();
|
||||
|
||||
const form = ((document as any).querySelector('form'));
|
||||
form.dispatchEvent(new Event('ngSubmit'));
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.reject.emit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
}));
|
||||
});
|
@@ -10,18 +10,45 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
|
||||
})
|
||||
|
||||
export class ClaimedTaskActionsRejectComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* A boolean representing if a reject operation is pending
|
||||
*/
|
||||
@Input() processingReject: boolean;
|
||||
@Input() taskId: string;
|
||||
|
||||
/**
|
||||
* CSS classes to append to reject button
|
||||
*/
|
||||
@Input() wrapperClass: string;
|
||||
|
||||
/**
|
||||
* An event fired when a reject action is confirmed.
|
||||
* Event's payload equals to reject reason.
|
||||
*/
|
||||
@Output() reject: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
/**
|
||||
* The reject form group
|
||||
*/
|
||||
public rejectForm: FormGroup;
|
||||
|
||||
/**
|
||||
* Reference to NgbModal
|
||||
*/
|
||||
public modalRef: NgbModalRef;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {FormBuilder} formBuilder
|
||||
* @param {NgbModal} modalService
|
||||
*/
|
||||
constructor(private formBuilder: FormBuilder, private modalService: NgbModal) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize form
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.rejectForm = this.formBuilder.group({
|
||||
reason: ['', Validators.required]
|
||||
@@ -29,15 +56,23 @@ export class ClaimedTaskActionsRejectComponent implements OnInit {
|
||||
|
||||
}
|
||||
|
||||
click() {
|
||||
/**
|
||||
* Close modal and emit reject event
|
||||
*/
|
||||
confirmReject() {
|
||||
this.processingReject = true;
|
||||
this.modalRef.close('Send Button');
|
||||
const reason = this.rejectForm.get('reason').value;
|
||||
this.reject.emit(reason);
|
||||
}
|
||||
|
||||
openRejectModal(rejectModal) {
|
||||
/**
|
||||
* Open modal
|
||||
*
|
||||
* @param content
|
||||
*/
|
||||
openRejectModal(content: any) {
|
||||
this.rejectForm.reset();
|
||||
this.modalRef = this.modalService.open(rejectModal);
|
||||
this.modalRef = this.modalService.open(content);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,96 @@
|
||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
|
||||
import { RouterStub } from '../../testing/router-stub';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ItemActionsComponent } from './item-actions.component';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||
|
||||
let component: ItemActionsComponent;
|
||||
let fixture: ComponentFixture<ItemActionsComponent>;
|
||||
|
||||
let mockObject: Item;
|
||||
|
||||
const mockDataService = {};
|
||||
|
||||
mockObject = Object.assign(new Item(), {
|
||||
bitstreams: observableOf({}),
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'This is just another title'
|
||||
}
|
||||
],
|
||||
'dc.type': [
|
||||
{
|
||||
language: null,
|
||||
value: 'Article'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
language: null,
|
||||
value: '2015-06-26'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
describe('ItemActionsComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [ItemActionsComponent],
|
||||
providers: [
|
||||
{ provide: Injector, useValue: {} },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: ItemDataService, useValue: mockDataService },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ItemActionsComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ItemActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = mockObject;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
});
|
||||
|
||||
it('should init object properly', () => {
|
||||
component.object = null;
|
||||
component.initObjects(mockObject);
|
||||
|
||||
expect(component.object).toEqual(mockObject);
|
||||
});
|
||||
|
||||
});
|
@@ -1,11 +1,17 @@
|
||||
import { Component, Injector, Input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { MyDSpaceActionsComponent } from '../mydspace-actions';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { ItemDataService } from '../../../core/data/item-data.service';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
|
||||
/**
|
||||
* This component represents mydspace actions related to Item object.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-item-actions',
|
||||
styleUrls: ['./item-actions.component.scss'],
|
||||
@@ -13,13 +19,32 @@ import { Item } from '../../../core/shared/item.model';
|
||||
})
|
||||
|
||||
export class ItemActionsComponent extends MyDSpaceActionsComponent<Item, ItemDataService> {
|
||||
|
||||
/**
|
||||
* The Item object
|
||||
*/
|
||||
@Input() object: Item;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {Injector} injector
|
||||
* @param {Router} router
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(protected injector: Injector,
|
||||
protected router: Router) {
|
||||
super(ResourceType.Workspaceitem, injector, router);
|
||||
protected router: Router,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService) {
|
||||
super(ResourceType.Item, injector, router, notificationsService, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the target object
|
||||
*
|
||||
* @param {Item} object
|
||||
*/
|
||||
initObjects(object: Item) {
|
||||
this.object = object;
|
||||
}
|
||||
|
@@ -5,10 +5,17 @@ import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.servi
|
||||
import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service';
|
||||
import { WorkflowitemDataService } from '../../core/submission/workflowitem-data.service';
|
||||
import { CacheableObject } from '../../core/cache/object-cache.reducer';
|
||||
import { ItemDataService } from '../../core/data/item-data.service';
|
||||
|
||||
/**
|
||||
* Class to return DataService for given ResourceType
|
||||
*/
|
||||
export class MydspaceActionsServiceFactory<T extends CacheableObject, TService extends DataService<T>> {
|
||||
public getConstructor(type: ResourceType): TService {
|
||||
switch (type) {
|
||||
case ResourceType.Item: {
|
||||
return ItemDataService as any;
|
||||
}
|
||||
case ResourceType.Workspaceitem: {
|
||||
return WorkspaceitemDataService as any;
|
||||
}
|
||||
|
@@ -8,19 +8,55 @@ import { RemoteData } from '../../core/data/remote-data';
|
||||
import { DataService } from '../../core/data/data.service';
|
||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||
import { ResourceType } from '../../core/shared/resource-type';
|
||||
import { NotificationOptions } from '../notifications/models/notification-options.model';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
/**
|
||||
* Abstract class for all different representations of mydspace actions
|
||||
*/
|
||||
export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService extends DataService<T>> {
|
||||
|
||||
/**
|
||||
* The target mydspace object
|
||||
*/
|
||||
@Input() abstract object: T;
|
||||
|
||||
/**
|
||||
* Instance of DataService realted to mydspace object
|
||||
*/
|
||||
protected objectDataService: TService;
|
||||
|
||||
constructor(protected objectType: ResourceType, protected injector: Injector, protected router: Router) {
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {ResourceType} objectType
|
||||
* @param {Injector} injector
|
||||
* @param {Router} router
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(
|
||||
protected objectType: ResourceType,
|
||||
protected injector: Injector,
|
||||
protected router: Router,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService) {
|
||||
const factory = new MydspaceActionsServiceFactory<T, TService>();
|
||||
this.objectDataService = injector.get(factory.getConstructor(objectType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method called to init the target object
|
||||
*
|
||||
* @param {T} object
|
||||
*/
|
||||
abstract initObjects(object: T): void;
|
||||
|
||||
reload() {
|
||||
/**
|
||||
* Refresh current page
|
||||
*/
|
||||
reload(): void {
|
||||
// override the route reuse strategy
|
||||
this.router.routeReuseStrategy.shouldReuseRoute = () => {
|
||||
return false;
|
||||
@@ -30,12 +66,34 @@ export abstract class MyDSpaceActionsComponent<T extends DSpaceObject, TService
|
||||
this.router.navigateByUrl(url);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
// override the object with a refreshed one
|
||||
/**
|
||||
* Override the target object with a refreshed one
|
||||
*/
|
||||
refresh(): void {
|
||||
// find object by id
|
||||
this.objectDataService.findById(this.object.id).pipe(
|
||||
find((rd: RemoteData<T>) => rd.hasSucceeded)
|
||||
).subscribe((rd: RemoteData<T>) => {
|
||||
this.initObjects(rd.payload as T);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle action response and show properly notification
|
||||
*
|
||||
* @param result
|
||||
* true on success, false otherwise
|
||||
*/
|
||||
handleActionResponse(result: boolean): void {
|
||||
if (result) {
|
||||
this.reload();
|
||||
this.notificationsService.success(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.success'),
|
||||
new NotificationOptions(5000, false));
|
||||
} else {
|
||||
this.notificationsService.error(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.error'),
|
||||
new NotificationOptions(20000, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,170 @@
|
||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||
import { RouterStub } from '../../testing/router-stub';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service';
|
||||
import { PoolTaskActionsComponent } from './pool-task-actions.component';
|
||||
import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
|
||||
import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
|
||||
|
||||
let component: PoolTaskActionsComponent;
|
||||
let fixture: ComponentFixture<PoolTaskActionsComponent>;
|
||||
|
||||
let mockObject: PoolTask;
|
||||
let notificationsServiceStub: NotificationsServiceStub;
|
||||
let router: RouterStub;
|
||||
|
||||
const mockDataService = jasmine.createSpyObj('PoolTaskDataService', {
|
||||
claimTask: jasmine.createSpy('claimTask')
|
||||
});
|
||||
|
||||
const item = Object.assign(new Item(), {
|
||||
bitstreams: observableOf({}),
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'This is just another title'
|
||||
}
|
||||
],
|
||||
'dc.type': [
|
||||
{
|
||||
language: null,
|
||||
value: 'Article'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
language: null,
|
||||
value: '2015-06-26'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
const rdItem = new RemoteData(false, false, true, null, item);
|
||||
const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) });
|
||||
const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem);
|
||||
mockObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' });
|
||||
|
||||
describe('PoolTaskActionsComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [PoolTaskActionsComponent],
|
||||
providers: [
|
||||
{ provide: Injector, useValue: {} },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: PoolTaskDataService, useValue: mockDataService },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(PoolTaskActionsComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PoolTaskActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = mockObject;
|
||||
notificationsServiceStub = TestBed.get(NotificationsService);
|
||||
router = TestBed.get(Router);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
});
|
||||
|
||||
it('should init objects properly', () => {
|
||||
component.object = null;
|
||||
component.initObjects(mockObject);
|
||||
|
||||
expect(component.object).toEqual(mockObject);
|
||||
|
||||
expect(component.workflowitem$).toBeObservable(cold('(b|)', {
|
||||
b: rdWorkflowitem.payload
|
||||
}))
|
||||
});
|
||||
|
||||
it('should display claim task button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('.btn-info'));
|
||||
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call claimTask method on claim', fakeAsync(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.claim();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(mockDataService.claimTask).toHaveBeenCalledWith(mockObject.id);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should display a success notification on claim success', async(() => {
|
||||
spyOn(component, 'reload');
|
||||
mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.claim();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reload page on claim success', async(() => {
|
||||
spyOn(router, 'navigateByUrl');
|
||||
router.url = 'test.url/test';
|
||||
mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true}));
|
||||
|
||||
component.claim();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display an error notification on claim failure', async(() => {
|
||||
mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: false}));
|
||||
|
||||
component.claim();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
@@ -10,35 +10,64 @@ import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-res
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PoolTask } from '../../../core/tasks/models/pool-task-object.model';
|
||||
import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationOptions } from '../../notifications/models/notification-options.model';
|
||||
import { isNotUndefined } from '../../empty.util';
|
||||
import { MyDSpaceActionsComponent } from '../mydspace-actions';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
|
||||
/**
|
||||
* This component represents mydspace actions related to PoolTask object.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-pool-task-actions',
|
||||
styleUrls: ['./pool-task-actions.component.scss'],
|
||||
templateUrl: './pool-task-actions.component.html',
|
||||
})
|
||||
|
||||
export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask, PoolTaskDataService> {
|
||||
|
||||
/**
|
||||
* The PoolTask object
|
||||
*/
|
||||
@Input() object: PoolTask;
|
||||
|
||||
/**
|
||||
* A boolean representing if a claim operation is pending
|
||||
* @type {BehaviorSubject<boolean>}
|
||||
*/
|
||||
public processingClaim$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* The workflowitem object that belonging to the PoolTask object
|
||||
*/
|
||||
public workflowitem$: Observable<Workflowitem>;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {Injector} injector
|
||||
* @param {Router} router
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(protected injector: Injector,
|
||||
protected router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private translate: TranslateService) {
|
||||
super(ResourceType.PoolTask, injector, router);
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService) {
|
||||
super(ResourceType.PoolTask, injector, router, notificationsService, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize objects
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.initObjects(this.object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the PoolTask and Workflowitem objects
|
||||
*
|
||||
* @param {PoolTask} object
|
||||
*/
|
||||
initObjects(object: PoolTask) {
|
||||
this.object = object;
|
||||
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<Workflowitem>>).pipe(
|
||||
@@ -46,27 +75,15 @@ export class PoolTaskActionsComponent extends MyDSpaceActionsComponent<PoolTask,
|
||||
map((rd: RemoteData<Workflowitem>) => rd.payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim the task.
|
||||
*/
|
||||
claim() {
|
||||
this.processingClaim$.next(true);
|
||||
this.objectDataService.claimTask(this.object.id)
|
||||
.subscribe((res: ProcessTaskResponse) => {
|
||||
this.responseHandle(res);
|
||||
this.handleActionResponse(res.hasSucceeded);
|
||||
this.processingClaim$.next(false);
|
||||
});
|
||||
}
|
||||
|
||||
private responseHandle(res: ProcessTaskResponse) {
|
||||
if (res.hasSucceeded) {
|
||||
this.processingClaim$.next(false);
|
||||
this.reload();
|
||||
this.notificationsService.success(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.success'),
|
||||
new NotificationOptions(5000, false));
|
||||
} else {
|
||||
this.processingClaim$.next(false);
|
||||
this.notificationsService.error(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.error'),
|
||||
new NotificationOptions(20000, true));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,98 @@
|
||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
|
||||
import { RouterStub } from '../../testing/router-stub';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
|
||||
import { WorkflowitemActionsComponent } from './workflowitem-actions.component';
|
||||
import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||
|
||||
let component: WorkflowitemActionsComponent;
|
||||
let fixture: ComponentFixture<WorkflowitemActionsComponent>;
|
||||
|
||||
let mockObject: Workflowitem;
|
||||
|
||||
const mockDataService = {};
|
||||
|
||||
const item = Object.assign(new Item(), {
|
||||
bitstreams: observableOf({}),
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'This is just another title'
|
||||
}
|
||||
],
|
||||
'dc.type': [
|
||||
{
|
||||
language: null,
|
||||
value: 'Article'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
language: null,
|
||||
value: '2015-06-26'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
const rd = new RemoteData(false, false, true, null, item);
|
||||
mockObject = Object.assign(new Workflowitem(), { item: observableOf(rd), id: '1234', uuid: '1234' });
|
||||
|
||||
describe('WorkflowitemActionsComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [WorkflowitemActionsComponent],
|
||||
providers: [
|
||||
{ provide: Injector, useValue: {} },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: WorkflowitemDataService, useValue: mockDataService },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(WorkflowitemActionsComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WorkflowitemActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
});
|
||||
|
||||
it('should init object properly', () => {
|
||||
component.initObjects(mockObject);
|
||||
|
||||
expect(component.object).toEqual(mockObject);
|
||||
});
|
||||
|
||||
});
|
@@ -1,25 +1,49 @@
|
||||
import { Component, Injector, Input } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { MyDSpaceActionsComponent } from '../mydspace-actions';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { Workflowitem } from '../../../core/submission/models/workflowitem.model';
|
||||
import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
|
||||
/**
|
||||
* This component represents mydspace actions related to Workflowitem object.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-workflowitem-actions',
|
||||
styleUrls: ['./workflowitem-actions.component.scss'],
|
||||
templateUrl: './workflowitem-actions.component.html',
|
||||
})
|
||||
|
||||
export class WorkflowitemActionsComponent extends MyDSpaceActionsComponent<Workflowitem, WorkflowitemDataService> {
|
||||
|
||||
/**
|
||||
* The Workflowitem object
|
||||
*/
|
||||
@Input() object: Workflowitem;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {Injector} injector
|
||||
* @param {Router} router
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(protected injector: Injector,
|
||||
protected router: Router) {
|
||||
super(ResourceType.Workflowitem, injector, router);
|
||||
protected router: Router,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService) {
|
||||
super(ResourceType.Workflowitem, injector, router, notificationsService, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the target object
|
||||
*
|
||||
* @param {Workflowitem} object
|
||||
*/
|
||||
initObjects(object: Workflowitem) {
|
||||
this.object = object;
|
||||
}
|
||||
|
@@ -0,0 +1,163 @@
|
||||
import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { MockTranslateLoader } from '../../mocks/mock-translate-loader';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationsServiceStub } from '../../testing/notifications-service-stub';
|
||||
import { RouterStub } from '../../testing/router-stub';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
|
||||
import { WorkspaceitemActionsComponent } from './workspaceitem-actions.component';
|
||||
import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
|
||||
|
||||
let component: WorkspaceitemActionsComponent;
|
||||
let fixture: ComponentFixture<WorkspaceitemActionsComponent>;
|
||||
|
||||
let mockObject: Workspaceitem;
|
||||
let notificationsServiceStub: NotificationsServiceStub;
|
||||
|
||||
const mockDataService = jasmine.createSpyObj('WorkspaceitemDataService', {
|
||||
delete: jasmine.createSpy('delete')
|
||||
});
|
||||
|
||||
const item = Object.assign(new Item(), {
|
||||
bitstreams: observableOf({}),
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'This is just another title'
|
||||
}
|
||||
],
|
||||
'dc.type': [
|
||||
{
|
||||
language: null,
|
||||
value: 'Article'
|
||||
}
|
||||
],
|
||||
'dc.contributor.author': [
|
||||
{
|
||||
language: 'en_US',
|
||||
value: 'Smith, Donald'
|
||||
}
|
||||
],
|
||||
'dc.date.issued': [
|
||||
{
|
||||
language: null,
|
||||
value: '2015-06-26'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
const rd = new RemoteData(false, false, true, null, item);
|
||||
mockObject = Object.assign(new Workspaceitem(), { item: observableOf(rd), id: '1234', uuid: '1234' });
|
||||
|
||||
describe('WorkspaceitemActionsComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
NgbModule.forRoot(),
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
})
|
||||
],
|
||||
declarations: [WorkspaceitemActionsComponent],
|
||||
providers: [
|
||||
{ provide: Injector, useValue: {} },
|
||||
{ provide: NotificationsService, useValue: new NotificationsServiceStub() },
|
||||
{ provide: Router, useValue: new RouterStub() },
|
||||
{ provide: WorkspaceitemDataService, useValue: mockDataService },
|
||||
NgbModal
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(WorkspaceitemActionsComponent, {
|
||||
set: { changeDetection: ChangeDetectionStrategy.Default }
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WorkspaceitemActionsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.object = mockObject;
|
||||
notificationsServiceStub = TestBed.get(NotificationsService);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture = null;
|
||||
component = null;
|
||||
});
|
||||
|
||||
it('should init object properly', () => {
|
||||
component.object = null;
|
||||
component.initObjects(mockObject);
|
||||
|
||||
expect(component.object).toEqual(mockObject);
|
||||
});
|
||||
|
||||
it('should display edit button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('.btn-primary'));
|
||||
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it('should display delete button', () => {
|
||||
const btn = fixture.debugElement.query(By.css('.btn-danger'));
|
||||
|
||||
expect(btn).toBeDefined();
|
||||
});
|
||||
|
||||
it('should call confirmDiscard on discard confirmation', fakeAsync(() => {
|
||||
mockDataService.delete.and.returnValue(observableOf(true));
|
||||
spyOn(component, 'reload');
|
||||
const btn = fixture.debugElement.query(By.css('.btn-danger'));
|
||||
btn.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const confirmBtn: any = ((document as any).querySelector('.modal-footer .btn-danger'));
|
||||
confirmBtn.click();
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(mockDataService.delete).toHaveBeenCalledWith(mockObject);
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('should display a success notification on delete success', async(() => {
|
||||
spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')});
|
||||
mockDataService.delete.and.returnValue(observableOf(true));
|
||||
spyOn(component, 'reload');
|
||||
|
||||
component.confirmDiscard('ok');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.success).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display an error notification on delete failure', async(() => {
|
||||
spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')});
|
||||
mockDataService.delete.and.returnValue(observableOf(false));
|
||||
spyOn(component, 'reload');
|
||||
|
||||
component.confirmDiscard('ok');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
});
|
@@ -7,50 +7,71 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model';
|
||||
import { MyDSpaceActionsComponent } from '../mydspace-actions';
|
||||
import { SubmissionRestService } from '../../../core/submission/submission-rest.service';
|
||||
import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service';
|
||||
import { ResourceType } from '../../../core/shared/resource-type';
|
||||
import { NotificationsService } from '../../notifications/notifications.service';
|
||||
import { NotificationOptions } from '../../notifications/models/notification-options.model';
|
||||
|
||||
/**
|
||||
* This component represents mydspace actions related to Workspaceitem object.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ds-workspaceitem-actions',
|
||||
styleUrls: ['./workspaceitem-actions.component.scss'],
|
||||
templateUrl: './workspaceitem-actions.component.html',
|
||||
})
|
||||
|
||||
export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent<Workspaceitem, WorkspaceitemDataService> {
|
||||
|
||||
/**
|
||||
* The workspaceitem object
|
||||
*/
|
||||
@Input() object: Workspaceitem;
|
||||
|
||||
/**
|
||||
* A boolean representing if a delete operation is pending
|
||||
* @type {BehaviorSubject<boolean>}
|
||||
*/
|
||||
public processingDelete$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
*
|
||||
* @param {Injector} injector
|
||||
* @param {Router} router
|
||||
* @param {NgbModal} modalService
|
||||
* @param {NotificationsService} notificationsService
|
||||
* @param {TranslateService} translate
|
||||
*/
|
||||
constructor(protected injector: Injector,
|
||||
protected router: Router,
|
||||
private modalService: NgbModal,
|
||||
private notificationsService: NotificationsService,
|
||||
private restService: SubmissionRestService,
|
||||
private translate: TranslateService) {
|
||||
super(ResourceType.Workspaceitem, injector, router);
|
||||
protected modalService: NgbModal,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService) {
|
||||
super(ResourceType.Workspaceitem, injector, router, notificationsService, translate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the target workspaceitem object
|
||||
*/
|
||||
public confirmDiscard(content) {
|
||||
this.modalService.open(content).result.then(
|
||||
(result) => {
|
||||
if (result === 'ok') {
|
||||
this.processingDelete$.next(true);
|
||||
this.restService.deleteById(this.object.id)
|
||||
.subscribe(() => {
|
||||
this.notificationsService.success(null,
|
||||
this.translate.get('submission.workflow.tasks.generic.success'),
|
||||
new NotificationOptions(5000, false));
|
||||
this.objectDataService.delete(this.object)
|
||||
.subscribe((response: boolean) => {
|
||||
this.processingDelete$.next(false);
|
||||
this.reload();
|
||||
this.handleActionResponse(response);
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the target object
|
||||
*
|
||||
* @param {Workspaceitem} object
|
||||
*/
|
||||
initObjects(object: Workspaceitem) {
|
||||
this.object = object;
|
||||
}
|
||||
|
@@ -140,6 +140,7 @@ import { MetadataValuesComponent } from '../+item-page/field-components/metadata
|
||||
import { MetadataUriValuesComponent } from '../+item-page/field-components/metadata-uri-values/metadata-uri-values.component';
|
||||
import { RoleDirective } from './roles/role.directive';
|
||||
import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component';
|
||||
import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component';
|
||||
|
||||
const MODULES = [
|
||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||
@@ -234,6 +235,7 @@ const COMPONENTS = [
|
||||
ClaimedTaskActionsComponent,
|
||||
ClaimedTaskActionsApproveComponent,
|
||||
ClaimedTaskActionsRejectComponent,
|
||||
ClaimedTaskActionsReturnToPoolComponent,
|
||||
ItemActionsComponent,
|
||||
PoolTaskActionsComponent,
|
||||
WorkflowitemActionsComponent,
|
||||
|
@@ -13,26 +13,30 @@ export const EPersonMock: EPerson = Object.assign(new EPerson(),{
|
||||
id: 'testid',
|
||||
uuid: 'testid',
|
||||
type: 'eperson',
|
||||
metadata: [
|
||||
metadata: {
|
||||
'dc.title': [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: null,
|
||||
value: 'User Test'
|
||||
},
|
||||
}
|
||||
],
|
||||
'eperson.firstname': [
|
||||
{
|
||||
key: 'eperson.firstname',
|
||||
language: null,
|
||||
value: 'User'
|
||||
},
|
||||
}
|
||||
],
|
||||
'eperson.lastname': [
|
||||
{
|
||||
key: 'eperson.lastname',
|
||||
language: null,
|
||||
value: 'Test'
|
||||
},
|
||||
],
|
||||
'eperson.language': [
|
||||
{
|
||||
key: 'eperson.language',
|
||||
language: null,
|
||||
value: 'en'
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
});
|
||||
|
@@ -1,6 +1,7 @@
|
||||
|
||||
export class RouterStub {
|
||||
url: string;
|
||||
routeReuseStrategy = {shouldReuseRoute: {}};
|
||||
//noinspection TypeScriptUnresolvedFunction
|
||||
navigate = jasmine.createSpy('navigate');
|
||||
parseUrl = jasmine.createSpy('parseUrl');
|
||||
|
@@ -10,7 +10,10 @@ export class SearchConfigurationServiceStub {
|
||||
}
|
||||
|
||||
getCurrentScope(a) {
|
||||
return observableOf('test-id')
|
||||
return observableOf('test-id');
|
||||
}
|
||||
|
||||
getCurrentConfiguration(a) {
|
||||
return observableOf(a);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user