diff --git a/src/app/main/dialog/dialog.component.html b/src/app/main/dialog/dialog.component.html index 3ab7821379..b179b65630 100644 --- a/src/app/main/dialog/dialog.component.html +++ b/src/app/main/dialog/dialog.component.html @@ -140,14 +140,14 @@
- - + + +
- +
@@ -156,7 +156,7 @@
- +
diff --git a/src/app/project/list/list-info-form/list-info-form.component.html b/src/app/project/list/list-info-form/list-info-form.component.html index b723232e89..9146368562 100644 --- a/src/app/project/list/list-info-form/list-info-form.component.html +++ b/src/app/project/list/list-info-form/list-info-form.component.html @@ -1,19 +1,13 @@
- - - - -
+
- + {{ labelInvalidMessage }}

@@ -31,17 +25,10 @@
- -
- - -
-
diff --git a/src/app/project/list/list-info-form/list-info-form.component.spec.ts b/src/app/project/list/list-info-form/list-info-form.component.spec.ts index bafcdf8b18..d716dd9464 100644 --- a/src/app/project/list/list-info-form/list-info-form.component.spec.ts +++ b/src/app/project/list/list-info-form/list-info-form.component.spec.ts @@ -1,37 +1,88 @@ +import { HarnessLoader } from '@angular/cdk/testing'; +import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { HttpClientModule } from '@angular/common/http'; +import { Component, ViewChild } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; +import { MatButtonHarness } from '@angular/material/button/testing'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; -import { KnoraApiConnection } from '@dasch-swiss/dsp-js'; +import { ApiResponseData, CreateListRequest, ListInfoResponse, ListResponse, ListsEndpointAdmin, UpdateListInfoRequest } from '@dasch-swiss/dsp-js'; import { - AppInitService, DspActionModule, - DspApiConfigToken, DspApiConnectionToken, DspCoreModule } from '@dasch-swiss/dsp-ui'; import { TranslateModule } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { AjaxResponse } from 'rxjs/ajax'; +import { DialogHeaderComponent } from 'src/app/main/dialog/dialog-header/dialog-header.component'; import { DialogComponent } from 'src/app/main/dialog/dialog.component'; import { ErrorComponent } from 'src/app/main/error/error.component'; -import { TestConfig } from 'test.config'; -import { ListItemFormComponent } from '../list-item-form/list-item-form.component'; -import { ListItemComponent } from '../list-item/list-item.component'; import { ListInfoFormComponent } from './list-info-form.component'; +/** + * test host component to simulate parent component for creating a new list. + */ +@Component({ + template: '' +}) +class TestHostUpdateListComponent { + + @ViewChild('listInfoForm') listInfoForm: ListInfoFormComponent; + + iri = 'http://rdfh.ch/lists/0001/otherTreeList'; + + mode = 'update'; + + projectIri = 'http://rdfh.ch/projects/0001'; + + constructor() {} +} + +/** + * test host component to simulate parent component for creating a new list. + */ +@Component({ + template: '' +}) +class TestHostCreateListComponent { + + @ViewChild('listInfoForm') listInfoForm: ListInfoFormComponent; + + mode = 'create'; + + projectcode = '0001'; + + projectIri = 'http://rdfh.ch/projects/0001'; + + constructor() {} +} + describe('ListInfoFormComponent', () => { - let component: ListInfoFormComponent; - let fixture: ComponentFixture; + let testHostUpdateListComponent: TestHostUpdateListComponent; + let testHostUpdateListFixture: ComponentFixture; + let testHostCreateListComponent: TestHostCreateListComponent; + let testHostCreateListFixture: ComponentFixture; + let rootLoader: HarnessLoader; + + const listsEndpointSpyObj = { + admin: { + listsEndpoint: jasmine.createSpyObj('listsEndpoint', ['getListInfo', 'updateListInfo', 'createList']) + } + }; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ + TestHostUpdateListComponent, + TestHostCreateListComponent, ListInfoFormComponent, - ListItemComponent, - ListItemFormComponent, DialogComponent, + DialogHeaderComponent, ErrorComponent ], imports: [ @@ -46,27 +97,168 @@ describe('ListInfoFormComponent', () => { TranslateModule.forRoot() ], providers: [ - AppInitService, { - provide: DspApiConfigToken, - useValue: TestConfig.ApiConfig + provide: DspApiConnectionToken, + useValue: listsEndpointSpyObj }, { - provide: DspApiConnectionToken, - useValue: new KnoraApiConnection(TestConfig.ApiConfig) + provide: MAT_DIALOG_DATA, + useValue: {} + }, + { + provide: MatDialogRef, + useValue: {} } ] }) .compileComponents(); })); - beforeEach(() => { - fixture = TestBed.createComponent(ListInfoFormComponent); - component = fixture.componentInstance; - fixture.detectChanges(); + describe('update existing list info', () => { + beforeEach(() => { + + const listsEndpointSpy = TestBed.inject(DspApiConnectionToken); + + (listsEndpointSpy.admin.listsEndpoint as jasmine.SpyObj).getListInfo.and.callFake( + () => { + const response = new ListInfoResponse(); + response.listinfo.id = 'http://rdfh.ch/lists/0001/otherTreeList'; + response.listinfo.labels = [{ 'value': 'Other Tree List', 'language': 'en' }]; + response.listinfo.comments = [{ 'value': 'Other Tree List comment', 'language': 'en' }]; + return of(ApiResponseData.fromAjaxResponse({ response } as AjaxResponse)); + } + ); + + testHostUpdateListFixture = TestBed.createComponent(TestHostUpdateListComponent); + testHostUpdateListComponent = testHostUpdateListFixture.componentInstance; + testHostUpdateListFixture.detectChanges(); + expect(testHostUpdateListComponent).toBeTruthy(); + + rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostUpdateListFixture); + }); + + it('should instantiate arrays for labels and comments', () => { + expect(testHostUpdateListComponent.listInfoForm.labels).toEqual([{ 'value': 'Other Tree List', 'language': 'en' }]); + expect(testHostUpdateListComponent.listInfoForm.comments).toEqual([{ 'value': 'Other Tree List comment', 'language': 'en' }]); + }); + + it('should display "Update" as the submit button text and be disabled as long as no labels are provided', async () => { + const submitButton = await rootLoader.getHarness(MatButtonHarness.with({ selector: '.list-submit' })); + + expect(await submitButton.getText()).toEqual('Update'); + + expect(await submitButton.isDisabled()).toBeFalsy(); + + testHostUpdateListComponent.listInfoForm.handleData([], 'labels'); + + expect(await submitButton.isDisabled()).toBeTruthy(); + + testHostUpdateListComponent.listInfoForm.handleData([{ 'value': 'My edited list label', 'language': 'en' }], 'labels'); + + expect(await submitButton.isDisabled()).toBeFalsy(); + }); + + it('should update labels when the value changes', () => { + testHostUpdateListComponent.listInfoForm.handleData([{ 'value': 'My edited list label', 'language': 'en' }], 'labels'); + expect(testHostUpdateListComponent.listInfoForm.labels).toEqual([{ 'value': 'My edited list label', 'language': 'en' }]); + }); + + it('should update comments when the value changes', () => { + testHostUpdateListComponent.listInfoForm.handleData([{ 'value': 'My edited list comment', 'language': 'en' }], 'comments'); + expect(testHostUpdateListComponent.listInfoForm.comments).toEqual([{ 'value': 'My edited list comment', 'language': 'en' }]); + }); + + it('should update the list info', () => { + const listsEndpointSpy = TestBed.inject(DspApiConnectionToken); + + testHostUpdateListComponent.listInfoForm.handleData([{ 'value': 'My edited list label', 'language': 'en' }], 'labels'); + testHostUpdateListComponent.listInfoForm.handleData([{ 'value': 'My edited list comment', 'language': 'en' }], 'comments'); + + const updateListInfoRequest: UpdateListInfoRequest = new UpdateListInfoRequest(); + updateListInfoRequest.listIri = testHostUpdateListComponent.listInfoForm.iri; + updateListInfoRequest.projectIri = testHostUpdateListComponent.listInfoForm.projectIri; + updateListInfoRequest.labels = testHostUpdateListComponent.listInfoForm.labels; + updateListInfoRequest.comments = testHostUpdateListComponent.listInfoForm.comments; + + (listsEndpointSpy.admin.listsEndpoint as jasmine.SpyObj).updateListInfo.and.callFake( + () => { + const response = new ListInfoResponse(); + response.listinfo.labels = [{ 'value': 'My edited list label', 'language': 'en' }]; + response.listinfo.comments = [{ 'value': 'My edited list comment', 'language': 'en' }]; + + expect(updateListInfoRequest.labels).toEqual(response.listinfo.labels); + expect(updateListInfoRequest.comments).toEqual(response.listinfo.comments); + + return of(ApiResponseData.fromAjaxResponse({ response } as AjaxResponse)); + } + ); + + testHostUpdateListComponent.listInfoForm.submitData(); + expect(listsEndpointSpy.admin.listsEndpoint.updateListInfo).toHaveBeenCalledTimes(1); + expect(listsEndpointSpy.admin.listsEndpoint.updateListInfo).toHaveBeenCalledWith(updateListInfoRequest); + }); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('create new list', () => { + beforeEach(() => { + testHostCreateListFixture = TestBed.createComponent(TestHostCreateListComponent); + testHostCreateListComponent = testHostCreateListFixture.componentInstance; + testHostCreateListFixture.detectChanges(); + expect(testHostCreateListComponent).toBeTruthy(); + + rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostCreateListFixture); + }); + + it('should instantiate empty arrays for labels and comments', () => { + expect(testHostCreateListComponent.listInfoForm.labels).toEqual([]); + expect(testHostCreateListComponent.listInfoForm.comments).toEqual([]); + }); + + it('should display "Create" as the submit button text and be disabled as long as no labels are provided', async () => { + const submitButton = await rootLoader.getHarness(MatButtonHarness.with({ selector: '.list-submit' })); + + expect(await submitButton.getText()).toEqual('Create'); + + expect(await submitButton.isDisabled()).toBeTruthy(); + + testHostCreateListComponent.listInfoForm.handleData([{ 'value': 'My new list', 'language': 'en' }], 'labels'); + + expect(await submitButton.isDisabled()).toBeFalsy(); + + testHostCreateListComponent.listInfoForm.handleData([], 'labels'); + + expect(await submitButton.isDisabled()).toBeTruthy(); + }); + + it('should create a new list', () => { + const listsEndpointSpy = TestBed.inject(DspApiConnectionToken); + + testHostCreateListComponent.listInfoForm.handleData([{ 'value': 'My new list', 'language': 'en' }], 'labels'); + testHostCreateListComponent.listInfoForm.handleData([{ 'value': 'My new list comment', 'language': 'en' }], 'comments'); + + const createListRequest: CreateListRequest = new CreateListRequest(); + createListRequest.projectIri = testHostCreateListComponent.listInfoForm.projectIri; + createListRequest.labels = testHostCreateListComponent.listInfoForm.labels; + createListRequest.comments = testHostCreateListComponent.listInfoForm.comments; + + (listsEndpointSpy.admin.listsEndpoint as jasmine.SpyObj).createList.and.callFake( + () => { + const response = new ListResponse(); + response.list.children = []; + response.list.listinfo.labels = [{ 'value': 'My new list', 'language': 'en' }]; + response.list.listinfo.comments = [{ 'value': 'My new list comment', 'language': 'en' }]; + + expect(createListRequest.labels).toEqual(response.list.listinfo.labels); + expect(createListRequest.comments).toEqual(response.list.listinfo.comments); + + return of(ApiResponseData.fromAjaxResponse({ response } as AjaxResponse)); + } + ); + + testHostCreateListComponent.listInfoForm.submitData(); + expect(listsEndpointSpy.admin.listsEndpoint.createList).toHaveBeenCalledTimes(1); + expect(listsEndpointSpy.admin.listsEndpoint.createList).toHaveBeenCalledWith(createListRequest); + }); }); + }); diff --git a/src/app/project/list/list-info-form/list-info-form.component.ts b/src/app/project/list/list-info-form/list-info-form.component.ts index b248665d5c..b2600efc5b 100644 --- a/src/app/project/list/list-info-form/list-info-form.component.ts +++ b/src/app/project/list/list-info-form/list-info-form.component.ts @@ -23,9 +23,10 @@ import { ErrorHandlerService } from 'src/app/main/error/error-handler.service'; }) export class ListInfoFormComponent implements OnInit { - @Input() iri?: string; + @Input() mode: 'create' | 'update'; + // project short code @Input() projectcode: string; @@ -33,8 +34,6 @@ export class ListInfoFormComponent implements OnInit { @Output() closeDialog: EventEmitter = new EventEmitter(); - @Output() updateParent: EventEmitter<{ title: string }> = new EventEmitter<{ title: string }>(); - loading: boolean; project: ReadProject; @@ -44,51 +43,16 @@ export class ListInfoFormComponent implements OnInit { labels: StringLiteral[]; comments: StringLiteral[]; - /** - * by adding new list, it starts with the list info and the next section is "creating the list"; - * true after adding list - * - */ - createList = false; - newList: List; - - nameMinLength = 3; - nameMaxLength = 16; - - /** - * form group for the form controller - */ - form: FormGroup; - - /** - * error checking on the following fields - */ - formErrors = { - label: '' - }; - - /** - * error hints - */ - validationMessages = { + // possible errors for the label + labelErrors = { label: { - 'required': 'Name is required.', - 'minlength': 'Name must be at least ' + this.nameMinLength + ' characters long.', - 'maxlength': 'Name cannot be more than ' + this.nameMaxLength + ' characters long.' + 'required': 'A label is required.' } }; - /** - * success of sending data - */ - success = false; - /** - * message after successful post - */ - successMessage: any = { - status: 200, - statusText: "You have successfully updated list's info." - }; + saveButtonDisabled = false; + + labelInvalidMessage: string; constructor( @Inject(DspApiConnectionToken) private _dspApiConnection: KnoraApiConnection, @@ -99,13 +63,13 @@ export class ListInfoFormComponent implements OnInit { this.loading = true; - // get list info in case of edit mode: this.iri is not undefined - if (this.iri) { + // get list info in case of edit mode + if (this.mode === 'update') { // edit mode, get list this._dspApiConnection.admin.listsEndpoint.getListInfo(this.iri).subscribe( (response: ApiResponseData) => { this.list = response.body.listinfo; - this.buildForm(response.body.listinfo); + this.buildLists(response.body.listinfo); }, (error: ApiResponseError) => { this._errorHandler.showMessage(error); @@ -114,11 +78,11 @@ export class ListInfoFormComponent implements OnInit { } else { // build the form - this.buildForm(); + this.buildLists(); } } - buildForm(list?: ListNodeInfo): void { + buildLists(list?: ListNodeInfo): void { this.loading = true; this.labels = []; @@ -129,47 +93,28 @@ export class ListInfoFormComponent implements OnInit { this.comments = list.comments; } - setTimeout(() => { - this.loading = false; - }); + this.loading = false; } submitData(): void { this.loading = true; - if (this.iri) { + if (this.mode === 'update') { // edit mode: update list info const listInfoUpdateData: UpdateListInfoRequest = new UpdateListInfoRequest(); listInfoUpdateData.projectIri = this.projectIri; listInfoUpdateData.listIri = this.iri; - - // initialize labels - let i = 0; - for (const l of this.labels) { - listInfoUpdateData.labels[i] = new StringLiteral(); - listInfoUpdateData.labels[i].language = l.language; - listInfoUpdateData.labels[i].value = l.value; - i++; - } - // initialize comments - let j = 0; - for (const c of this.comments) { - listInfoUpdateData.comments[j] = new StringLiteral(); - listInfoUpdateData.comments[j].language = c.language; - listInfoUpdateData.comments[j].value = c.value; - j++; - } + listInfoUpdateData.labels = this.labels; + listInfoUpdateData.comments = this.comments; this._dspApiConnection.admin.listsEndpoint.updateListInfo(listInfoUpdateData).subscribe( (response: ApiResponseData) => { - this.success = true; this.loading = false; this.closeDialog.emit(response.body.listinfo); }, (error: ApiResponseError) => { this._errorHandler.showMessage(error); this.loading = false; - this.success = false; } ); @@ -177,36 +122,17 @@ export class ListInfoFormComponent implements OnInit { // new: create list const listInfoData: CreateListRequest = new CreateListRequest(); listInfoData.projectIri = this.projectIri; - - // initialize labels - let i = 0; - for (const l of this.labels) { - listInfoData.labels[i] = new StringLiteral(); - listInfoData.labels[i].language = l.language; - listInfoData.labels[i].value = l.value; - i++; - } - // initialize comments - let j = 0; - for (const c of this.comments) { - listInfoData.comments[j] = new StringLiteral(); - listInfoData.comments[j].language = c.language; - listInfoData.comments[j].value = c.value; - j++; - } + listInfoData.labels = this.labels; + listInfoData.comments = this.comments; this._dspApiConnection.admin.listsEndpoint.createList(listInfoData).subscribe( (response: ApiResponseData) => { - this.newList = response.body.list; - - this.updateParent.emit({ title: response.body.list.listinfo.labels[0].value + ' (' + response.body.list.listinfo.labels[0].language + ')' }); + this.closeDialog.emit(response.body.list); this.loading = false; - this.createList = true; }, (error: ApiResponseError) => { this._errorHandler.showMessage(error); this.loading = false; - this.success = false; } ); } @@ -215,13 +141,13 @@ export class ListInfoFormComponent implements OnInit { /** * reset the form */ - resetForm(ev: Event, list?: ListNodeInfo) { + resetLists(ev: Event, list?: ListNodeInfo) { ev.preventDefault(); list = list ? list : new ListNodeInfo(); - this.buildForm(list); + this.buildLists(list); } handleData(data: StringLiteral[], type: string) { @@ -235,5 +161,15 @@ export class ListInfoFormComponent implements OnInit { this.comments = data; break; } + + if (this.labels.length === 0) { + // invalid label, don't let user submit + this.saveButtonDisabled = true; + this.labelInvalidMessage = this.labelErrors.label.required; + } else { + this.saveButtonDisabled = false; + this.labelInvalidMessage = null; + } } + } diff --git a/src/app/project/list/list-item-form/edit-list-item/edit-list-item.component.html b/src/app/project/list/list-item-form/edit-list-item/edit-list-item.component.html index 88deb051b1..248fdbd988 100644 --- a/src/app/project/list/list-item-form/edit-list-item/edit-list-item.component.html +++ b/src/app/project/list/list-item-form/edit-list-item/edit-list-item.component.html @@ -37,8 +37,8 @@ type="submit" color="primary" [disabled]="saveButtonDisabled" - (click)=" mode === 'editListNode' ? updateChildNode() : insertChildNode()"> - {{ mode === 'editListNode' ? 'appLabels.form.action.update' : 'appLabels.form.action.submit' | translate }} + (click)=" mode === 'update' ? updateChildNode() : insertChildNode()"> + {{ mode === 'update' ? ('appLabels.form.action.update' | translate) : ('appLabels.form.action.submit' | translate) }} diff --git a/src/app/project/list/list-item-form/list-item-form.component.html b/src/app/project/list/list-item-form/list-item-form.component.html index 04f6bdaa95..c590cb40ca 100644 --- a/src/app/project/list/list-item-form/list-item-form.component.html +++ b/src/app/project/list/list-item-form/list-item-form.component.html @@ -3,7 +3,7 @@ -