TSK-1349: Added test for import-export

This commit is contained in:
Tristan Eisermann 2020-09-07 11:47:38 +02:00 committed by Tristan2357
parent fc6d6b908a
commit 8cbbab4050
8 changed files with 162 additions and 49 deletions

View File

@ -10,7 +10,7 @@
</button>
<taskana-administration-import-export
class ="btn-group" [currentSelection]="taskanaType.CLASSIFICATIONS">
class="btn-group" [currentSelection]="taskanaType.CLASSIFICATIONS">
</taskana-administration-import-export>
</div>
<div class="col-xs-6">

View File

@ -1,5 +1,5 @@
<button type="button" [ngClass]="{disabled: uploadservice?.isInUse}" (click)="selectedFile.click()" data-toggle="tooltip" title="Import" class="btn btn-default">
<button type="button" [ngClass]="{disabled: uploadService?.isInUse}" (click)="selectedFile.click()" data-toggle="tooltip" title="Import" class="btn btn-default">
<span class="material-icons md-20 green-blue">cloud_upload</span>
</button>
<form class="hidden" id="upload_form" enctype="multipart/form-data" method="post">
@ -18,6 +18,6 @@
</a>
</li>
</ul>
<button [ngClass]="{disabled: uploadservice?.isInUse}" type="button" data-toggle="dropdown" title="Export" class="btn btn-default">
<button [ngClass]="{disabled: uploadService?.isInUse}" type="button" data-toggle="dropdown" title="Export" class="btn btn-default">
<span class="material-icons md-20 red">cloud_download</span>
</button>

View File

@ -0,0 +1,104 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core';
import { ImportExportComponent } from './import-export.component';
import { StartupService } from '../../../shared/services/startup/startup.service';
import { TaskanaEngineService } from '../../../shared/services/taskana-engine/taskana-engine.service';
import { WindowRefService } from '../../../shared/services/window/window.service';
import { DomainService } from '../../../shared/services/domain/domain.service';
import { WorkbasketDefinitionService } from '../../services/workbasket-definition.service';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { UploadService } from '../../../shared/services/upload/upload.service';
import { ImportExportService } from '../../services/import-export.service';
import { HttpClient, HttpHandler } from '@angular/common/http';
import { Router } from '@angular/router';
import { of } from 'rxjs';
import { ClassificationDefinitionService } from '../../services/classification-definition.service';
import { take, timeout } from 'rxjs/operators';
import { TaskanaType } from '../../../shared/models/taskana-type';
import { BlobGenerator } from '../../../shared/util/blob-generator';
jest.mock('../../../shared/util/blob-generator');
describe('ImportExportComponent', () => {
let fixture: ComponentFixture<ImportExportComponent>;
let debugElement: DebugElement;
let app: ImportExportComponent;
const domainServiceSpy = jest.fn().mockImplementation(
(): Partial<DomainService> => ({
getSelectedDomainValue: jest.fn().mockReturnValue(of()),
getSelectedDomain: jest.fn().mockReturnValue(of()),
getDomains: jest.fn().mockReturnValue(of())
})
);
const httpSpy = jest.fn().mockImplementation(
(): Partial<HttpClient> => ({
get: jest.fn().mockReturnValue(of([])),
post: jest.fn().mockReturnValue(of([]))
})
);
const showDialogFn = jest.fn().mockReturnValue(true);
const notificationServiceSpy = jest.fn().mockImplementation(
(): Partial<NotificationService> => ({
showDialog: showDialogFn,
showToast: showDialogFn,
triggerError: showDialogFn
})
);
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [],
declarations: [ImportExportComponent],
providers: [
StartupService,
TaskanaEngineService,
WindowRefService,
WorkbasketDefinitionService,
ClassificationDefinitionService,
UploadService,
ImportExportService,
{ provide: DomainService, useClass: domainServiceSpy },
{ provide: NotificationService, useClass: notificationServiceSpy },
{ provide: HttpClient, useClass: httpSpy }
]
}).compileComponents();
jest.clearAllMocks();
fixture = TestBed.createComponent(ImportExportComponent);
debugElement = fixture.debugElement;
app = fixture.debugElement.componentInstance;
app.currentSelection = TaskanaType.WORKBASKETS;
fixture.detectChanges();
}));
it('should successfully upload a valid file', () => {
app.selectedFileInput = {
nativeElement: {
files: [
{
lastModified: 1599117374674,
name: 'Workbaskets_2020-09-03T09_16_14.1414Z.json',
size: 59368,
type: 'application/json',
webkitRelativePath: ''
}
]
}
};
app.uploadFile();
expect(app.uploadService.isInUse).toBeTruthy();
});
it('should successfully export the classifications', async (done) => {
app
.export()
.pipe(take(1))
.subscribe(() => {
expect(BlobGenerator.saveFile).toHaveBeenCalled();
done();
});
});
});

