[ACA-2643] Upload new version - refactoring (#1138)

* refactor upload version effect

* subscriber noop fn

* move logic to subscribe

* tests
This commit is contained in:
Cilibiu Bogdan 2019-06-26 10:33:13 +03:00 committed by Adina Parpalita
parent 9b74063032
commit 9e0e50d57d
2 changed files with 185 additions and 113 deletions

View File

@ -34,13 +34,34 @@ import {
FileUploadCompleteEvent, FileUploadCompleteEvent,
FileModel FileModel
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
import { UnlockWriteAction } from '@alfresco/aca-shared/store'; import {
UnlockWriteAction,
SnackbarErrorAction
} from '@alfresco/aca-shared/store';
import { ContentManagementService } from '../../services/content-management.service';
import { of, throwError } from 'rxjs';
function createFileList(fileName, type = 'text/plain') {
const data = new Blob([''], { type });
const arrayOfBlob = new Array<Blob>();
arrayOfBlob.push(data);
const file = new File(arrayOfBlob, fileName);
const files = [file];
const reducer = (dataTransfer, currentFile) => {
dataTransfer.items.add(currentFile);
return dataTransfer;
};
return files.reduce(reducer, new DataTransfer()).files;
}
describe('UploadEffects', () => { describe('UploadEffects', () => {
let store: Store<any>; let store: Store<any>;
let uploadService: UploadService; let uploadService: UploadService;
let effects: UploadEffects; let effects: UploadEffects;
let zone: NgZone; let zone: NgZone;
let contentManagementService: ContentManagementService;
let uploadVersionInput: HTMLInputElement;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@ -52,121 +73,165 @@ describe('UploadEffects', () => {
return fn(); return fn();
}); });
contentManagementService = TestBed.get(ContentManagementService);
store = TestBed.get(Store); store = TestBed.get(Store);
uploadService = TestBed.get(UploadService); uploadService = TestBed.get(UploadService);
effects = TestBed.get(UploadEffects); effects = TestBed.get(UploadEffects);
}); });
it('should work', () => { beforeEach(() => {
expect(store).toBeDefined(); uploadVersionInput = document.querySelector('#app-upload-file-version');
expect(uploadService).toBeDefined();
expect(effects).toBeDefined();
}); });
it('should not upload and unlock file if param not provided', () => { afterEach(() => {
effects.uploadAndUnlock(null); uploadVersionInput.remove();
expect(zone.run).not.toHaveBeenCalled();
}); });
it('should upload the file before unlocking', () => { describe('uploadAndUnlock()', () => {
const file: any = {}; it('should not upload and unlock file if param not provided', () => {
effects.uploadAndUnlock(null);
expect(zone.run).not.toHaveBeenCalled();
});
spyOn(uploadService, 'addToQueue').and.stub(); it('should upload the file before unlocking', () => {
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub(); const file: any = {};
effects.uploadAndUnlock(file); spyOn(uploadService, 'addToQueue').and.stub();
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
expect(uploadService.addToQueue).toHaveBeenCalled(); effects.uploadAndUnlock(file);
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled();
});
it('should dispatch the unlock write action for a locked file', () => { expect(uploadService.addToQueue).toHaveBeenCalled();
const file: FileModel = new FileModel( expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled();
<File>{ name: 'file1.png', size: 10 }, });
null,
'file1'
);
file.data = { it('should dispatch the unlock write action for a locked file', () => {
entry: { const file: FileModel = new FileModel(
id: 'file1', <File>{ name: 'file1.png', size: 10 },
properties: { null,
'cm:lockType': 'WRITE_LOCK' 'file1'
);
file.data = {
entry: {
id: 'file1',
properties: {
'cm:lockType': 'WRITE_LOCK'
}
} }
} };
};
spyOn(uploadService, 'addToQueue').and.stub(); spyOn(uploadService, 'addToQueue').and.stub();
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub(); spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
spyOn(store, 'dispatch').and.stub(); spyOn(store, 'dispatch').and.stub();
effects.uploadAndUnlock(file); effects.uploadAndUnlock(file);
uploadService.fileUploadComplete.next( uploadService.fileUploadComplete.next(
new FileUploadCompleteEvent(file, 100, file.data) new FileUploadCompleteEvent(file, 100, file.data)
); );
expect(store.dispatch).toHaveBeenCalledWith( expect(store.dispatch).toHaveBeenCalledWith(
new UnlockWriteAction(file.data) new UnlockWriteAction(file.data)
); );
}); });
it('should dispatch only one unlock action for a locked file', () => { it('should dispatch only one unlock action for a locked file', () => {
const file: FileModel = new FileModel( const file: FileModel = new FileModel(
<File>{ name: 'file1.png', size: 10 }, <File>{ name: 'file1.png', size: 10 },
null, null,
'file1' 'file1'
); );
file.data = { file.data = {
entry: { entry: {
id: 'file1', id: 'file1',
properties: { properties: {
'cm:lockType': 'WRITE_LOCK' 'cm:lockType': 'WRITE_LOCK'
}
} }
} };
};
spyOn(uploadService, 'addToQueue').and.stub(); spyOn(uploadService, 'addToQueue').and.stub();
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub(); spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
spyOn(store, 'dispatch').and.stub(); spyOn(store, 'dispatch').and.stub();
effects.uploadAndUnlock(file); effects.uploadAndUnlock(file);
const completeEvent = new FileUploadCompleteEvent(file, 100, file.data); const completeEvent = new FileUploadCompleteEvent(file, 100, file.data);
uploadService.fileUploadComplete.next(completeEvent); uploadService.fileUploadComplete.next(completeEvent);
uploadService.fileUploadComplete.next(completeEvent); uploadService.fileUploadComplete.next(completeEvent);
uploadService.fileUploadComplete.next(completeEvent); uploadService.fileUploadComplete.next(completeEvent);
expect(store.dispatch).toHaveBeenCalledWith( expect(store.dispatch).toHaveBeenCalledWith(
new UnlockWriteAction(file.data) new UnlockWriteAction(file.data)
); );
expect(store.dispatch).toHaveBeenCalledTimes(1); expect(store.dispatch).toHaveBeenCalledTimes(1);
});
it('should dispatch no actions if file is not locked', () => {
const file: FileModel = new FileModel(
<File>{ name: 'file1.png', size: 10 },
null,
'file1'
);
file.data = {
entry: {
id: 'file1',
properties: {}
}
};
spyOn(uploadService, 'addToQueue').and.stub();
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
spyOn(store, 'dispatch').and.stub();
effects.uploadAndUnlock(file);
uploadService.fileUploadComplete.next(
new FileUploadCompleteEvent(file, 100, file.data)
);
expect(store.dispatch).not.toHaveBeenCalled();
});
}); });
it('should dispatch no actions if file is not locked', () => { describe('upload file version', () => {
const file: FileModel = new FileModel( beforeEach(() => {
<File>{ name: 'file1.png', size: 10 }, const dialog = { afterClosed: () => of({}) };
null, spyOn(contentManagementService, 'versionUploadDialog').and.returnValue(
'file1' dialog
); );
spyOn(effects, 'uploadAndUnlock').and.stub();
});
file.data = { it('should upload file', () => {
entry: { spyOn(contentManagementService, 'getNodeInfo').and.returnValue(
id: 'file1', of({
properties: {} entry: {
} id: 'file1',
}; properties: {}
}
})
);
spyOn(uploadService, 'addToQueue').and.stub(); uploadVersionInput.files = createFileList('bogus.txt');
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub(); uploadVersionInput.dispatchEvent(new CustomEvent('change'));
spyOn(store, 'dispatch').and.stub(); expect(effects.uploadAndUnlock).toHaveBeenCalled();
});
effects.uploadAndUnlock(file); it('should raise error when getNodeInfo fails', () => {
uploadService.fileUploadComplete.next( spyOn(store, 'dispatch').and.stub();
new FileUploadCompleteEvent(file, 100, file.data) spyOn(contentManagementService, 'getNodeInfo').and.returnValue(
); throwError('error')
);
expect(store.dispatch).not.toHaveBeenCalled(); uploadVersionInput.files = createFileList('bogus.txt');
uploadVersionInput.dispatchEvent(new CustomEvent('change'));
expect(store.dispatch).toHaveBeenCalledWith(
new SnackbarErrorAction('VERSION.ERROR.GENERIC')
);
expect(effects.uploadAndUnlock).not.toHaveBeenCalled();
});
}); });
}); });

