From b3a7c63b45560253cb39ce618d7874117ade193c Mon Sep 17 00:00:00 2001 From: pionnegru Date: Mon, 23 Dec 2019 10:56:48 +0200 Subject: [PATCH 1/6] create from template dialog --- .../create-from-template.dialog.html | 59 ++++++++ .../create-from-template.dialog.scss | 64 +++++++++ .../create-from-template.dialog.ts | 128 ++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 src/app/dialogs/node-templates/create-from-template.dialog.html create mode 100644 src/app/dialogs/node-templates/create-from-template.dialog.scss create mode 100644 src/app/dialogs/node-templates/create-from-template.dialog.ts diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.html b/src/app/dialogs/node-templates/create-from-template.dialog.html new file mode 100644 index 000000000..a542bebd2 --- /dev/null +++ b/src/app/dialogs/node-templates/create-from-template.dialog.html @@ -0,0 +1,59 @@ +

+
+
+ + + + + {{ form.controls['name'].errors?.message | translate }} + + + + + + + + {{ 'FILE_FROM_TEMPLATE.FORM.ERRORS.TITLE_TOO_LONG' | translate }} + + + + + + + + {{ 'FILE_FROM_TEMPLATE.FORM.ERRORS.DESCRIPTION_TOO_LONG' | translate }} + + +
+
+
+ + +
diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.scss b/src/app/dialogs/node-templates/create-from-template.dialog.scss new file mode 100644 index 000000000..b88024c6e --- /dev/null +++ b/src/app/dialogs/node-templates/create-from-template.dialog.scss @@ -0,0 +1,64 @@ +@mixin app-create-file-from-template-theme($theme) { + $primary: map-get($theme, primary); + $accent: map-get($theme, accent); + $foreground: map-get($theme, foreground); + $background: map-get($theme, background); + + .aca-file-from-template-dialog { + ng-component { + overflow: visible; + } + + .mat-dialog-title { + margin-left: 24px; + margin-right: 24px; + font-size: 20px; + font-style: normal; + font-stretch: normal; + line-height: 1.6; + letter-spacing: -0.5px; + color: mat-color($foreground, text, 0.87); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + .bold { + font-weight: 600; + } + } + + .mat-form-field { + margin-bottom: 20px; + } + + .mat-dialog-container { + padding-left: 0; + padding-right: 0; + } + + .mat-dialog-content { + margin: 0 2px; + overflow: hidden; + } + + .mat-dialog-actions { + padding: 8px 22px; + display: flex; + justify-content: flex-end; + color: mat-color($foreground, secondary-text); + + button { + text-transform: uppercase; + font-weight: normal; + } + + .create:disabled { + color: mat-color($primary); + } + + .create { + color: mat-color($accent); + } + } + } +} diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.ts b/src/app/dialogs/node-templates/create-from-template.dialog.ts new file mode 100644 index 000000000..9e7562d57 --- /dev/null +++ b/src/app/dialogs/node-templates/create-from-template.dialog.ts @@ -0,0 +1,128 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { Component, ViewEncapsulation, Inject, OnInit } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { Node } from '@alfresco/js-api'; +import { + FormBuilder, + FormGroup, + Validators, + FormControl, + ValidationErrors +} from '@angular/forms'; + +@Component({ + templateUrl: './create-from-template.dialog.html', + encapsulation: ViewEncapsulation.None, + styleUrls: ['./create-from-template.dialog.scss'] +}) +export class CreateFileFromTemplateDialogComponent implements OnInit { + public form: FormGroup; + + constructor( + private formBuilder: FormBuilder, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any + ) {} + + ngOnInit() { + this.form = this.formBuilder.group({ + name: [ + this.data.name, + [ + Validators.required, + this.forbidEndingDot, + this.forbidOnlySpaces, + this.forbidSpecialCharacters + ] + ], + title: [this.data.properties['cm:title'], Validators.maxLength(256)], + description: [ + this.data.properties['cm:description'], + Validators.maxLength(512) + ] + }); + } + + onSubmit() { + const update = { + name: this.form.value.name, + properties: { + 'cm:title': this.form.value.title, + 'cm:description': this.form.value.description + } + }; + const data: Node = Object.assign({}, this.data, update); + this.dialogRef.close(data); + } + + close() { + this.dialogRef.close(); + } + + private forbidSpecialCharacters({ + value + }: FormControl): ValidationErrors | null { + const specialCharacters: RegExp = /([\*\"\<\>\\\/\?\:\|])/; + const isValid: boolean = !specialCharacters.test(value); + + return isValid + ? null + : { + message: `FILE_FROM_TEMPLATE.FORM.ERRORS.SPECIAL_CHARACTERS` + }; + } + + private forbidEndingDot({ value }: FormControl): ValidationErrors | null { + const isValid: boolean = + (value || '') + .trim() + .split('') + .pop() !== '.'; + + return isValid + ? null + : { + message: `FILE_FROM_TEMPLATE.FORM.ERRORS.ENDING_DOT` + }; + } + + private forbidOnlySpaces({ value }: FormControl): ValidationErrors | null { + if (value.length) { + const isValid: boolean = !!(value || '').trim(); + + return isValid + ? null + : { + message: `FILE_FROM_TEMPLATE.FORM.ERRORS.ONLY_SPACES` + }; + } else { + return { + message: `FILE_FROM_TEMPLATE.FORM.ERRORS.REQUIRED` + }; + } + } +} From 42cdbc293172847c1f9154ee8ea1136dd48ce49e Mon Sep 17 00:00:00 2001 From: pionnegru Date: Mon, 23 Dec 2019 10:59:01 +0200 Subject: [PATCH 2/6] add open create from template --- src/app/app.module.ts | 8 ++-- .../create-file-from-template.service.ts | 39 ++++++++++++------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ec362240d..111bad187 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -76,7 +76,7 @@ import { AppNodeVersionModule } from './components/node-version/node-version.mod import { FavoritesComponent } from './components/favorites/favorites.component'; import { RecentFilesComponent } from './components/recent-files/recent-files.component'; import { SharedFilesComponent } from './components/shared-files/shared-files.component'; - +import { CreateFileFromTemplateDialogComponent } from './dialogs/node-templates/create-from-template.dialog'; import { environment } from '../environments/environment'; import { registerLocaleData } from '@angular/common'; @@ -158,7 +158,8 @@ registerLocaleData(localeSv); NodeVersionsDialogComponent, FavoritesComponent, RecentFilesComponent, - SharedFilesComponent + SharedFilesComponent, + CreateFileFromTemplateDialogComponent ], providers: [ { provide: RouteReuseStrategy, useClass: AppRouteReuseStrategy }, @@ -175,7 +176,8 @@ registerLocaleData(localeSv); entryComponents: [ NodeVersionsDialogComponent, NodeVersionUploadDialogComponent, - LibraryDialogComponent + LibraryDialogComponent, + CreateFileFromTemplateDialogComponent ], bootstrap: [AppComponent] }) diff --git a/src/app/services/create-file-from-template.service.ts b/src/app/services/create-file-from-template.service.ts index 9848b22c0..f9a8d8790 100644 --- a/src/app/services/create-file-from-template.service.ts +++ b/src/app/services/create-file-from-template.service.ts @@ -24,17 +24,18 @@ */ import { Injectable } from '@angular/core'; -import { MatDialog, MatDialogConfig } from '@angular/material'; -import { - ContentNodeSelectorComponentData, - ContentNodeSelectorComponent -} from '@alfresco/adf-content-services'; +import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material'; +import { CreateFileFromTemplateDialogComponent } from '../dialogs/node-templates/create-from-template.dialog'; import { Subject, from, of } from 'rxjs'; -import { Node } from '@alfresco/js-api'; -import { AlfrescoApiService } from '@alfresco/adf-core'; +import { Node, MinimalNode } from '@alfresco/js-api'; +import { AlfrescoApiService, TranslationService } from '@alfresco/adf-core'; import { switchMap, catchError } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { AppStore, SnackbarErrorAction } from '@alfresco/aca-shared/store'; +import { + ContentNodeSelectorComponent, + ContentNodeSelectorComponentData +} from '@alfresco/adf-content-services'; @Injectable({ providedIn: 'root' @@ -43,6 +44,7 @@ export class CreateFileFromTemplateService { constructor( private store: Store, private alfrescoApiService: AlfrescoApiService, + private translation: TranslationService, public dialog: MatDialog ) {} @@ -53,7 +55,8 @@ export class CreateFileFromTemplateService { }); const data: ContentNodeSelectorComponentData = { - title: null, + title: this.title, + actionName: 'NEXT', dropdownHideMyFiles: true, currentFolderId: null, dropdownSiteList: null, @@ -62,10 +65,6 @@ export class CreateFileFromTemplateService { isSelectionValid: this.isSelectionValid.bind(this) }; - data.select.subscribe({ - complete: this.close.bind(this) - }); - from( this.alfrescoApiService.getInstance().nodes.getNodeInfo('-root-', { relativePath: 'Data Dictionary/Node Templates' @@ -97,7 +96,17 @@ export class CreateFileFromTemplateService { return select; } - private transformNode(node: Node): Node { + createTemplateDialog( + node: Node + ): MatDialogRef { + return this.dialog.open(CreateFileFromTemplateDialogComponent, { + data: node, + panelClass: 'aca-file-from-template-dialog', + width: '630px' + }); + } + + private transformNode(node: MinimalNode): MinimalNode { if (node && node.path && node.path && node.path.elements instanceof Array) { let { path: { elements: elementsPath = [] } @@ -118,4 +127,8 @@ export class CreateFileFromTemplateService { private close() { this.dialog.closeAll(); } + + private get title() { + return this.translation.instant('NODE_SELECTOR.SELECT_TEMPLATE_TITLE'); + } } From 9e93d462f76df53eb9030b948b4c320b039af84f Mon Sep 17 00:00:00 2001 From: pionnegru Date: Mon, 23 Dec 2019 11:00:02 +0200 Subject: [PATCH 3/6] open create dialog after template selection --- src/app/store/effects/template.effects.ts | 85 ++++++++++++++++++----- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/src/app/store/effects/template.effects.ts b/src/app/store/effects/template.effects.ts index 9ae79a2e1..ec7485076 100644 --- a/src/app/store/effects/template.effects.ts +++ b/src/app/store/effects/template.effects.ts @@ -25,7 +25,15 @@ import { Effect, Actions, ofType } from '@ngrx/effects'; import { Injectable } from '@angular/core'; -import { map, withLatestFrom, switchMap, catchError } from 'rxjs/operators'; +import { + map, + withLatestFrom, + switchMap, + catchError, + debounceTime, + flatMap, + skipWhile +} from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { CreateFileFromTemplate, @@ -37,8 +45,8 @@ import { import { CreateFileFromTemplateService } from '../../services/create-file-from-template.service'; import { AlfrescoApiService } from '@alfresco/adf-core'; import { ContentManagementService } from '../../services/content-management.service'; -import { from, of } from 'rxjs'; -import { NodeEntry } from '@alfresco/js-api'; +import { from, of, Observable } from 'rxjs'; +import { NodeEntry, NodeBodyUpdate, MinimalNode } from '@alfresco/js-api'; @Injectable() export class TemplateEffects { @@ -57,31 +65,70 @@ export class TemplateEffects { this.createFileFromTemplateService .openTemplatesDialog() .pipe( + debounceTime(300), + flatMap(([node]) => + this.createFileFromTemplateService + .createTemplateDialog(node) + .afterClosed() + ), + skipWhile(node => !node), withLatestFrom(this.store.select(getCurrentFolder)), - switchMap(([[template], parentNode]) => { - return from( - this.apiService - .getInstance() - .nodes.copyNode(template.id, { targetParentId: parentNode.id }) - ); + switchMap(([template, parentNode]) => { + return this.copyNode(template, parentNode.id); }), catchError(error => { - const { statusCode } = JSON.parse(error.message).error; - - if (statusCode !== 409) { - this.store.dispatch( - new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC') - ); - } - - return of(null); + return this.handleError(error); }) ) .subscribe((node: NodeEntry | null) => { if (node) { - this.content.reload.next(); + this.content.reload.next(node); } }); }) ); + + private copyNode( + source: MinimalNode, + parentId: string + ): Observable { + return from( + this.apiService.getInstance().nodes.copyNode(source.id, { + targetParentId: parentId, + name: source.name + }) + ).pipe( + switchMap(node => + this.updateNode(node.entry.id, { + properties: { + 'cm:title': source.properties['cm:title'], + 'cm:description': source.properties['cm:description'] + } + }) + ) + ); + } + + private updateNode( + id: string, + update: NodeBodyUpdate + ): Observable { + return from(this.apiService.getInstance().nodes.updateNode(id, update)); + } + + private handleError(error: Error): Observable { + const { statusCode } = JSON.parse(error.message).error; + + if (statusCode !== 409) { + this.store.dispatch( + new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC') + ); + } else { + this.store.dispatch( + new SnackbarErrorAction('APP.MESSAGES.ERRORS.CONFLICT') + ); + } + + return of(null); + } } From 04fc47c08ddd2cf53973332412d4dc396494cea7 Mon Sep 17 00:00:00 2001 From: pionnegru Date: Mon, 23 Dec 2019 11:00:35 +0200 Subject: [PATCH 4/6] add dialog theme --- src/app/ui/custom-theme.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/ui/custom-theme.scss b/src/app/ui/custom-theme.scss index db39d309c..3059f4a95 100644 --- a/src/app/ui/custom-theme.scss +++ b/src/app/ui/custom-theme.scss @@ -10,6 +10,7 @@ @import '../dialogs/node-versions/node-versions.dialog.theme'; @import '../components/create-menu/create-menu.component.scss'; @import '../components/layout/layout.theme.scss'; +@import '../dialogs/node-templates/create-from-template.dialog.scss'; @import './overrides/adf-style-fixes.theme'; @@ -67,6 +68,7 @@ $warn: map-get($custom-theme, warn); @include sidenav-component-theme($theme); @include aca-current-user-theme($theme); @include aca-context-menu-theme($theme); + @include app-create-file-from-template-theme($theme); @include app-create-menu-theme($theme); @include adf-style-fixes($theme); From bc570acca5a26752763a30e29ae52134c3357498 Mon Sep 17 00:00:00 2001 From: pionnegru Date: Mon, 23 Dec 2019 11:00:59 +0200 Subject: [PATCH 5/6] add i18n --- src/assets/i18n/en.json | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 7813a920c..931b9ad41 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -356,7 +356,29 @@ "COPY_ITEMS": "Copy {{ number }} items to...", "MOVE_ITEM": "Move '{{ name }}' to...", "MOVE_ITEMS": "Move {{ number }} items to...", - "SEARCH": "Search" + "SEARCH": "Search", + "NEXT": "Next", + "SELECT_TEMPLATE_TITLE": "Select a document template" + }, + "FILE_FROM_TEMPLATE": { + "CANCEL": "CANCEL", + "CREATE": "Create", + "TITLE": "Create new document from '{{ template }}'", + "FORM": { + "PLACEHOLDER": { + "NAME": "Name", + "TITLE": "Title", + "DESCRIPTION": "Description" + }, + "ERRORS": { + "DESCRIPTION_TOO_LONG": "Use 512 characters or less for description", + "TITLE_TOO_LONG": "Use 256 characters or less for title", + "REQUIRED": "File name is required", + "SPECIAL_CHARACTERS": "File name can't contain these characters * \" < > \\ / ? : |", + "ENDING_DOT": "File name can't end with a period .", + "ONLY_SPACES": "File name can't contain only spaces" + } + } }, "PERMISSIONS": { "DIALOG": { From c1dfed9e1c6880f6d49585df56ddb358b40d2aaa Mon Sep 17 00:00:00 2001 From: pionnegru Date: Mon, 23 Dec 2019 11:01:38 +0200 Subject: [PATCH 6/6] tests --- .../create-from-template.dialog.spec.ts | 140 ++++++++++++++++++ .../store/effects/template.effects.spec.ts | 76 +++++++++- 2 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 src/app/dialogs/node-templates/create-from-template.dialog.spec.ts diff --git a/src/app/dialogs/node-templates/create-from-template.dialog.spec.ts b/src/app/dialogs/node-templates/create-from-template.dialog.spec.ts new file mode 100644 index 000000000..0ff8ee84f --- /dev/null +++ b/src/app/dialogs/node-templates/create-from-template.dialog.spec.ts @@ -0,0 +1,140 @@ +/*! + * @license + * Alfresco Example Content Application + * + * Copyright (C) 2005 - 2019 Alfresco Software Limited + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +import { CreateFileFromTemplateDialogComponent } from './create-from-template.dialog'; +import { TestBed, ComponentFixture } from '@angular/core/testing'; +import { AppTestingModule } from '../../testing/app-testing.module'; +import { CoreModule } from '@alfresco/adf-core'; +import { + MatDialogModule, + MatDialogRef, + MAT_DIALOG_DATA +} from '@angular/material/dialog'; + +function text(length: number) { + return new Array(length) + .fill( + Math.random() + .toString() + .substring(2, 3) + ) + .join(''); +} + +describe('CreateFileFromTemplateDialogComponent', () => { + let fixture: ComponentFixture; + let component: CreateFileFromTemplateDialogComponent; + let dialogRef: MatDialogRef; + + const data = { + id: 'node-id', + name: 'node-name', + properties: { + 'cm:title': 'node-title', + 'cm:description': '' + } + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CoreModule.forRoot(), AppTestingModule, MatDialogModule], + declarations: [CreateFileFromTemplateDialogComponent], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: data }, + { + provide: MatDialogRef, + useValue: { + close: jasmine.createSpy('close') + } + } + ] + }); + + fixture = TestBed.createComponent(CreateFileFromTemplateDialogComponent); + dialogRef = TestBed.get(MatDialogRef); + component = fixture.componentInstance; + + fixture.detectChanges(); + }); + + it('should populate form with provided dialog data', () => { + expect(component.form.controls.name.value).toBe(data.name); + expect(component.form.controls.title.value).toBe( + data.properties['cm:title'] + ); + expect(component.form.controls.description.value).toBe( + data.properties['cm:description'] + ); + }); + + it('should invalidate form if required `name` field is invalid', () => { + component.form.controls.name.setValue(''); + fixture.detectChanges(); + expect(component.form.invalid).toBe(true); + }); + + it('should invalidate form if required `name` field has `only spaces`', () => { + component.form.controls.name.setValue(' '); + fixture.detectChanges(); + expect(component.form.invalid).toBe(true); + }); + + it('should invalidate form if required `name` field has `ending dot`', () => { + component.form.controls.name.setValue('something.'); + fixture.detectChanges(); + expect(component.form.invalid).toBe(true); + }); + + it('should invalidate form if `title` text length is long', () => { + component.form.controls.title.setValue(text(260)); + fixture.detectChanges(); + expect(component.form.invalid).toBe(true); + }); + + it('should invalidate form if `description` text length is long', () => { + component.form.controls.description.setValue(text(520)); + fixture.detectChanges(); + expect(component.form.invalid).toBe(true); + }); + + it('should update data with form values', () => { + component.form.controls.name.setValue('new-node-name'); + component.form.controls.title.setValue('new-node-title'); + component.form.controls.description.setValue('new-node-description'); + + fixture.detectChanges(); + + component.onSubmit(); + + expect(dialogRef.close['calls'].argsFor(0)[0]).toEqual({ + id: 'node-id', + name: 'new-node-name', + properties: { + 'cm:title': 'new-node-title', + 'cm:description': 'new-node-description' + } + }); + }); +}); diff --git a/src/app/store/effects/template.effects.spec.ts b/src/app/store/effects/template.effects.spec.ts index 51e976b27..266656a88 100644 --- a/src/app/store/effects/template.effects.spec.ts +++ b/src/app/store/effects/template.effects.spec.ts @@ -36,12 +36,28 @@ import { CreateFileFromTemplateService } from '../../services/create-file-from-t import { of } from 'rxjs'; import { AlfrescoApiServiceMock, AlfrescoApiService } from '@alfresco/adf-core'; import { ContentManagementService } from '../../services/content-management.service'; +import { Node } from '@alfresco/js-api'; describe('TemplateEffects', () => { let store: Store; let createFileFromTemplateService: CreateFileFromTemplateService; let alfrescoApiService: AlfrescoApiService; let contentManagementService: ContentManagementService; + const node: Node = { + name: 'node-name', + id: 'node-id', + nodeType: 'cm:content', + isFolder: false, + isFile: true, + modifiedAt: null, + modifiedByUser: null, + createdAt: null, + createdByUser: null, + properties: { + 'cm:title': 'title', + 'cm:description': 'description' + } + }; beforeEach(() => { TestBed.configureTestingModule({ @@ -64,17 +80,27 @@ describe('TemplateEffects', () => { ); }); - it('should reload content on template copy', fakeAsync(() => { + it('should reload content on create file from template', fakeAsync(() => { spyOn(alfrescoApiService.getInstance().nodes, 'copyNode').and.returnValue( + of({ entry: { id: 'node-id' } }) + ); + + spyOn(alfrescoApiService.getInstance().nodes, 'updateNode').and.returnValue( of({}) ); + + spyOn( + createFileFromTemplateService, + 'createTemplateDialog' + ).and.returnValue({ afterClosed: () => of(node) }); + store.dispatch(new CreateFileFromTemplate()); - tick(); + tick(300); expect(contentManagementService.reload.next).toHaveBeenCalled(); })); - it('should raise error when copy template fails', fakeAsync(() => { + it('should raise error when copyNode api fails', fakeAsync(() => { spyOn(store, 'dispatch').and.callThrough(); spyOn(alfrescoApiService.getInstance().nodes, 'copyNode').and.returnValue( Promise.reject({ @@ -82,12 +108,54 @@ describe('TemplateEffects', () => { }) ); + spyOn( + createFileFromTemplateService, + 'createTemplateDialog' + ).and.returnValue({ afterClosed: () => of(node) }); + store.dispatch(new CreateFileFromTemplate()); - tick(); + tick(300); expect(contentManagementService.reload.next).not.toHaveBeenCalled(); expect(store.dispatch['calls'].argsFor(1)[0]).toEqual( new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC') ); })); + + it('should raise error when updateNode api fails', fakeAsync(() => { + spyOn(store, 'dispatch').and.callThrough(); + spyOn(alfrescoApiService.getInstance().nodes, 'copyNode').and.returnValue( + of({ entry: { id: 'node-id' } }) + ); + + spyOn(alfrescoApiService.getInstance().nodes, 'updateNode').and.returnValue( + Promise.reject({ + message: `{ "error": { "statusCode": 404 } } ` + }) + ); + + spyOn( + createFileFromTemplateService, + 'createTemplateDialog' + ).and.returnValue({ afterClosed: () => of(node) }); + + store.dispatch(new CreateFileFromTemplate()); + tick(300); + + expect(contentManagementService.reload.next).not.toHaveBeenCalled(); + expect(store.dispatch['calls'].argsFor(1)[0]).toEqual( + new SnackbarErrorAction('APP.MESSAGES.ERRORS.GENERIC') + ); + })); + + it('should update file from template with form data', () => { + spyOn(alfrescoApiService.getInstance().nodes, 'copyNode').and.returnValue( + of({ entry: { id: 'node-id' } }) + ); + + spyOn( + createFileFromTemplateService, + 'createTemplateDialog' + ).and.returnValue({ afterClosed: () => of(node) }); + }); });