diff --git a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts index b37571782c..988f70d4be 100644 --- a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts +++ b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.spec.ts @@ -25,20 +25,29 @@ import { LibraryMetadataFormComponent } from './library-metadata-form.component'; import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing'; import { Store } from '@ngrx/store'; -import { UpdateLibraryAction } from '@alfresco/aca-shared/store'; +import { SnackbarAction, SnackbarErrorAction, SnackbarInfoAction, UpdateLibraryAction } from '@alfresco/aca-shared/store'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { Site, SitePaging } from '@alfresco/js-api'; +import { Site, SiteBodyCreate, SitePaging } from '@alfresco/js-api'; +import { Actions } from '@ngrx/effects'; +import { Subject } from 'rxjs'; describe('LibraryMetadataFormComponent', () => { let fixture: ComponentFixture; let component: LibraryMetadataFormComponent; let store: Store; + let actions$: Subject; + let siteEntryModel: SiteBodyCreate; beforeEach(() => { + actions$ = new Subject(); TestBed.configureTestingModule({ imports: [AppTestingModule, LibraryMetadataFormComponent], providers: [ + { + provide: Actions, + useValue: actions$ + }, { provide: Store, useValue: { @@ -53,13 +62,10 @@ describe('LibraryMetadataFormComponent', () => { fixture = TestBed.createComponent(LibraryMetadataFormComponent); component = fixture.componentInstance; - }); - - it('should initialize form with node data', () => { - const siteEntryModel = { + siteEntryModel = { title: 'libraryTitle', description: 'description', - visibility: 'PRIVATE' + visibility: Site.VisibilityEnum.PRIVATE }; component.node = { entry: { @@ -67,31 +73,21 @@ describe('LibraryMetadataFormComponent', () => { ...siteEntryModel } as Site }; + }); + + it('should initialize form with node data', () => { fixture.detectChanges(); expect(component.form.value).toEqual(siteEntryModel); }); it('should update form data when node data changes', () => { - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - const newSiteEntryModel = { title: 'libraryTitle2', description: 'description2', visibility: 'PUBLIC' }; - component.node = { - entry: { - id: 'libraryId', - ...siteEntryModel - } as Site - }; - fixture.detectChanges(); expect(component.form.value).toEqual(siteEntryModel); @@ -108,19 +104,61 @@ describe('LibraryMetadataFormComponent', () => { expect(component.form.value).toEqual(newSiteEntryModel); }); - it('should update library node if form is valid', () => { - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; + it('should assign form value to node entry if updating of form is finished with success', () => { + const entry = { + id: 'libraryId', + title: 'some different title', + description: 'some different description', + visibility: Site.VisibilityEnum.PUBLIC + } as Site; + component.ngOnInit(); + component.form.setValue(entry); + + actions$.next(new SnackbarInfoAction('LIBRARY.SUCCESS.LIBRARY_UPDATED')); + expect(component.node.entry).toEqual(jasmine.objectContaining(entry)); + }); + + it('should not assign form value to node entry if info snackbar was displayed for different action than updating library', () => { + const entry = { + id: 'libraryId', + title: 'some different title', + description: 'some different description', + visibility: Site.VisibilityEnum.PUBLIC + } as Site; + component.ngOnInit(); + component.form.setValue(entry); + + actions$.next(new SnackbarInfoAction('Some different action')); + expect(component.node.entry).not.toEqual(jasmine.objectContaining(entry)); + }); + + it('should call markAsDirty on form if updating of form is finished with error', () => { component.node = { entry: { id: 'libraryId', role: 'SiteManager', - ...siteEntryModel + title: 'libraryTitle', + description: 'description', + visibility: Site.VisibilityEnum.PRIVATE } as Site }; + component.ngOnInit(); + spyOn(component.form, 'markAsDirty'); + + actions$.next(new SnackbarErrorAction('LIBRARY.ERRORS.LIBRARY_UPDATE_ERROR')); + expect(component.form.markAsDirty).toHaveBeenCalled(); + }); + + it('should not call markAsDirty on form if error snackbar was displayed for different action than updating library', () => { + component.ngOnInit(); + spyOn(component.form, 'markAsDirty'); + + actions$.next(new SnackbarErrorAction('Some different action')); + expect(component.form.markAsDirty).not.toHaveBeenCalled(); + }); + + it('should update library node if form is valid', () => { + component.node.entry.role = Site.RoleEnum.SiteManager; fixture.detectChanges(); @@ -129,19 +167,17 @@ describe('LibraryMetadataFormComponent', () => { expect(store.dispatch).toHaveBeenCalledWith(new UpdateLibraryAction(siteEntryModel)); }); + it('should call markAsPristine on form when updating valid form and has permission to update', () => { + component.node.entry.role = Site.RoleEnum.SiteManager; + spyOn(component.form, 'markAsPristine'); + component.ngOnInit(); + + component.update(); + expect(component.form.markAsPristine).toHaveBeenCalled(); + }); + it('should not update library node if it has no permission', () => { - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - component.node = { - entry: { - id: 'libraryId', - role: 'Consumer', - ...siteEntryModel - } as Site - }; + component.node.entry.role = Site.RoleEnum.SiteConsumer; fixture.detectChanges(); @@ -150,19 +186,17 @@ describe('LibraryMetadataFormComponent', () => { expect(store.dispatch).not.toHaveBeenCalledWith(new UpdateLibraryAction(siteEntryModel)); }); + it('should not call markAsPristine on form when updating valid form but has not permission to update', () => { + component.node.entry.role = Site.RoleEnum.SiteConsumer; + spyOn(component.form, 'markAsPristine'); + component.ngOnInit(); + + component.update(); + expect(component.form.markAsPristine).not.toHaveBeenCalled(); + }); + it('should not update library node if form is invalid', () => { - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - component.node = { - entry: { - id: 'libraryId', - role: 'SiteManager', - ...siteEntryModel - } as Site - }; + component.node.entry.role = Site.RoleEnum.SiteManager; fixture.detectChanges(); @@ -173,6 +207,16 @@ describe('LibraryMetadataFormComponent', () => { expect(store.dispatch).not.toHaveBeenCalledWith(new UpdateLibraryAction(siteEntryModel)); }); + it('should not call markAsPristine on form when updating invalid form and has permission to update', () => { + component.node.entry.role = Site.RoleEnum.SiteManager; + spyOn(component.form, 'markAsPristine'); + spyOnProperty(component.form, 'valid').and.returnValue(false); + component.ngOnInit(); + + component.update(); + expect(component.form.markAsPristine).not.toHaveBeenCalled(); + }); + it('should toggle edit mode', () => { component.edit = false; @@ -184,17 +228,6 @@ describe('LibraryMetadataFormComponent', () => { }); it('should cancel from changes', () => { - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - component.node = { - entry: { - id: 'libraryId', - ...siteEntryModel - } as Site - }; fixture.detectChanges(); expect(component.form.value).toEqual(siteEntryModel); @@ -208,6 +241,13 @@ describe('LibraryMetadataFormComponent', () => { expect(component.form.value).toEqual(siteEntryModel); }); + it('should call markAsPristine on form when cancelled', () => { + spyOn(component.form, 'markAsPristine'); + + component.cancel(); + expect(component.form.markAsPristine).toHaveBeenCalled(); + }); + it('should warn if library name input is used by another library', fakeAsync(() => { const title = 'some-title'; spyOn(component['queriesApi'], 'findSites').and.returnValue( @@ -216,19 +256,6 @@ describe('LibraryMetadataFormComponent', () => { } as SitePaging) ); - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - - component.node = { - entry: { - id: 'libraryId', - ...siteEntryModel - } as Site - }; - fixture.detectChanges(); component.form.controls.title.setValue(title); fixture.detectChanges(); @@ -244,19 +271,6 @@ describe('LibraryMetadataFormComponent', () => { } as SitePaging) ); - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - - component.node = { - entry: { - id: 'libraryId', - ...siteEntryModel - } as Site - }; - fixture.detectChanges(); component.form.controls.title.setValue('libraryTitle'); fixture.detectChanges(); @@ -272,19 +286,6 @@ describe('LibraryMetadataFormComponent', () => { } as SitePaging) ); - const siteEntryModel = { - title: 'libraryTitle', - description: 'description', - visibility: 'PRIVATE' - }; - - component.node = { - entry: { - id: 'libraryId', - ...siteEntryModel - } as Site - }; - fixture.detectChanges(); component.form.controls.title.setValue('some-name'); fixture.detectChanges(); diff --git a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts index b04cc60cd8..0f14836eae 100644 --- a/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts +++ b/projects/aca-content/src/lib/components/info-drawer/library-metadata-tab/library-metadata-form.component.ts @@ -26,8 +26,15 @@ import { Component, Input, OnChanges, OnDestroy, OnInit, ViewEncapsulation } fro import { UntypedFormGroup, UntypedFormControl, Validators, FormGroupDirective, NgForm, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { QueriesApi, SiteEntry, SitePaging } from '@alfresco/js-api'; import { Store } from '@ngrx/store'; -import { AppStore, UpdateLibraryAction } from '@alfresco/aca-shared/store'; -import { debounceTime, mergeMap, takeUntil } from 'rxjs/operators'; +import { + AppStore, + SnackbarAction, + SnackbarActionTypes, + SnackbarErrorAction, + SnackbarInfoAction, + UpdateLibraryAction +} from '@alfresco/aca-shared/store'; +import { debounceTime, filter, mergeMap, takeUntil } from 'rxjs/operators'; import { AlfrescoApiService } from '@alfresco/adf-core'; import { Observable, from, Subject } from 'rxjs'; import { ErrorStateMatcher, MatOptionModule } from '@angular/material/core'; @@ -39,6 +46,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatInputModule } from '@angular/material/input'; import { A11yModule } from '@angular/cdk/a11y'; import { MatButtonModule } from '@angular/material/button'; +import { Actions, ofType } from '@ngrx/effects'; export class InstantErrorStateMatcher implements ErrorStateMatcher { isErrorState(control: UntypedFormControl | null, form: FormGroupDirective | NgForm | null): boolean { @@ -99,7 +107,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro onDestroy$: Subject = new Subject(); - constructor(private alfrescoApiService: AlfrescoApiService, protected store: Store) {} + constructor(private alfrescoApiService: AlfrescoApiService, protected store: Store, private actions$: Actions) {} getVisibilityLabel(value: string) { return this.libraryType.find((type) => type.value === value).label; } @@ -111,6 +119,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro cancel() { this.updateForm(this.node); this.toggleEdit(); + this.form.markAsPristine(); } ngOnInit() { @@ -137,6 +146,10 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro }); this.canUpdateLibrary = this.node?.entry?.role === 'SiteManager'; this.visibilityLabel = this.libraryType.find((type) => type.value === this.form.controls['visibility'].value).label; + this.handleUpdatingEvent(SnackbarActionTypes.Info, 'LIBRARY.SUCCESS.LIBRARY_UPDATED', () => + Object.assign(this.node.entry, this.form.value) + ); + this.handleUpdatingEvent(SnackbarActionTypes.Error, 'LIBRARY.ERRORS.LIBRARY_UPDATE_ERROR', () => this.form.markAsDirty()); } ngOnDestroy() { @@ -150,6 +163,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro update() { if (this.canUpdateLibrary && this.form.valid) { + this.form.markAsPristine(); this.store.dispatch(new UpdateLibraryAction(this.form.value)); } } @@ -175,4 +189,14 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro .catch(() => ({ list: { entries: [] } })) ); } + + private handleUpdatingEvent(actionType: SnackbarActionTypes, payload: string, handle: () => void): void { + this.actions$ + .pipe( + ofType(actionType), + filter((action) => action.payload === payload), + takeUntil(this.onDestroy$) + ) + .subscribe(handle); + } }