mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
83707: Implement tests
This commit is contained in:
@@ -0,0 +1,219 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { ContentSource } from '../../../../core/shared/content-source.model';
|
||||||
|
import { Collection } from '../../../../core/shared/collection.model';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { NotificationsService } from '../../../../shared/notifications/notifications.service';
|
||||||
|
import { CollectionDataService } from '../../../../core/data/collection-data.service';
|
||||||
|
import { RequestService } from '../../../../core/data/request.service';
|
||||||
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
|
import { ProcessDataService } from '../../../../core/data/processes/process-data.service';
|
||||||
|
import { ScriptDataService } from '../../../../core/data/processes/script-data.service';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
import { NotificationsServiceStub } from '../../../../shared/testing/notifications-service.stub';
|
||||||
|
import { Process } from '../../../../process-page/processes/process.model';
|
||||||
|
import { of as observableOf } from 'rxjs';
|
||||||
|
import { CollectionSourceControlsComponent } from './collection-source-controls.component';
|
||||||
|
import { Bitstream } from '../../../../core/shared/bitstream.model';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { VarDirective } from '../../../../shared/utils/var.directive';
|
||||||
|
|
||||||
|
describe('CollectionSourceControlsComponent', () => {
|
||||||
|
let comp: CollectionSourceControlsComponent;
|
||||||
|
let fixture: ComponentFixture<CollectionSourceControlsComponent>;
|
||||||
|
|
||||||
|
const uuid = '29481ed7-ae6b-409a-8c51-34dd347a0ce4';
|
||||||
|
let contentSource: ContentSource;
|
||||||
|
let collection: Collection;
|
||||||
|
let process: Process;
|
||||||
|
let bitstream: Bitstream;
|
||||||
|
|
||||||
|
let scriptDataService: ScriptDataService;
|
||||||
|
let processDataService: ProcessDataService;
|
||||||
|
let requestService: RequestService;
|
||||||
|
let notificationsService;
|
||||||
|
let collectionService: CollectionDataService;
|
||||||
|
let httpClient: HttpClient;
|
||||||
|
let bitstreamService: BitstreamDataService;
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
contentSource = Object.assign(new ContentSource(), {
|
||||||
|
uuid: uuid,
|
||||||
|
metadataConfigs: [
|
||||||
|
{
|
||||||
|
id: 'dc',
|
||||||
|
label: 'Simple Dublin Core',
|
||||||
|
nameSpace: 'http://www.openarchives.org/OAI/2.0/oai_dc/'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'qdc',
|
||||||
|
label: 'Qualified Dublin Core',
|
||||||
|
nameSpace: 'http://purl.org/dc/terms/'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dim',
|
||||||
|
label: 'DSpace Intermediate Metadata',
|
||||||
|
nameSpace: 'http://www.dspace.org/xmlns/dspace/dim'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
oaiSource: 'oai-harvest-source',
|
||||||
|
oaiSetId: 'oai-set-id',
|
||||||
|
_links: {self: {href: 'contentsource-selflink'}}
|
||||||
|
});
|
||||||
|
process = Object.assign(new Process(), {
|
||||||
|
processId: 'process-id', processStatus: 'COMPLETED',
|
||||||
|
_links: {output: {href: 'output-href'}}
|
||||||
|
});
|
||||||
|
|
||||||
|
bitstream = Object.assign(new Bitstream(), {_links: {content: {href: 'content-href'}}});
|
||||||
|
|
||||||
|
collection = Object.assign(new Collection(), {
|
||||||
|
uuid: 'fake-collection-id',
|
||||||
|
_links: {self: {href: 'collection-selflink'}}
|
||||||
|
});
|
||||||
|
notificationsService = new NotificationsServiceStub();
|
||||||
|
collectionService = jasmine.createSpyObj('collectionService', {
|
||||||
|
getContentSource: createSuccessfulRemoteDataObject$(contentSource),
|
||||||
|
findByHref: createSuccessfulRemoteDataObject$(collection)
|
||||||
|
});
|
||||||
|
scriptDataService = jasmine.createSpyObj('scriptDataService', {
|
||||||
|
invoke: createSuccessfulRemoteDataObject$(process),
|
||||||
|
});
|
||||||
|
processDataService = jasmine.createSpyObj('processDataService', {
|
||||||
|
findById: createSuccessfulRemoteDataObject$(process),
|
||||||
|
});
|
||||||
|
bitstreamService = jasmine.createSpyObj('bitstreamService', {
|
||||||
|
findByHref: createSuccessfulRemoteDataObject$(bitstream),
|
||||||
|
});
|
||||||
|
httpClient = jasmine.createSpyObj('httpClient', {
|
||||||
|
get: observableOf('Script text'),
|
||||||
|
});
|
||||||
|
requestService = jasmine.createSpyObj('requestService', ['removeByHrefSubstring', 'setStaleByHrefSubstring']);
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
||||||
|
declarations: [CollectionSourceControlsComponent, VarDirective],
|
||||||
|
providers: [
|
||||||
|
{provide: ScriptDataService, useValue: scriptDataService},
|
||||||
|
{provide: ProcessDataService, useValue: processDataService},
|
||||||
|
{provide: RequestService, useValue: requestService},
|
||||||
|
{provide: NotificationsService, useValue: notificationsService},
|
||||||
|
{provide: CollectionDataService, useValue: collectionService},
|
||||||
|
{provide: HttpClient, useValue: httpClient},
|
||||||
|
{provide: BitstreamDataService, useValue: bitstreamService}
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CollectionSourceControlsComponent);
|
||||||
|
comp = fixture.componentInstance;
|
||||||
|
comp.isEnabled = true;
|
||||||
|
comp.collection = collection;
|
||||||
|
comp.shouldShow = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
describe('init', () => {
|
||||||
|
it('should', () => {
|
||||||
|
expect(comp).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('testConfiguration', () => {
|
||||||
|
it('should invoke a script and ping the resulting process until completed and show the resulting info', () => {
|
||||||
|
comp.testConfiguration(contentSource);
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(scriptDataService.invoke).toHaveBeenCalledWith('harvest', [
|
||||||
|
{name: '-g', value: null},
|
||||||
|
{name: '-a', value: contentSource.oaiSource},
|
||||||
|
{name: '-i', value: contentSource.oaiSetId},
|
||||||
|
], []);
|
||||||
|
|
||||||
|
expect(processDataService.findById).toHaveBeenCalledWith(process.processId, false);
|
||||||
|
expect(bitstreamService.findByHref).toHaveBeenCalledWith(process._links.output.href);
|
||||||
|
expect(notificationsService.info).toHaveBeenCalledWith(jasmine.anything() as any, 'Script text');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('importNow', () => {
|
||||||
|
it('should invoke a script that will start the harvest', () => {
|
||||||
|
comp.importNow();
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(scriptDataService.invoke).toHaveBeenCalledWith('harvest', [
|
||||||
|
{name: '-r', value: null},
|
||||||
|
{name: '-e', value: 'dspacedemo+admin@gmail.com'},
|
||||||
|
{name: '-c', value: collection.uuid},
|
||||||
|
], []);
|
||||||
|
expect(processDataService.findById).toHaveBeenCalledWith(process.processId, false);
|
||||||
|
expect(notificationsService.success).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('the controls', () => {
|
||||||
|
it('should be shown when shouldShow is true', () => {
|
||||||
|
comp.shouldShow = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||||
|
expect(buttons.length).toEqual(3);
|
||||||
|
});
|
||||||
|
it('should be shown when shouldShow is false', () => {
|
||||||
|
comp.shouldShow = false;
|
||||||
|
fixture.detectChanges();
|
||||||
|
const buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||||
|
expect(buttons.length).toEqual(0);
|
||||||
|
});
|
||||||
|
it('should be disabled when isEnabled is false', () => {
|
||||||
|
comp.shouldShow = true;
|
||||||
|
comp.isEnabled = false;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||||
|
|
||||||
|
expect(buttons[0].nativeElement.disabled).toBeTrue();
|
||||||
|
expect(buttons[1].nativeElement.disabled).toBeTrue();
|
||||||
|
expect(buttons[2].nativeElement.disabled).toBeTrue();
|
||||||
|
});
|
||||||
|
it('should be enabled when isEnabled is true', () => {
|
||||||
|
comp.shouldShow = true;
|
||||||
|
comp.isEnabled = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||||
|
|
||||||
|
expect(buttons[0].nativeElement.disabled).toBeFalse();
|
||||||
|
expect(buttons[1].nativeElement.disabled).toBeFalse();
|
||||||
|
expect(buttons[2].nativeElement.disabled).toBeFalse();
|
||||||
|
});
|
||||||
|
it('should call the corresponding button when clicked', () => {
|
||||||
|
spyOn(comp, 'testConfiguration');
|
||||||
|
spyOn(comp, 'importNow');
|
||||||
|
spyOn(comp, 'resetAndReimport');
|
||||||
|
|
||||||
|
comp.shouldShow = true;
|
||||||
|
comp.isEnabled = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const buttons = fixture.debugElement.queryAll(By.css('button'));
|
||||||
|
|
||||||
|
buttons[0].triggerEventHandler('click', null);
|
||||||
|
expect(comp.testConfiguration).toHaveBeenCalled();
|
||||||
|
|
||||||
|
buttons[1].triggerEventHandler('click', null);
|
||||||
|
expect(comp.importNow).toHaveBeenCalled();
|
||||||
|
|
||||||
|
buttons[2].triggerEventHandler('click', null);
|
||||||
|
expect(comp.resetAndReimport).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@@ -22,14 +22,28 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
import { BitstreamDataService } from '../../../../core/data/bitstream-data.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that contains the controls to run, reset and test the harvest
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-collection-source-controls',
|
selector: 'ds-collection-source-controls',
|
||||||
templateUrl: './collection-source-controls.component.html',
|
templateUrl: './collection-source-controls.component.html',
|
||||||
})
|
})
|
||||||
export class CollectionSourceControlsComponent implements OnDestroy {
|
export class CollectionSourceControlsComponent implements OnDestroy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the controls be enabled.
|
||||||
|
*/
|
||||||
@Input() isEnabled: boolean;
|
@Input() isEnabled: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current collection
|
||||||
|
*/
|
||||||
@Input() collection: Collection;
|
@Input() collection: Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the control section be shown
|
||||||
|
*/
|
||||||
@Input() shouldShow: boolean;
|
@Input() shouldShow: boolean;
|
||||||
|
|
||||||
contentSource$: Observable<ContentSource>;
|
contentSource$: Observable<ContentSource>;
|
||||||
@@ -47,6 +61,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
// ensure the contentSource gets updated after being set to stale
|
||||||
this.contentSource$ = this.collectionService.findByHref(this.collection._links.self.href, false).pipe(
|
this.contentSource$ = this.collectionService.findByHref(this.collection._links.self.href, false).pipe(
|
||||||
getAllSucceededRemoteDataPayload(),
|
getAllSucceededRemoteDataPayload(),
|
||||||
switchMap((collection) => this.collectionService.getContentSource(collection.uuid, false)),
|
switchMap((collection) => this.collectionService.getContentSource(collection.uuid, false)),
|
||||||
@@ -54,6 +69,10 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the provided content source's configuration.
|
||||||
|
* @param contentSource - The content source to be tested
|
||||||
|
*/
|
||||||
testConfiguration(contentSource) {
|
testConfiguration(contentSource) {
|
||||||
this.subs.push(this.scriptDataService.invoke('harvest', [
|
this.subs.push(this.scriptDataService.invoke('harvest', [
|
||||||
{name: '-g', value: null},
|
{name: '-g', value: null},
|
||||||
@@ -63,9 +82,11 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
tap((rd) => {
|
tap((rd) => {
|
||||||
if (rd.hasFailed) {
|
if (rd.hasFailed) {
|
||||||
|
// show a notification when the script invocation fails
|
||||||
this.notificationsService.error(this.translateService.get('collection.source.controls.test.submit.error'));
|
this.notificationsService.error(this.translateService.get('collection.source.controls.test.submit.error'));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
// filter out responses that aren't successful since the pinging of the process only needs to happen when the invocation was successful.
|
||||||
filter((rd) => rd.hasSucceeded && hasValue(rd.payload)),
|
filter((rd) => rd.hasSucceeded && hasValue(rd.payload)),
|
||||||
switchMap((rd) => this.processDataService.findById(rd.payload.processId, false)),
|
switchMap((rd) => this.processDataService.findById(rd.payload.processId, false)),
|
||||||
getAllCompletedRemoteData(),
|
getAllCompletedRemoteData(),
|
||||||
@@ -75,6 +96,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
).subscribe((process: Process) => {
|
).subscribe((process: Process) => {
|
||||||
if (process.processStatus.toString() !== ProcessStatus[ProcessStatus.COMPLETED].toString() &&
|
if (process.processStatus.toString() !== ProcessStatus[ProcessStatus.COMPLETED].toString() &&
|
||||||
process.processStatus.toString() !== ProcessStatus[ProcessStatus.FAILED].toString()) {
|
process.processStatus.toString() !== ProcessStatus[ProcessStatus.FAILED].toString()) {
|
||||||
|
// Ping the current process state every 5s
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.requestService.setStaleByHrefSubstring(process._links.self.href);
|
this.requestService.setStaleByHrefSubstring(process._links.self.href);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
@@ -83,12 +105,12 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
this.notificationsService.error(this.translateService.get('collection.source.controls.test.failed'));
|
this.notificationsService.error(this.translateService.get('collection.source.controls.test.failed'));
|
||||||
}
|
}
|
||||||
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
|
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
|
||||||
this.bitstreamService.findByHref(process._links.output.href, false).pipe(getFirstSucceededRemoteDataPayload()).subscribe((bitstream) => {
|
this.bitstreamService.findByHref(process._links.output.href).pipe(getFirstSucceededRemoteDataPayload()).subscribe((bitstream) => {
|
||||||
this.httpClient.get(bitstream._links.content.href, {responseType: 'text'}).subscribe((data: any) => {
|
this.httpClient.get(bitstream._links.content.href, {responseType: 'text'}).subscribe((data: any) => {
|
||||||
const output = data.replaceAll(new RegExp('.*\\@(.*)', 'g'), '$1')
|
const output = data.replaceAll(new RegExp('.*\\@(.*)', 'g'), '$1')
|
||||||
.replaceAll('The script has started', '')
|
.replaceAll('The script has started', '')
|
||||||
.replaceAll('The script has completed', '');
|
.replaceAll('The script has completed', '');
|
||||||
this.notificationsService.success(this.translateService.get('collection.source.controls.test.completed'), output);
|
this.notificationsService.info(this.translateService.get('collection.source.controls.test.completed'), output);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -96,6 +118,9 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the harvest for the current collection
|
||||||
|
*/
|
||||||
importNow() {
|
importNow() {
|
||||||
this.subs.push(this.scriptDataService.invoke('harvest', [
|
this.subs.push(this.scriptDataService.invoke('harvest', [
|
||||||
{name: '-r', value: null},
|
{name: '-r', value: null},
|
||||||
@@ -118,6 +143,7 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
).subscribe((process) => {
|
).subscribe((process) => {
|
||||||
if (process.processStatus.toString() !== ProcessStatus[ProcessStatus.COMPLETED].toString() &&
|
if (process.processStatus.toString() !== ProcessStatus[ProcessStatus.COMPLETED].toString() &&
|
||||||
process.processStatus.toString() !== ProcessStatus[ProcessStatus.FAILED].toString()) {
|
process.processStatus.toString() !== ProcessStatus[ProcessStatus.FAILED].toString()) {
|
||||||
|
// Ping the current process state every 5s
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.requestService.setStaleByHrefSubstring(process._links.self.href);
|
this.requestService.setStaleByHrefSubstring(process._links.self.href);
|
||||||
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
|
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
|
||||||
@@ -128,14 +154,15 @@ export class CollectionSourceControlsComponent implements OnDestroy {
|
|||||||
}
|
}
|
||||||
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
|
if (process.processStatus.toString() === ProcessStatus[ProcessStatus.COMPLETED].toString()) {
|
||||||
this.notificationsService.success(this.translateService.get('collection.source.controls.import.completed'));
|
this.notificationsService.success(this.translateService.get('collection.source.controls.import.completed'));
|
||||||
setTimeout(() => {
|
|
||||||
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
|
this.requestService.setStaleByHrefSubstring(this.collection._links.self.href);
|
||||||
}, 5000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset and reimport the current collection
|
||||||
|
*/
|
||||||
resetAndReimport() {
|
resetAndReimport() {
|
||||||
// TODO implement when a single option is present
|
// TODO implement when a single option is present
|
||||||
}
|
}
|
||||||
|
@@ -62,7 +62,8 @@ describe('CollectionSourceComponent', () => {
|
|||||||
label: 'DSpace Intermediate Metadata',
|
label: 'DSpace Intermediate Metadata',
|
||||||
nameSpace: 'http://www.dspace.org/xmlns/dspace/dim'
|
nameSpace: 'http://www.dspace.org/xmlns/dspace/dim'
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
_links: { self: { href: 'contentsource-selflink' } }
|
||||||
});
|
});
|
||||||
fieldUpdate = {
|
fieldUpdate = {
|
||||||
field: contentSource,
|
field: contentSource,
|
||||||
@@ -115,7 +116,7 @@ describe('CollectionSourceComponent', () => {
|
|||||||
updateContentSource: observableOf(contentSource),
|
updateContentSource: observableOf(contentSource),
|
||||||
getHarvesterEndpoint: observableOf('harvester-endpoint')
|
getHarvesterEndpoint: observableOf('harvester-endpoint')
|
||||||
});
|
});
|
||||||
requestService = jasmine.createSpyObj('requestService', ['removeByHrefSubstring']);
|
requestService = jasmine.createSpyObj('requestService', ['removeByHrefSubstring', 'setStaleByHrefSubstring']);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
imports: [TranslateModule.forRoot(), RouterTestingModule],
|
||||||
|
Reference in New Issue
Block a user