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

refactor(list-editor): new list form refactor (DSP-1392) #403

Merged
merged 11 commits into from Mar 5, 2021
8 changes: 4 additions & 4 deletions src/app/main/dialog/dialog.component.html
Expand Up @@ -140,9 +140,9 @@

<!-- Create new list -->
<div *ngSwitchCase="'createList'">
<app-dialog-header [title]="data.title" [subtitle]="'Create new'"></app-dialog-header>
<app-list-info-form [projectcode]="data.project" [projectIri]="data.id" (closeDialog)="dialogRef.close()"
(updateParent)="replaceTitle($event)"></app-list-info-form>
<app-dialog-header [title]="'Create new list'" [subtitle]=""></app-dialog-header>
<app-list-info-form [projectcode]="data.project" [mode]="'create'" [projectIri]="data.id" (closeDialog)="dialogRef.close($event)">
</app-list-info-form>
</div>

<!-- Insert list child node -->
Expand All @@ -156,7 +156,7 @@
<div *ngSwitchCase="'editListInfo'">
<app-dialog-header [title]="data.title" [subtitle]="'Edit list info'">
</app-dialog-header>
<app-list-info-form [iri]="data.id" [projectIri]="data.project" (closeDialog)="dialogRef.close()">
<app-list-info-form [iri]="data.id" [mode]="'update'" [projectIri]="data.project" (closeDialog)="dialogRef.close()">
</app-list-info-form>
</div>

Expand Down
21 changes: 4 additions & 17 deletions src/app/project/list/list-info-form/list-info-form.component.html
@@ -1,19 +1,13 @@
<div>

<dsp-progress-indicator *ngIf="loading"></dsp-progress-indicator>

<!-- success message after updating -->
<dsp-message *ngIf="success" [message]="successMessage" [short]="true"></dsp-message>

<div *ngIf="!loading && !createList" class="form-content list-info">
<div *ngIf="!loading" class="form-content list-info">

<!-- list label -->
<dsp-string-literal-input [placeholder]="'List label'" [value]="labels"
(dataChanged)="handleData($event, 'labels')">
</dsp-string-literal-input>
<!-- TODO: we should support mat hint in the string literal input <mat-hint *ngIf="formErrors.label">
{{ formErrors.label }}
</mat-hint> -->
<mat-hint class="invalid-form" *ngIf="labelInvalidMessage">{{ labelInvalidMessage }}</mat-hint>

<br><br>

Expand All @@ -31,17 +25,10 @@
<span class="fill-remaining-space"></span>
<span>
<button mat-raised-button type="submit" color="primary" [disabled]="labels.length === 0"
(click)="submitData()">
{{ iri ? 'Update' : 'Next' }}
(click)="submitData()" class="list-submit">
{{ mode === 'update' ? 'Update' : 'Create' | translate }}
</button>
</span>
</div>
</div>

<div class="create-list-items" *ngIf="!loading && createList && newList">
<app-list-item [projectcode]="projectcode" [projectIri]="projectIri" [parentIri]="newList.listinfo.id"
[language]="labels[0].language">
</app-list-item>
</div>

</div>
234 changes: 213 additions & 21 deletions 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: '<app-list-info-form #listInfoForm [iri]="iri" [mode]="mode" [projectIri]="projectIri"></app-list-info-form>'
})
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: '<app-list-info-form #listInfoForm [mode]="mode" [projectcode]="projectcode" [projectIri]="projectIri"></app-list-info-form>'
})
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<ListInfoFormComponent>;
let testHostUpdateListComponent: TestHostUpdateListComponent;
let testHostUpdateListFixture: ComponentFixture<TestHostUpdateListComponent>;
let testHostCreateListComponent: TestHostCreateListComponent;
let testHostCreateListFixture: ComponentFixture<TestHostCreateListComponent>;
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: [
Expand All @@ -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<ListsEndpointAdmin>).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<ListsEndpointAdmin>).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<ListsEndpointAdmin>).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);
});
});

});