View File

@ -37,17 +37,8 @@ import { FileModel, FileUtils, UploadService } from '@alfresco/adf-core';
import { Injectable, NgZone, RendererFactory2 } from '@angular/core'; import { Injectable, NgZone, RendererFactory2 } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects'; import { Actions, Effect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { forkJoin, fromEvent, of } from 'rxjs'; import { forkJoin, of } from 'rxjs';
import { import { tap, filter, catchError, flatMap, map, take } from 'rxjs/operators';
catchError,
distinctUntilChanged,
filter,
flatMap,
map,
switchMap,
take,
tap
} from 'rxjs/operators';
import { ContentManagementService } from '../../services/content-management.service'; import { ContentManagementService } from '../../services/content-management.service';
@Injectable() @Injectable()
@ -78,7 +69,9 @@ export class UploadEffects {
this.fileVersionInput.id = 'app-upload-file-version'; this.fileVersionInput.id = 'app-upload-file-version';
this.fileVersionInput.type = 'file'; this.fileVersionInput.type = 'file';
this.fileVersionInput.style.display = 'none'; this.fileVersionInput.style.display = 'none';
this.fileVersionInput.addEventListener('change', event => event); this.fileVersionInput.addEventListener('change', () =>
this.uploadVersion()
);
renderer.appendChild(document.body, this.fileVersionInput); renderer.appendChild(document.body, this.fileVersionInput);
this.folderInput = renderer.createElement('input') as HTMLInputElement; this.folderInput = renderer.createElement('input') as HTMLInputElement;
@ -110,19 +103,38 @@ export class UploadEffects {
@Effect({ dispatch: false }) @Effect({ dispatch: false })
uploadVersion$ = this.actions$.pipe( uploadVersion$ = this.actions$.pipe(
ofType<UploadFileVersionAction>(UploadActionTypes.UploadFileVersion), ofType<UploadFileVersionAction>(UploadActionTypes.UploadFileVersion),
switchMap(() => { map(() => {
this.fileVersionInput.click(); this.fileVersionInput.click();
return fromEvent(this.fileVersionInput, 'change').pipe( })
distinctUntilChanged(), );
flatMap(() => this.contentService.versionUploadDialog().afterClosed()),
private uploadVersion() {
this.contentService
.versionUploadDialog()
.afterClosed()
.pipe(
tap(form => { tap(form => {
if (!form) { if (!form) {
this.fileVersionInput.value = ''; this.fileVersionInput.value = '';
} }
}), }),
filter(form => !!form), filter(form => !!form),
flatMap(form => forkJoin(of(form), this.contentService.getNodeInfo())), flatMap(form =>
map(([form, node]) => { forkJoin(
of(form),
this.contentService.getNodeInfo().pipe(
catchError(_ => {
this.store.dispatch(
new SnackbarErrorAction('VERSION.ERROR.GENERIC')
);
return of(null);
})
)
)
)
)
.subscribe(([form, node]) => {
if (form && node) {
const file = this.fileVersionInput.files[0]; const file = this.fileVersionInput.files[0];
const fileModel = new FileModel( const fileModel = new FileModel(
file, file,
@ -139,17 +151,12 @@ export class UploadEffects {
}, },
node.id node.id
); );
this.fileVersionInput.value = '';
this.uploadAndUnlock(fileModel); this.uploadAndUnlock(fileModel);
}), }
catchError(_ => {
this.fileVersionInput.value = ''; this.fileVersionInput.value = '';
return of(new SnackbarErrorAction('VERSION.ERROR.GENERIC')); });
}) }
);
})
);
private upload(event: any): void { private upload(event: any): void {
this.store this.store