Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACS-5949] library property update and cancel button do not reflect the change in UI #3461

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -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<LibraryMetadataFormComponent>;
let component: LibraryMetadataFormComponent;
let store: Store<any>;
let actions$: Subject<SnackbarAction>;
let siteEntryModel: SiteBodyCreate;

beforeEach(() => {
actions$ = new Subject<SnackbarAction>();
TestBed.configureTestingModule({
imports: [AppTestingModule, LibraryMetadataFormComponent],
providers: [
{
provide: Actions,
useValue: actions$
},
{
provide: Store,
useValue: {
Expand All @@ -53,45 +62,32 @@ 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: {
id: 'libraryId',
...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);
Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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;

Expand All @@ -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);
Expand All @@ -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(
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 {
Expand Down Expand Up @@ -99,7 +107,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro

onDestroy$: Subject<boolean> = new Subject<boolean>();

constructor(private alfrescoApiService: AlfrescoApiService, protected store: Store<AppStore>) {}
constructor(private alfrescoApiService: AlfrescoApiService, protected store: Store<AppStore>, private actions$: Actions) {}
getVisibilityLabel(value: string) {
return this.libraryType.find((type) => type.value === value).label;
}
Expand All @@ -111,6 +119,7 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
cancel() {
this.updateForm(this.node);
this.toggleEdit();
this.form.markAsPristine();
}

ngOnInit() {
Expand All @@ -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<SnackbarInfoAction>(SnackbarActionTypes.Info, 'LIBRARY.SUCCESS.LIBRARY_UPDATED', () =>
Object.assign(this.node.entry, this.form.value)
);
this.handleUpdatingEvent<SnackbarErrorAction>(SnackbarActionTypes.Error, 'LIBRARY.ERRORS.LIBRARY_UPDATE_ERROR', () => this.form.markAsDirty());
}

ngOnDestroy() {
Expand All @@ -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));
}
}
Expand All @@ -175,4 +189,14 @@ export class LibraryMetadataFormComponent implements OnInit, OnChanges, OnDestro
.catch(() => ({ list: { entries: [] } }))
);
}

private handleUpdatingEvent<T extends SnackbarAction>(actionType: SnackbarActionTypes, payload: string, handle: () => void): void {
this.actions$
.pipe(
ofType<T>(actionType),
filter((action) => action.payload === payload),
takeUntil(this.onDestroy$)
)
.subscribe(handle);
}
}
Loading