View File

@ -1,14 +1,17 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ClassificationDefinitionService } from 'app/administration/services/classification-definition.service';
import { WorkbasketDefinitionService } from 'app/administration/services/workbasket-definition.service';
import { DomainService } from 'app/shared/services/domain/domain.service';
import { TaskanaType } from 'app/shared/models/taskana-type';
import { environment } from 'environments/environment';
import { UploadService } from 'app/shared/services/upload/upload.service';
import { ImportExportService } from 'app/administration/services/import-export.service';
import { HttpErrorResponse } from '@angular/common/http';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
import { Observable } from 'rxjs';
import { TaskanaType } from '../../../shared/models/taskana-type';
import { DomainService } from '../../../shared/services/domain/domain.service';
import { WorkbasketDefinitionService } from '../../services/workbasket-definition.service';
import { ClassificationDefinitionService } from '../../services/classification-definition.service';
import { NotificationService } from '../../../shared/services/notifications/notification.service';
import { UploadService } from '../../../shared/services/upload/upload.service';
import { ImportExportService } from '../../services/import-export.service';
import { WorkbasketDefinition } from '../../../shared/models/workbasket-definition';
import { Classification } from '../../../shared/models/classification';
import { environment } from '../../../../environments/environment';
import { NOTIFICATION_TYPES } from '../../../shared/models/notifications';
@Component({
selector: 'taskana-administration-import-export',
@ -29,7 +32,7 @@ export class ImportExportComponent implements OnInit {
private workbasketDefinitionService: WorkbasketDefinitionService,
private classificationDefinitionService: ClassificationDefinitionService,
private notificationsService: NotificationService,
public uploadservice: UploadService,
public uploadService: UploadService,
private errorsService: NotificationService,
private importExportService: ImportExportService
) {}
@ -40,68 +43,65 @@ export class ImportExportComponent implements OnInit {
});
}
export(domain = '') {
export(domain = ''): Observable<WorkbasketDefinition[] | Classification[]> {
if (this.currentSelection === TaskanaType.WORKBASKETS) {
this.workbasketDefinitionService.exportWorkbaskets(domain);
return this.workbasketDefinitionService.exportWorkbaskets(domain);
} else {
this.classificationDefinitionService.exportClassifications(domain);
return this.classificationDefinitionService.exportClassifications(domain);
}
}
uploadFile() {
const file = this.selectedFileInput.nativeElement.files[0];
const formdata = new FormData();
const ajax = new XMLHttpRequest();
console.log(this.selectedFileInput);
const formData = new FormData();
const xhr = new XMLHttpRequest();
if (this.checkFormatFile(file)) {
formdata.append('file', file);
ajax.upload.addEventListener('progress', this.progressHandler.bind(this), false);
ajax.addEventListener('load', this.resetProgress.bind(this), false);
ajax.addEventListener('error', this.onFailedResponse.bind(this, ajax), false);
ajax.onreadystatechange = this.onReadyStateChangeHandler.bind(this, ajax);
formData.append('file', file);
xhr.upload.addEventListener('progress', this.progressHandler.bind(this), false);
xhr.addEventListener('load', this.resetProgress.bind(this), false);
xhr.addEventListener('error', this.onFailedResponse.bind(this, xhr), false);
xhr.onreadystatechange = this.onReadyStateChangeHandler.bind(this, xhr);
if (this.currentSelection === TaskanaType.WORKBASKETS) {
ajax.open('POST', `${environment.taskanaRestUrl}/v1/workbasket-definitions`);
xhr.open('POST', `${environment.taskanaRestUrl}/v1/workbasket-definitions`);
} else {
ajax.open('POST', `${environment.taskanaRestUrl}/v1/classification-definitions`);
xhr.open('POST', `${environment.taskanaRestUrl}/v1/classification-definitions`);
}
if (!environment.production) {
ajax.setRequestHeader('Authorization', 'Basic YWRtaW46YWRtaW4=');
xhr.setRequestHeader('Authorization', 'Basic YWRtaW46YWRtaW4=');
}
ajax.send(formdata);
this.uploadservice.isInUse = true;
this.uploadservice.setCurrentProgressValue(1);
xhr.send(formData);
this.uploadService.isInUse = true;
this.uploadService.setCurrentProgressValue(1);
}
}
progressHandler(event) {
const percent = (event.loaded / event.total) * 100;
this.uploadservice.setCurrentProgressValue(Math.round(percent));
this.uploadService.setCurrentProgressValue(Math.round(percent));
}
private checkFormatFile(file): boolean {
const ending = file.name.match(/\.([^.]+)$/)[1];
let check = false;
if (ending === 'json') {
check = true;
if (file.name.endsWith('json')) {
return true;
} else {
file.value = '';
this.errorsService.triggerError(NOTIFICATION_TYPES.FILE_ERR);
return false;
}
return check;
}
private resetProgress() {
this.uploadservice.setCurrentProgressValue(0);
this.uploadservice.isInUse = false;
this.uploadService.setCurrentProgressValue(0);
this.uploadService.isInUse = false;
this.selectedFileInput.nativeElement.value = '';
}
private onReadyStateChangeHandler(event) {
if (event.readyState === 4 && event.status >= 400) {
let title;
let key: NOTIFICATION_TYPES;
if (event.status === 401) {
key = NOTIFICATION_TYPES.IMPORT_ERR_1;
title = 'Import was not successful, you have no access to apply this operation.';
} else if (event.status === 404) {
key = NOTIFICATION_TYPES.IMPORT_ERR_2;
} else if (event.status === 409) {
@ -122,6 +122,7 @@ export class ImportExportComponent implements OnInit {
}
private errorHandler(key: NOTIFICATION_TYPES, passedError?: HttpErrorResponse) {
console.log(key, passedError);
this.errorsService.triggerError(key, passedError);
delete this.selectedFileInput.files;
this.resetProgress();

View File

@ -4,6 +4,8 @@ import { TaskanaDate } from 'app/shared/util/taskana.date';
import { BlobGenerator } from 'app/shared/util/blob-generator';
import { Classification } from '../../shared/models/classification';
import { StartupService } from '../../shared/services/startup/startup.service';
import { take } from 'rxjs/operators';
import { Observable } from 'rxjs';
@Injectable()
export class ClassificationDefinitionService {
@ -14,9 +16,12 @@ export class ClassificationDefinitionService {
}
// GET
async exportClassifications(domain: string) {
exportClassifications(domain: string): Observable<Classification[]> {
const domainRequest = domain ? '' : `?domain=${domain}`;
const classificationDefinitions = await this.httpClient.get<Classification[]>(this.url + domainRequest).toPromise();
BlobGenerator.saveFile(classificationDefinitions, `Classifications_${TaskanaDate.getDate()}.json`);
const classificationDefObservable = this.httpClient.get<Classification[]>(this.url + domainRequest).pipe(take(1));
classificationDefObservable.subscribe((classificationDefinitions) =>
BlobGenerator.saveFile(classificationDefinitions, `Classifications_${TaskanaDate.getDate()}.json`)
);
return classificationDefObservable;
}
}

View File

@ -4,6 +4,8 @@ import { environment } from 'environments/environment';
import { WorkbasketDefinition } from 'app/shared/models/workbasket-definition';
import { TaskanaDate } from 'app/shared/util/taskana.date';
import { BlobGenerator } from 'app/shared/util/blob-generator';
import { take } from 'rxjs/operators';
import { Observable } from 'rxjs';
@Injectable()
export class WorkbasketDefinitionService {
@ -12,11 +14,12 @@ export class WorkbasketDefinitionService {
constructor(private httpClient: HttpClient) {}
// GET
async exportWorkbaskets(domain: string) {
exportWorkbaskets(domain: string): Observable<WorkbasketDefinition[]> {
const domainRequest = domain === '' ? domain : `?domain=${domain}`;
const workbasketDefinitions = await this.httpClient
.get<WorkbasketDefinition[]>(this.url + domainRequest)
.toPromise();
BlobGenerator.saveFile(workbasketDefinitions, `Workbaskets_${TaskanaDate.getDate()}.json`);
const workbasketDefObservable = this.httpClient.get<WorkbasketDefinition[]>(this.url + domainRequest).pipe(take(1));
workbasketDefObservable.subscribe((workbasketDefinitions) =>
BlobGenerator.saveFile(workbasketDefinitions, `Classifications_${TaskanaDate.getDate()}.json`)
);
return workbasketDefObservable;
}
}

View File

@ -65,7 +65,7 @@ export class AppComponent implements OnInit, OnDestroy {
}
this.selectedRoute = value;
});
this.uploadingFileSubscription = this.uploadService.getCurrentProgressValue().subscribe((value) => {
this.uploadingFileSubscription = this.uploadService.getCurrentProgressObservable().subscribe((value) => {
this.currentProgressValue = value;
});
}

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { Observable, Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
@ -12,7 +12,7 @@ export class UploadService {
this.currentProgressValue.next(value);
}
getCurrentProgressValue() {
getCurrentProgressObservable(): Observable<number> {
return this.currentProgressValue.asObservable();
}
}