diff --git a/src/app/shared/confirmation-modal/confirmation-modal.component.spec.ts b/src/app/shared/confirmation-modal/confirmation-modal.component.spec.ts index 63930fc4ed..4d1de112cd 100644 --- a/src/app/shared/confirmation-modal/confirmation-modal.component.spec.ts +++ b/src/app/shared/confirmation-modal/confirmation-modal.component.spec.ts @@ -46,27 +46,27 @@ describe('ConfirmationModalComponent', () => { describe('confirmPressed', () => { beforeEach(() => { - spyOn(component.response, 'emit'); + spyOn(component.response, 'next'); component.confirmPressed(); }); it('should call the close method on the active modal', () => { expect(modalStub.close).toHaveBeenCalled(); }); - it('event emitter should emit true', () => { - expect(component.response.emit).toHaveBeenCalledWith(true); + it('behaviour subject should have true as next', () => { + expect(component.response.next).toHaveBeenCalledWith(true); }); }); describe('cancelPressed', () => { beforeEach(() => { - spyOn(component.response, 'emit'); + spyOn(component.response, 'next'); component.cancelPressed(); }); it('should call the close method on the active modal', () => { expect(modalStub.close).toHaveBeenCalled(); }); - it('event emitter should emit false', () => { - expect(component.response.emit).toHaveBeenCalledWith(false); + it('behaviour subject should have false as next', () => { + expect(component.response.next).toHaveBeenCalledWith(false); }); }); @@ -85,7 +85,7 @@ describe('ConfirmationModalComponent', () => { describe('when the click method emits on cancel button', () => { beforeEach(fakeAsync(() => { spyOn(component, 'close'); - spyOn(component.response, 'emit'); + spyOn(component.response, 'next'); debugElement.query(By.css('button.cancel')).triggerEventHandler('click', {}); tick(); fixture.detectChanges(); @@ -93,15 +93,15 @@ describe('ConfirmationModalComponent', () => { it('should call the close method on the component', () => { expect(component.close).toHaveBeenCalled(); }); - it('event emitter should emit false', () => { - expect(component.response.emit).toHaveBeenCalledWith(false); + it('behaviour subject should have false as next', () => { + expect(component.response.next).toHaveBeenCalledWith(false); }); }); describe('when the click method emits on confirm button', () => { beforeEach(fakeAsync(() => { spyOn(component, 'close'); - spyOn(component.response, 'emit'); + spyOn(component.response, 'next'); debugElement.query(By.css('button.confirm')).triggerEventHandler('click', {}); tick(); fixture.detectChanges(); @@ -109,8 +109,8 @@ describe('ConfirmationModalComponent', () => { it('should call the close method on the component', () => { expect(component.close).toHaveBeenCalled(); }); - it('event emitter should emit true', () => { - expect(component.response.emit).toHaveBeenCalledWith(true); + it('behaviour subject should have true as next', () => { + expect(component.response.next).toHaveBeenCalledWith(true); }); }); diff --git a/src/app/shared/confirmation-modal/confirmation-modal.component.ts b/src/app/shared/confirmation-modal/confirmation-modal.component.ts index e52d26021f..3cf5ffac1f 100644 --- a/src/app/shared/confirmation-modal/confirmation-modal.component.ts +++ b/src/app/shared/confirmation-modal/confirmation-modal.component.ts @@ -1,6 +1,6 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Component, Input, Output } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Subject } from 'rxjs/internal/Subject'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; @Component({ @@ -18,7 +18,7 @@ export class ConfirmationModalComponent { * An event fired when the cancel or confirm button is clicked, with respectively false or true */ @Output() - response = new EventEmitter(); + response: Subject = new Subject(); constructor(protected activeModal: NgbActiveModal) { } @@ -27,7 +27,7 @@ export class ConfirmationModalComponent { * Confirm the action that led to the modal */ confirmPressed() { - this.response.emit(true); + this.response.next(true); this.close(); } @@ -35,7 +35,7 @@ export class ConfirmationModalComponent { * Cancel the action that led to the modal and close modal */ cancelPressed() { - this.response.emit(false); + this.response.next(false); this.close(); } diff --git a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts index e534cbf226..c619217688 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.spec.ts @@ -1,24 +1,45 @@ import { of as observableOf } from 'rxjs'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { TranslateModule } from '@ngx-translate/core'; -import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { DebugElement, NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NgbActiveModal, NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; import { ActivatedRoute, Router } from '@angular/router'; import { METADATA_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service'; import { Collection } from '../../../../core/shared/collection.model'; import { Community } from '../../../../core/shared/community.model'; import { Item } from '../../../../core/shared/item.model'; import { ProcessParameter } from '../../../../process-page/processes/process-parameter.model'; +import { ConfirmationModalComponent } from '../../../confirmation-modal/confirmation-modal.component'; +import { TranslateLoaderMock } from '../../../mocks/translate-loader.mock'; import { NotificationsService } from '../../../notifications/notifications.service'; import { NotificationsServiceStub } from '../../../testing/notifications-service.stub'; import { createSuccessfulRemoteDataObject } from '../../../remote-data.utils'; import { ExportMetadataSelectorComponent } from './export-metadata-selector.component'; + +// No way to add entryComponents yet to testbed; alternative implemented; source: https://stackoverflow.com/questions/41689468/how-to-shallow-test-a-component-with-an-entrycomponents +@NgModule({ + imports: [ NgbModalModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + }), + ], + exports: [], + declarations: [ConfirmationModalComponent], + providers: [], + entryComponents: [ConfirmationModalComponent], +}) +class ModelTestModule { } + describe('ExportMetadataSelectorComponent', () => { let component: ExportMetadataSelectorComponent; let fixture: ComponentFixture; let debugElement: DebugElement; + let modalRef; let router; let notificationService: NotificationsServiceStub; @@ -61,7 +82,7 @@ describe('ExportMetadataSelectorComponent', () => { } ); TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([])], + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), ModelTestModule], declarations: [ExportMetadataSelectorComponent], providers: [ { provide: NgbActiveModal, useValue: modalStub }, @@ -92,6 +113,9 @@ describe('ExportMetadataSelectorComponent', () => { fixture = TestBed.createComponent(ExportMetadataSelectorComponent); component = fixture.componentInstance; debugElement = fixture.debugElement; + const modalService = TestBed.get(NgbModal); + modalRef = modalService.open(ConfirmationModalComponent); + modalRef.componentInstance.response = observableOf(true); fixture.detectChanges(); }); @@ -116,6 +140,7 @@ describe('ExportMetadataSelectorComponent', () => { describe('if collection is selected', () => { let scriptRequestSucceeded; beforeEach((done) => { + spyOn((component as any).modalService, 'open').and.returnValue(modalRef); component.navigate(mockCollection).subscribe((succeeded: boolean) => { scriptRequestSucceeded = succeeded; done() @@ -140,6 +165,7 @@ describe('ExportMetadataSelectorComponent', () => { describe('if community is selected', () => { let scriptRequestSucceeded; beforeEach((done) => { + spyOn((component as any).modalService, 'open').and.returnValue(modalRef); component.navigate(mockCommunity).subscribe((succeeded: boolean) => { scriptRequestSucceeded = succeeded; done() @@ -164,6 +190,7 @@ describe('ExportMetadataSelectorComponent', () => { describe('if community/collection is selected; but script invoke fails', () => { let scriptRequestSucceeded; beforeEach((done) => { + spyOn((component as any).modalService, 'open').and.returnValue(modalRef); jasmine.getEnv().allowRespy(true); spyOn(scriptService, 'invoke').and.returnValue(observableOf({ response: diff --git a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts index 647e77584c..fdc4e8344c 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-metadata-selector/export-metadata-selector.component.ts @@ -2,9 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs/internal/Observable'; -import { take, map } from 'rxjs/operators'; +import { take, map, switchMap } from 'rxjs/operators'; import { of as observableOf } from 'rxjs'; -import { AuthService } from '../../../../core/auth/auth.service'; import { METADATA_EXPORT_SCRIPT_NAME, ScriptDataService } from '../../../../core/data/processes/script-data.service'; import { RequestEntry } from '../../../../core/data/request.reducer'; import { Collection } from '../../../../core/shared/collection.model'; @@ -47,21 +46,25 @@ export class ExportMetadataSelectorComponent extends DSOSelectorModalWrapperComp if (dso instanceof Collection || dso instanceof Community) { const modalRef = this.modalService.open(ConfirmationModalComponent); modalRef.componentInstance.dso = dso; - modalRef.componentInstance.headerLabel = "confirmation-modal.export-metadata.header"; - modalRef.componentInstance.infoLabel = "confirmation-modal.export-metadata.info"; - modalRef.componentInstance.cancelLabel = "confirmation-modal.export-metadata.cancel"; - modalRef.componentInstance.confirmLabel = "confirmation-modal.export-metadata.confirm"; - - modalRef.componentInstance.response.subscribe((confirm: boolean) => { + modalRef.componentInstance.headerLabel = 'confirmation-modal.export-metadata.header'; + modalRef.componentInstance.infoLabel = 'confirmation-modal.export-metadata.info'; + modalRef.componentInstance.cancelLabel = 'confirmation-modal.export-metadata.cancel'; + modalRef.componentInstance.confirmLabel = 'confirmation-modal.export-metadata.confirm'; + const resp$ = modalRef.componentInstance.response.pipe(switchMap((confirm: boolean) => { if (confirm) { - const startScriptSucceeded = this.startScriptNotifyAndRedirect(dso, dso.handle); - startScriptSucceeded.pipe(take(1)).subscribe(); - return startScriptSucceeded; + const startScriptSucceeded$ = this.startScriptNotifyAndRedirect(dso, dso.handle); + return startScriptSucceeded$.pipe( + switchMap((r: boolean) => { + return observableOf(r); + }) + ) } else { const modalRef = this.modalService.open(ExportMetadataSelectorComponent); modalRef.componentInstance.dsoRD = createSuccessfulRemoteDataObject(dso); } - }); + })); + resp$.subscribe(); + return resp$; } else { this.notificationsService.error(this.translationService.get('dso-selector.export-metadata.notValidDSO')); return observableOf(false); diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 398167cb39..cc0379ac37 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -3,7 +3,7 @@ import { Observable } from 'rxjs/internal/Observable'; import { MenuService } from './menu.service'; import { MenuID } from './initial-menus-state'; import { MenuSection } from './menu.reducer'; -import { distinctUntilChanged, first, map, tap, switchMap, take } from 'rxjs/operators'; +import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { hasValue } from '../empty.util'; import { MenuSectionComponent } from './menu-section/menu-section.component'; diff --git a/src/environments/mock-environment.ts b/src/environments/mock-environment.ts index 57a747f374..6e4d60e268 100644 --- a/src/environments/mock-environment.ts +++ b/src/environments/mock-environment.ts @@ -105,7 +105,7 @@ export const environment: Partial = { }, // Angular Universal settings universal: { - preboot: false, + preboot: true, async: true, time: false },