Added more tests

This commit is contained in:
Giuseppe Digilio
2018-06-26 16:41:11 +02:00
parent 5d377287ad
commit d49cba9090
4 changed files with 561 additions and 56 deletions

View File

@@ -9,6 +9,13 @@ describe('RowParser test suite', () => {
let row1: FormRowModel; let row1: FormRowModel;
let row2: FormRowModel; let row2: FormRowModel;
let row3: FormRowModel; let row3: FormRowModel;
let row4: FormRowModel;
let row5: FormRowModel;
let row6: FormRowModel;
let row7: FormRowModel;
let row8: FormRowModel;
let row9: FormRowModel;
let row10: FormRowModel;
const scopeUUID = 'testScopeUUID'; const scopeUUID = 'testScopeUUID';
const initFormValues = {}; const initFormValues = {};
@@ -77,9 +84,10 @@ describe('RowParser test suite', () => {
} }
], ],
languageCodes: [] languageCodes: []
} } as FormFieldModel
] ]
} as FormRowModel; } as FormRowModel;
row3 = { row3 = {
fields: [ fields: [
{ {
@@ -96,7 +104,7 @@ describe('RowParser test suite', () => {
} }
], ],
languageCodes: [] languageCodes: []
}, } as FormFieldModel,
{ {
input: {type: 'onebox'}, input: {type: 'onebox'},
label: 'Other title', label: 'Other title',
@@ -112,14 +120,248 @@ describe('RowParser test suite', () => {
} }
], ],
languageCodes: [] languageCodes: []
} } as FormFieldModel
] ]
} as FormRowModel; } as FormRowModel;
row4 = {
fields: [
{
input: {
type: 'dropdown'
},
label: 'Type',
mandatory: 'false',
repeatable: false,
hints: 'Select the tyupe.',
selectableMetadata: [
{
metadata: 'type',
authority: 'common_types_dataset',
closed: false
}
],
languageCodes: []
} as FormFieldModel,
{
input: {type: 'series'},
label: 'Series/Report No.',
mandatory: 'false',
repeatable: false,
hints: 'Enter the series and number assigned to this item by your community.',
selectableMetadata: [
{
metadata: 'series',
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
row5 = {
fields: [
{
input: {
type: 'lookup-name'
},
label: 'Author',
mandatory: 'false',
repeatable: false,
hints: 'Enter the name of the author.',
selectableMetadata: [
{
metadata: 'author',
authority: 'RPAuthority',
closed: false
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
row6 = {
fields: [
{
input: {
type: 'list'
},
label: 'Type',
mandatory: 'false',
repeatable: true,
hints: 'Select the type.',
selectableMetadata: [
{
metadata: 'type',
authority: 'type_programme',
closed: false
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
row7 = {
fields: [
{
input: {
type: 'date'
},
label: 'Date of Issue.',
mandatory: 'true',
repeatable: false,
hints: 'Please give the date of previous publication or public distribution. You can leave out the day and/or month if they aren\'t applicable.',
mandatoryMessage: 'You must enter at least the year.',
selectableMetadata: [
{
metadata: 'date',
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
row8 = {
fields: [
{
input: {
type: 'tag'
},
label: 'Keywords',
mandatory: 'false',
repeatable: false,
hints: 'Local controlled vocabulary.',
selectableMetadata: [
{
metadata: 'subject',
authority: 'JOURNALAuthority',
closed: false
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
row9 = {
fields: [
{
input: {
type: 'textarea'
},
label: 'Description',
mandatory: 'false',
repeatable: false,
hints: 'Enter a description.',
selectableMetadata: [
{
metadata: 'description'
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
row10 = {
fields: [
{
input: {
type: 'group'
},
rows: [
{
fields: [
{
input: {
type: 'onebox'
},
label: 'Author',
mandatory: 'false',
repeatable: false,
hints: 'Enter the name of the author.',
selectableMetadata: [
{
metadata: 'author'
}
],
languageCodes: []
},
{
input: {
type: 'onebox'
},
label: 'Affiliation',
mandatory: false,
repeatable: true,
hints: 'Enter the affiliation of the author.',
selectableMetadata: [
{
metadata: 'affiliation'
}
],
languageCodes: []
}
]
}
],
label: 'Authors',
mandatory: 'true',
repeatable: false,
mandatoryMessage: 'Entering at least the first author is mandatory.',
hints: 'Enter the names of the authors of this item.',
selectableMetadata: [
{
metadata: 'author'
}
],
languageCodes: []
} as FormFieldModel
]
} as FormRowModel;
}); });
it('should init parser properly', () => { it('should init parser properly', () => {
const parser = new RowParser(row1, scopeUUID, initFormValues, submissionScope, readOnly); let parser = new RowParser(row1, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row2, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row3, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row4, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row5, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row6, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row7, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row8, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row9, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true);
parser = new RowParser(row10, scopeUUID, initFormValues, submissionScope, readOnly);
expect(parser instanceof RowParser).toBe(true); expect(parser instanceof RowParser).toBe(true);
}); });

View File

@@ -1,13 +1,16 @@
// Load the implementations that should be tested
import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, inject, TestBed, } from '@angular/core/testing'; import { async, ComponentFixture, inject, TestBed, } from '@angular/core/testing';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of'; import 'rxjs/add/observable/of';
import { DynamicFormControlModel, DynamicFormValidationService, DynamicInputModel } from '@ng-dynamic-forms/core'; import {
DynamicFormArrayModel,
DynamicFormControlEvent,
DynamicFormControlModel,
DynamicFormValidationService,
DynamicInputModel
} from '@ng-dynamic-forms/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
@@ -16,6 +19,9 @@ import { FormComponent } from './form.component';
import { FormService } from './form.service'; import { FormService } from './form.service';
import { FormBuilderService } from './builder/form-builder.service'; import { FormBuilderService } from './builder/form-builder.service';
import { FormState } from './form.reducers'; import { FormState } from './form.reducers';
import { FormChangeAction, FormStatusChangeAction } from './form.actions';
import { MockStore } from '../testing/mock-store';
import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model';
function createTestComponent<T>(html: string, type: { new(...args: any[]): T }): ComponentFixture<T> { function createTestComponent<T>(html: string, type: { new(...args: any[]): T }): ComponentFixture<T> {
TestBed.overrideComponent(type, { TestBed.overrideComponent(type, {
@@ -76,33 +82,47 @@ export const TEST_FORM_MODEL = [
), ),
]; ];
export const TEST_FORM_GROUP = { export const TEST_FORM_MODEL_WITH_ARRAY = [
dc_title: new FormControl(), new DynamicFormArrayModel({
dc_title_alternative: new FormControl(),
dc_publisher: new FormControl(),
dc_identifier_citation: new FormControl(),
dc_identifier_issn: new FormControl()
}
describe('Form component', () => { id: 'bootstrapFormArray',
initialCount: 1,
label: 'Form Array',
groupFactory: () => {
return [
new DynamicInputModel({
id: 'bootstrapArrayGroupInput',
placeholder: 'example array group input',
readOnly: false
})
];
}
})
];
describe('FormComponent test suite', () => {
let testComp: TestComponent; let testComp: TestComponent;
let formComp: FormComponent;
let testFixture: ComponentFixture<TestComponent>; let testFixture: ComponentFixture<TestComponent>;
let formFixture: ComponentFixture<FormComponent>;
const formState: FormState = {
testForm: {
data: {
dc_title: null,
dc_title_alternative: null,
dc_publisher: null,
dc_identifier_citation: null,
dc_identifier_issn: null
},
valid: false,
errors: []
}
};
let html; let html;
const formServiceStub = {
getFormData: (formId) => Observable.of([])
}
const formBuilderServiceStub = {
createFormGroup: (formModel) => new FormGroup(TEST_FORM_GROUP)
}
const submissionFormsConfigServiceStub = {};
const store: Store<FormState> = jasmine.createSpyObj('store', { const store: MockStore<FormState> = new MockStore<FormState>(formState);
/* tslint:disable:no-empty */
dispatch: {},
/* tslint:enable:no-empty */
select: Observable.of({})
});
// async beforeEach // async beforeEach
beforeEach(async(() => { beforeEach(async(() => {
@@ -135,23 +155,265 @@ describe('Form component', () => {
})); }));
// synchronous beforeEach describe('', () => {
beforeEach(() => { // synchronous beforeEach
html = ` beforeEach(() => {
<ds-form *ngIf="formModel" #formRef="formComponent" html = `
[formId]="formId" <ds-form *ngIf="formModel" #formRef="formComponent"
[formModel]="formModel" [formId]="formId"
[displaySubmit]="displaySubmit"></ds-form>`; [formModel]="formModel"
[displaySubmit]="displaySubmit"></ds-form>`;
testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>; testFixture = createTestComponent(html, TestComponent) as ComponentFixture<TestComponent>;
testComp = testFixture.componentInstance; testComp = testFixture.componentInstance;
});
it('should create FormComponent', inject([FormComponent], (app: FormComponent) => {
expect(app).toBeDefined();
}));
}); });
it('should create Form Component', inject([FormComponent], (app: FormComponent) => { describe('', () => {
beforeEach(() => {
expect(app).toBeDefined(); formFixture = TestBed.createComponent(FormComponent);
})); formComp = formFixture.componentInstance; // FormComponent test instance
formComp.formId = 'testForm';
formComp.formModel = TEST_FORM_MODEL;
formComp.displaySubmit = false;
formFixture.detectChanges();
spyOn(store, 'dispatch');
});
afterEach(() => {
formFixture.destroy();
formComp = null;
});
it('should dispatch a FormStatusChangeAction when Form group status changes', () => {
const control = formComp.formGroup.get(['dc_title']);
control.setValue('Test Title');
expect(store.dispatch).toHaveBeenCalledWith(new FormStatusChangeAction('testForm', formComp.formGroup.valid));
});
it('should display form errors when errors are added to the state', () => {
const errors = [{
fieldId: 'dc_title',
message: 'error.validation.required'
}];
formState.testForm.errors = errors;
store.nextState(formState);
formFixture.detectChanges();
expect((formComp as any).formErrors).toEqual(errors);
});
it('should remove form errors when errors are empty in the state', () => {
(formComp as any).formErrors = [{
fieldId: 'dc_title',
message: 'error.validation.required'
}];
const errors = [];
formState.testForm.errors = errors;
store.nextState(formState);
formFixture.detectChanges();
expect((formComp as any).formErrors).toEqual(errors);
});
it('should dispatch FormChangeAction on form change', inject([FormBuilderService], (service: FormBuilderService) => {
const event = {
$event: new FormFieldMetadataValueObject('Test Title'),
context: null,
control: formComp.formGroup.get('dc_title'),
group: formComp.formGroup,
model: formComp.formModel[0],
type: 'change'
} as DynamicFormControlEvent;
spyOn(formComp.change, 'emit');
formComp.onChange(event);
expect(store.dispatch).toHaveBeenCalledWith(new FormChangeAction('testForm', service.getValueFromModel(formComp.formModel)));
expect(formComp.change.emit).toHaveBeenCalled();
}));
it('should emit change on form change', inject([FormBuilderService], (service: FormBuilderService) => {
const event = {
$event: new FormFieldMetadataValueObject('Test Title'),
context: null,
control: formComp.formGroup.get('dc_title'),
group: formComp.formGroup,
model: formComp.formModel[0],
type: 'change'
} as DynamicFormControlEvent;
spyOn(formComp.change, 'emit');
formComp.onChange(event);
expect(formComp.change.emit).toHaveBeenCalled();
}));
it('should not emit change Event on form change when emitChange is false', inject([FormBuilderService], (service: FormBuilderService) => {
const event = {
$event: new FormFieldMetadataValueObject('Test Title'),
context: null,
control: formComp.formGroup.get('dc_title'),
group: formComp.formGroup,
model: formComp.formModel[0],
type: 'change'
} as DynamicFormControlEvent;
formComp.emitChange = false;
spyOn(formComp.change, 'emit');
formComp.onChange(event);
expect(formComp.change.emit).not.toHaveBeenCalled();
}));
it('should emit blur Event on blur', () => {
const event = {
$event: new FocusEvent('blur'),
context: null,
control: formComp.formGroup.get('dc_title'),
group: formComp.formGroup,
model: formComp.formModel[0],
type: 'blur'
} as DynamicFormControlEvent;
spyOn(formComp.blur, 'emit');
formComp.onBlur(event);
expect(formComp.blur.emit).toHaveBeenCalled();
});
it('should emit focus Event on focus', () => {
const event = {
$event: new FocusEvent('focus'),
context: null,
control: formComp.formGroup.get('dc_title'),
group: formComp.formGroup,
model: formComp.formModel[0],
type: 'focus'
} as DynamicFormControlEvent;
spyOn(formComp.focus, 'emit');
formComp.onFocus(event);
expect(formComp.focus.emit).toHaveBeenCalled();
});
it('should return Observable of form status', () => {
const control = formComp.formGroup.get(['dc_title']);
control.setValue('Test Title');
formState.testForm.valid = true;
store.nextState(formState);
formFixture.detectChanges();
formComp.isValid().subscribe((valid) => {
expect(valid).toBe(true);
});
});
it('should emit submit Event on form submit whether the form is valid', () => {
const control = formComp.formGroup.get(['dc_title']);
control.setValue('Test Title');
formState.testForm.valid = true;
spyOn(formComp.submit, 'emit');
store.nextState(formState);
formFixture.detectChanges();
formComp.onSubmit();
expect(formComp.submit.emit).toHaveBeenCalled();
});
it('should not emit submit Event on form submit whether the form is not valid', () => {
spyOn((formComp as any).formService, 'validateAllFormFields');
store.nextState(formState);
formFixture.detectChanges();
formComp.onSubmit();
expect((formComp as any).formService.validateAllFormFields).toHaveBeenCalled();
});
it('should reset form group', () => {
spyOn(formComp.formGroup, 'reset');
formComp.reset();
expect(formComp.formGroup.reset).toHaveBeenCalled();
});
});
describe('', () => {
beforeEach(() => {
formFixture = TestBed.createComponent(FormComponent);
formComp = formFixture.componentInstance; // FormComponent test instance
formComp.formId = 'testFormArray';
formComp.formModel = TEST_FORM_MODEL_WITH_ARRAY;
formComp.displaySubmit = false;
formFixture.detectChanges();
spyOn(store, 'dispatch');
});
afterEach(() => {
formFixture.destroy();
formComp = null;
});
it('should return ReadOnly property from array item', inject([FormBuilderService], (service: FormBuilderService) => {
const readOnly = formComp.isItemReadOnly(formComp.formModel[0] as DynamicFormArrayModel, 0);
expect(readOnly).toBe(false);
}));
it('should dispatch FormChangeAction when an item has been added to an array', inject([FormBuilderService], (service: FormBuilderService) => {
formComp.insertItem(new Event('click'), formComp.formModel[0] as DynamicFormArrayModel, 1);
expect(store.dispatch).toHaveBeenCalledWith(new FormChangeAction('testFormArray', service.getValueFromModel(formComp.formModel)));
}));
it('should emit addArrayItem Event when an item has been added to an array', inject([FormBuilderService], (service: FormBuilderService) => {
spyOn(formComp.addArrayItem, 'emit');
formComp.insertItem(new Event('click'), formComp.formModel[0] as DynamicFormArrayModel, 1);
expect(formComp.addArrayItem.emit).toHaveBeenCalled();
}));
it('should dispatch FormChangeAction when an item has been removed from an array', inject([FormBuilderService], (service: FormBuilderService) => {
formComp.removeItem(new Event('click'), formComp.formModel[0] as DynamicFormArrayModel, 1);
expect(store.dispatch).toHaveBeenCalledWith(new FormChangeAction('testFormArray', service.getValueFromModel(formComp.formModel)));
}));
it('should emit removeArrayItem Event when an item has been removed from an array', inject([FormBuilderService], (service: FormBuilderService) => {
spyOn(formComp.removeArrayItem, 'emit');
formComp.removeItem(new Event('click'), formComp.formModel[0] as DynamicFormArrayModel, 1);
expect(formComp.removeArrayItem.emit).toHaveBeenCalled();
}));
})
}); });
// declare a test component // declare a test component

View File

@@ -21,7 +21,7 @@ import {
import { FormBuilderService } from './builder/form-builder.service'; import { FormBuilderService } from './builder/form-builder.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription'; import { Subscription } from 'rxjs/Subscription';
import { hasValue, isNotNull, isNull } from '../empty.util'; import { hasValue, isNotEmpty, isNotNull, isNull } from '../empty.util';
import { FormService } from './form.service'; import { FormService } from './form.service';
import { formObjectFromIdSelector } from './selectors'; import { formObjectFromIdSelector } from './selectors';
import { FormEntry, FormError } from './form.reducers'; import { FormEntry, FormError } from './form.reducers';
@@ -154,7 +154,7 @@ export class FormComponent implements OnDestroy, OnInit {
this.subs.push( this.subs.push(
this.store.select(formObjectFromIdSelector(this.formId)) this.store.select(formObjectFromIdSelector(this.formId))
.filter((formState: FormEntry) => !!formState && !isEmpty(formState.errors)) .filter((formState: FormEntry) => !!formState && (isNotEmpty(formState.errors) || isNotEmpty(this.formErrors)))
.map((formState) => formState.errors) .map((formState) => formState.errors)
.distinctUntilChanged() .distinctUntilChanged()
// .delay(100) // this terrible delay is here to prevent the detection change error // .delay(100) // this terrible delay is here to prevent the detection change error
@@ -223,7 +223,7 @@ export class FormComponent implements OnDestroy, OnInit {
/** /**
* Method to keep synchronized form controls values with form state * Method to keep synchronized form controls values with form state
*/ */
private keepSync() { private keepSync(): void {
this.subs.push(this.formService.getFormData(this.formId) this.subs.push(this.formService.getFormData(this.formId)
.subscribe((stateFormData) => { .subscribe((stateFormData) => {
if (!Object.is(stateFormData, this.formGroup.value) && this.formGroup) { if (!Object.is(stateFormData, this.formGroup.value) && this.formGroup) {
@@ -232,15 +232,15 @@ export class FormComponent implements OnDestroy, OnInit {
})); }));
} }
onBlur(event) { onBlur(event: DynamicFormControlEvent): void {
this.blur.emit(event); this.blur.emit(event);
} }
onFocus(event) { onFocus(event: DynamicFormControlEvent): void {
this.focus.emit(event); this.focus.emit(event);
} }
onChange(event) { onChange(event: DynamicFormControlEvent): void {
const action: FormChangeAction = new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel)); const action: FormChangeAction = new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel));
this.store.dispatch(action); this.store.dispatch(action);
@@ -260,7 +260,7 @@ export class FormComponent implements OnDestroy, OnInit {
* Method called on submit. * Method called on submit.
* Emit a new submit Event whether the form is valid, mark fields with error otherwise * Emit a new submit Event whether the form is valid, mark fields with error otherwise
*/ */
onSubmit() { onSubmit(): void {
if (this.getFormGroupValidStatus()) { if (this.getFormGroupValidStatus()) {
this.submit.emit(this.formService.getFormData(this.formId)); this.submit.emit(this.formService.getFormData(this.formId));
} else { } else {
@@ -271,7 +271,7 @@ export class FormComponent implements OnDestroy, OnInit {
/** /**
* Method to reset form fields * Method to reset form fields
*/ */
reset() { reset(): void {
this.formGroup.reset(); this.formGroup.reset();
} }
@@ -281,14 +281,14 @@ export class FormComponent implements OnDestroy, OnInit {
return model.readOnly; return model.readOnly;
} }
removeItem($event, arrayContext: DynamicFormArrayModel, index: number) { removeItem($event, arrayContext: DynamicFormArrayModel, index: number): void {
const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray;
this.removeArrayItem.emit(this.getEvent($event, arrayContext, index, 'remove')); this.removeArrayItem.emit(this.getEvent($event, arrayContext, index, 'remove'));
this.formBuilderService.removeFormArrayGroup(index, formArrayControl, arrayContext); this.formBuilderService.removeFormArrayGroup(index, formArrayControl, arrayContext);
this.store.dispatch(new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel))); this.store.dispatch(new FormChangeAction(this.formId, this.formBuilderService.getValueFromModel(this.formModel)));
} }
insertItem($event, arrayContext: DynamicFormArrayModel, index: number) { insertItem($event, arrayContext: DynamicFormArrayModel, index: number): void {
const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray; const formArrayControl = this.formGroup.get(this.formBuilderService.getPath(arrayContext)) as FormArray;
this.formBuilderService.insertFormArrayGroup(index, formArrayControl, arrayContext); this.formBuilderService.insertFormArrayGroup(index, formArrayControl, arrayContext);
this.addArrayItem.emit(this.getEvent($event, arrayContext, index, 'add')); this.addArrayItem.emit(this.getEvent($event, arrayContext, index, 'add'));

View File

@@ -9,12 +9,13 @@ export class MockStore<T> extends BehaviorSubject<T> {
} }
dispatch = (action: Action): void => { dispatch = (action: Action): void => {
console.info(); // console.info(action);
} };
select = <R>(pathOrMapFn: any): Observable<T> => { select = <R>(pathOrMapFn: any): Observable<T> => {
return Observable.of(this.getValue()); return this.asObservable()
} .map((value) => pathOrMapFn.projector(value))
};
nextState(_newState: T) { nextState(_newState: T) {
this.next(_newState); this.next(_newState);