diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.spec.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.spec.ts index e69de29bb2..16dcce1cf9 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.spec.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.spec.ts @@ -0,0 +1,277 @@ +import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing'; +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement, SimpleChange } from '@angular/core'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TextMaskModule } from 'angular2-text-mask'; +import { + DynamicCheckboxGroupModel, + DynamicCheckboxModel, + DynamicColorPickerModel, + DynamicDatePickerModel, + DynamicEditorModel, + DynamicFileUploadModel, + DynamicFormArrayModel, + DynamicFormControlModel, + DynamicFormGroupModel, + DynamicFormsCoreModule, + DynamicFormService, + DynamicInputModel, + DynamicRadioGroupModel, + DynamicRatingModel, + DynamicSelectModel, + DynamicSliderModel, + DynamicSwitchModel, + DynamicTextAreaModel, + DynamicTimePickerModel +} from '@ng-dynamic-forms/core'; +import { DsDynamicFormControlComponent, NGBootstrapFormControlType } from './ds-dynamic-form-control.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '../../../shared.module'; +import { DynamicDsDatePickerModel } from './models/date-picker/date-picker.model'; +import { DynamicGroupModel } from './models/dynamic-group/dynamic-group.model'; +import { DynamicListCheckboxGroupModel } from './models/list/dynamic-list-checkbox-group.model'; +import { AuthorityOptions } from '../../../../core/integration/models/authority-options.model'; +import { DynamicListRadioGroupModel } from './models/list/dynamic-list-radio-group.model'; +import { DynamicLookupModel } from './models/lookup/dynamic-lookup.model'; +import { DynamicScrollableDropdownModel } from './models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; +import { DynamicTagModel } from './models/tag/dynamic-tag.model'; +import { DynamicTypeaheadModel } from './models/typeahead/dynamic-typeahead.model'; +import { DynamicComboboxModel } from './models/ds-dynamic-combobox.model'; + +describe('DsDynamicFormControlComponent test suite', () => { + + const authorityOptions: AuthorityOptions = { + closed: false, + metadata: 'list', + name: 'type_programme', + scope: 'c1c16450-d56f-41bc-bb81-27f1d1eb5c23' + }; + const formModel = [ + new DynamicCheckboxModel({id: 'checkbox'}), + new DynamicCheckboxGroupModel({id: 'checkboxGroup', group: []}), + new DynamicColorPickerModel({id: 'colorpicker'}), + new DynamicDatePickerModel({id: 'datepicker'}), + new DynamicEditorModel({id: 'editor'}), + new DynamicFileUploadModel({id: 'upload', url: ''}), + new DynamicFormArrayModel({id: 'formArray', groupFactory: () => []}), + new DynamicFormGroupModel({id: 'formGroup', group: []}), + new DynamicInputModel({id: 'input', maxLength: 51}), + new DynamicRadioGroupModel({id: 'radioGroup'}), + new DynamicRatingModel({id: 'rating'}), + new DynamicSelectModel({id: 'select', options: [{value: 'One'}, {value: 'Two'}], value: 'One'}), + new DynamicSliderModel({id: 'slider'}), + new DynamicSwitchModel({id: 'switch'}), + new DynamicTextAreaModel({id: 'textarea'}), + new DynamicTimePickerModel({id: 'timepicker'}), + new DynamicTypeaheadModel({id: 'typeahead'}), + new DynamicScrollableDropdownModel({id: 'scrollableDropdown', authorityOptions: authorityOptions}), + new DynamicTagModel({id: 'tag'}), + new DynamicListCheckboxGroupModel({id: 'checkboxList', authorityOptions: authorityOptions, repeatable: true}), + new DynamicListRadioGroupModel({id: 'radioList', authorityOptions: authorityOptions, repeatable: false}), + new DynamicGroupModel({ + id: 'relationGroup', + formConfiguration: [], + mandatoryField: '', + name: 'relationGroup', + relationFields: [], + scopeUUID: '', + submissionScope: '' + }), + new DynamicDsDatePickerModel({id: 'datepicker'}), + new DynamicLookupModel({id: 'lookup', separator: ','}), + new DynamicComboboxModel({id: 'combobox', readOnly: false}) + ]; + const testModel = formModel[8]; + let formGroup: FormGroup; + let fixture: ComponentFixture; + let component: DsDynamicFormControlComponent; + let debugElement: DebugElement; + let testElement: DebugElement; + + beforeEach(async(() => { + + TestBed.configureTestingModule({ + + imports: [ + FormsModule, + ReactiveFormsModule, + NgbModule.forRoot(), + DynamicFormsCoreModule.forRoot(), + SharedModule, + TranslateModule.forRoot(), + TextMaskModule, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }).compileComponents().then(() => { + + fixture = TestBed.createComponent(DsDynamicFormControlComponent); + + component = fixture.componentInstance; + debugElement = fixture.debugElement; + }); + })); + + beforeEach(inject([DynamicFormService], (service: DynamicFormService) => { + + formGroup = service.createFormGroup(formModel); + + component.group = formGroup; + component.model = testModel; + + component.ngOnChanges({ + + group: new SimpleChange(null, component.group, true), + model: new SimpleChange(null, component.model, true) + }); + + fixture.detectChanges(); + + testElement = debugElement.query(By.css(`input[id='${testModel.id}']`)); + })); + + it('should initialize correctly', () => { + + expect(component.context).toBeNull(); + expect(component.control instanceof FormControl).toBe(true); + expect(component.group instanceof FormGroup).toBe(true); + expect(component.model instanceof DynamicFormControlModel).toBe(true); + expect(component.hasErrorMessaging).toBe(false); + expect(component.asBootstrapFormGroup).toBe(true); + + expect(component.onControlValueChanges).toBeDefined(); + expect(component.onModelDisabledUpdates).toBeDefined(); + expect(component.onModelValueUpdates).toBeDefined(); + + expect(component.blur).toBeDefined(); + expect(component.change).toBeDefined(); + expect(component.focus).toBeDefined(); + + expect(component.onValueChange).toBeDefined(); + expect(component.onBlur).toBeDefined(); + expect(component.onFocus).toBeDefined(); + + expect(component.isValid).toBe(true); + expect(component.isInvalid).toBe(false); + expect(component.showErrorMessages).toBe(false); + + expect(component.type).toBe(NGBootstrapFormControlType.Input); + }); + + it('should have an input element', () => { + + expect(testElement instanceof DebugElement).toBe(true); + }); + + it('should listen to native blur events', () => { + + spyOn(component, 'onBlur'); + + testElement.triggerEventHandler('blur', null); + + expect(component.onBlur).toHaveBeenCalled(); + }); + + it('should listen to native focus events', () => { + + spyOn(component, 'onFocus'); + + testElement.triggerEventHandler('focus', null); + + expect(component.onFocus).toHaveBeenCalled(); + }); + + it('should listen to native change event', () => { + + spyOn(component, 'onValueChange'); + + testElement.triggerEventHandler('change', null); + + expect(component.onValueChange).toHaveBeenCalled(); + }); + + it('should update model value when control value changes', () => { + + spyOn(component, 'onControlValueChanges'); + + component.control.setValue('test'); + + expect(component.onControlValueChanges).toHaveBeenCalled(); + }); + + it('should update control value when model value changes', () => { + + spyOn(component, 'onModelValueUpdates'); + + (testModel as DynamicInputModel).valueUpdates.next('test'); + + expect(component.onModelValueUpdates).toHaveBeenCalled(); + }); + + it('should update control activation when model disabled property changes', () => { + + spyOn(component, 'onModelDisabledUpdates'); + + testModel.disabledUpdates.next(true); + + expect(component.onModelDisabledUpdates).toHaveBeenCalled(); + }); + + it('should determine correct form control type', () => { + + const testFn = DsDynamicFormControlComponent.getFormControlType; + + expect(testFn(formModel[0])).toEqual(NGBootstrapFormControlType.Checkbox); + + expect(testFn(formModel[1])).toEqual(NGBootstrapFormControlType.CheckboxGroup); + + expect(testFn(formModel[2])).toBeNull(); + + expect(testFn(formModel[3])).toEqual(NGBootstrapFormControlType.DatePicker); + + (formModel[3] as DynamicDatePickerModel).inline = true; + expect(testFn(formModel[3])).toEqual(NGBootstrapFormControlType.Calendar); + + expect(testFn(formModel[4])).toBeNull(); + + expect(testFn(formModel[5])).toBeNull(); + + expect(testFn(formModel[6])).toEqual(NGBootstrapFormControlType.Array); + + expect(testFn(formModel[7])).toEqual(NGBootstrapFormControlType.Group); + + expect(testFn(formModel[8])).toEqual(NGBootstrapFormControlType.Input); + + expect(testFn(formModel[9])).toEqual(NGBootstrapFormControlType.RadioGroup); + + expect(testFn(formModel[10])).toBeNull(); + + expect(testFn(formModel[11])).toEqual(NGBootstrapFormControlType.Select); + + expect(testFn(formModel[12])).toBeNull(); + + expect(testFn(formModel[13])).toBeNull(); + + expect(testFn(formModel[14])).toEqual(NGBootstrapFormControlType.TextArea); + + expect(testFn(formModel[15])).toEqual(NGBootstrapFormControlType.TimePicker); + + expect(testFn(formModel[16])).toEqual(NGBootstrapFormControlType.TypeAhead); + + expect(testFn(formModel[17])).toEqual(NGBootstrapFormControlType.ScrollableDropdown); + + expect(testFn(formModel[18])).toEqual(NGBootstrapFormControlType.Tag); + + expect(testFn(formModel[19])).toEqual(NGBootstrapFormControlType.List); + + expect(testFn(formModel[20])).toEqual(NGBootstrapFormControlType.List); + + expect(testFn(formModel[21])).toEqual(NGBootstrapFormControlType.Relation); + + expect(testFn(formModel[22])).toEqual(NGBootstrapFormControlType.Date); + + expect(testFn(formModel[23])).toEqual(NGBootstrapFormControlType.Lookup); + + expect(testFn(formModel[24])).toEqual(NGBootstrapFormControlType.Group); + }); +}); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts index a38c35eb9c..5bd8402702 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/ds-dynamic-form-control.component.ts @@ -55,10 +55,10 @@ export const enum NGBootstrapFormControlType { TimePicker = 11, // 'TIMEPICKER' TypeAhead = 12, // 'TYPEAHEAD' ScrollableDropdown = 13, // 'SCROLLABLE_DROPDOWN' - TypeTag = 14, // 'TYPETAG' + Tag = 14, // 'TAG' List = 15, // 'TYPELIST' - Relation = 16, // Dynamic Group - DsDatePicker = 17, // Ds Date Picker + Relation = 16, // 'RELATION' + Date = 17, // 'DATE' Lookup = 18, // LOOKUP } @@ -109,7 +109,6 @@ export class DsDynamicFormControlComponent extends DynamicFormControlComponent i return datepickerModel.inline ? NGBootstrapFormControlType.Calendar : NGBootstrapFormControlType.DatePicker; case DYNAMIC_FORM_CONTROL_TYPE_GROUP: - // return (model instanceof DynamicGroupModel) ? NGBootstrapFormControlType.DynamicGroup : NGBootstrapFormControlType.Group; return NGBootstrapFormControlType.Group; case DYNAMIC_FORM_CONTROL_TYPE_INPUT: @@ -134,13 +133,13 @@ export class DsDynamicFormControlComponent extends DynamicFormControlComponent i return NGBootstrapFormControlType.ScrollableDropdown; case DYNAMIC_FORM_CONTROL_TYPE_TAG: - return NGBootstrapFormControlType.TypeTag; + return NGBootstrapFormControlType.Tag; case DYNAMIC_FORM_CONTROL_TYPE_RELATION: return NGBootstrapFormControlType.Relation; case DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER: - return NGBootstrapFormControlType.DsDatePicker; + return NGBootstrapFormControlType.Date; case DYNAMIC_FORM_CONTROL_TYPE_LOOKUP: return NGBootstrapFormControlType.Lookup; diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.model.ts index 5713d813b7..a75a1d2f1a 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.model.ts @@ -2,7 +2,7 @@ import { DynamicDateControlModel, DynamicFormControlLayout, serializable } from import { DynamicDateControlModelConfig } from '@ng-dynamic-forms/core/src/model/dynamic-date-control.model'; import { Subject } from 'rxjs/Subject'; -export const DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER = 'DSDATEPICKER'; +export const DYNAMIC_FORM_CONTROL_TYPE_DSDATEPICKER = 'DATE'; /** * Dynamic Date Picker Model class diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-combobox.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-combobox.model.ts index b8306bd491..2f260ff50d 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-combobox.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/ds-dynamic-combobox.model.ts @@ -9,8 +9,8 @@ export const COMBOBOX_METADATA_SUFFIX = '_COMBO_METADATA'; export const COMBOBOX_VALUE_SUFFIX = '_COMBO_VALUE'; export interface DsDynamicComboboxModelConfig extends DynamicFormGroupModelConfig { - languageCodes: LanguageCode[]; - language: string; + languageCodes?: LanguageCode[]; + language?: string; readOnly: boolean; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts index a6bba88fc6..984e3b3880 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/list/dynamic-list-radio-group.model.ts @@ -9,7 +9,7 @@ import { hasValue } from '../../../../../empty.util'; export interface DynamicListModelConfig extends DynamicRadioGroupModelConfig { authorityOptions: AuthorityOptions; - groupLength: number; + groupLength?: number; repeatable: boolean; value?: any; } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.model.ts index 21b11d0bf8..1d9f7ed9e9 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.model.ts @@ -6,8 +6,8 @@ import { AuthorityOptions } from '../../../../../../core/integration/models/auth export const DYNAMIC_FORM_CONTROL_TYPE_LOOKUP = 'LOOKUP'; export interface DynamicLookupModelConfig extends DsDynamicInputModelConfig { - maxOptions: number; - value: any; + maxOptions?: number; + value?: any; separator: string; } @@ -26,7 +26,7 @@ export class DynamicLookupModel extends DsDynamicInputModel { super(config, layout); this.autoComplete = AUTOCOMPLETE_OFF; - this.maxOptions = config.maxOptions; + this.maxOptions = config.maxOptions || 10; this.separator = config.separator; // Defined only for lookup-name this.valueUpdates.next(config.value); diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts index 69cdb475e2..8ab497ec6e 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model.ts @@ -6,8 +6,8 @@ export const DYNAMIC_FORM_CONTROL_TYPE_SCROLLABLE_DROPDOWN = 'SCROLLABLE_DROPDOW export interface DynamicScrollableDropdownModelConfig extends DsDynamicInputModelConfig { authorityOptions: AuthorityOptions; - maxOptions: number; - value: any; + maxOptions?: number; + value?: any; } export class DynamicScrollableDropdownModel extends DsDynamicInputModel { @@ -21,7 +21,7 @@ export class DynamicScrollableDropdownModel extends DsDynamicInputModel { this.autoComplete = AUTOCOMPLETE_OFF; this.authorityOptions = config.authorityOptions; - this.maxOptions = config.maxOptions; + this.maxOptions = config.maxOptions || 10; } } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.model.ts index 73018122f3..d17cccd338 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.model.ts @@ -2,10 +2,10 @@ import { AUTOCOMPLETE_OFF, DynamicFormControlLayout, serializable } from '@ng-dy import { DsDynamicInputModel, DsDynamicInputModelConfig } from '../ds-dynamic-input.model'; import { AuthorityOptions } from '../../../../../../core/integration/models/authority-options.model'; -export const DYNAMIC_FORM_CONTROL_TYPE_TAG = 'TYPETAG'; +export const DYNAMIC_FORM_CONTROL_TYPE_TAG = 'TAG'; export interface DynamicTagModelConfig extends DsDynamicInputModelConfig { - minChars: number; + minChars?: number; value?: any; } @@ -20,7 +20,7 @@ export class DynamicTagModel extends DsDynamicInputModel { super(config, layout); this.autoComplete = AUTOCOMPLETE_OFF; - this.minChars = config.minChars; + this.minChars = config.minChars || 3; const value = config.value || []; this.valueUpdates.next(value) } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.model.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.model.ts index 054127825d..fd68cf43ad 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.model.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/typeahead/dynamic-typeahead.model.ts @@ -5,8 +5,8 @@ import { AuthorityOptions } from '../../../../../../core/integration/models/auth export const DYNAMIC_FORM_CONTROL_TYPE_TYPEAHEAD = 'TYPEAHEAD'; export interface DsDynamicTypeaheadModelConfig extends DsDynamicInputModelConfig { - minChars: number; - value: any; + minChars?: number; + value?: any; } export class DynamicTypeaheadModel extends DsDynamicInputModel { @@ -19,7 +19,7 @@ export class DynamicTypeaheadModel extends DsDynamicInputModel { super(config, layout); this.autoComplete = AUTOCOMPLETE_OFF; - this.minChars = config.minChars; + this.minChars = config.minChars || 3; } } diff --git a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts index 431e126641..45d14bea69 100644 --- a/src/app/shared/form/builder/parsers/dropdown-field-parser.ts +++ b/src/app/shared/form/builder/parsers/dropdown-field-parser.ts @@ -23,7 +23,6 @@ export class DropdownFieldParser extends FieldParser { if (isNotEmpty(this.configData.selectableMetadata[0].authority)) { this.setAuthorityOptions(dropdownModelConfig, this.authorityUuid); - dropdownModelConfig.maxOptions = 10; if (isNotEmpty(fieldValue)) { dropdownModelConfig.value = fieldValue; } diff --git a/src/app/shared/form/builder/parsers/lookup-field-parser.ts b/src/app/shared/form/builder/parsers/lookup-field-parser.ts index 9cec80c5ad..9a9637997d 100644 --- a/src/app/shared/form/builder/parsers/lookup-field-parser.ts +++ b/src/app/shared/form/builder/parsers/lookup-field-parser.ts @@ -1,8 +1,5 @@ import { FieldParser } from './field-parser'; import { FormFieldModel } from '../models/form-field.model'; -import { AuthorityValueModel } from '../../../../core/integration/models/authority-value.model'; -import { isNotEmpty } from '../../../empty.util'; -import { FormFieldMetadataValueObject } from '../models/form-field-metadata-value.model'; import { DynamicLookupModel, DynamicLookupModelConfig } from '../ds-dynamic-form-ui/models/lookup/dynamic-lookup.model'; export class LookupFieldParser extends FieldParser { @@ -19,7 +16,6 @@ export class LookupFieldParser extends FieldParser { const lookupModelConfig: DynamicLookupModelConfig = this.initModel(); this.setAuthorityOptions(lookupModelConfig, this.authorityUuid); - lookupModelConfig.maxOptions = 10; this.setValues(lookupModelConfig, fieldValue, true); diff --git a/src/app/shared/form/builder/parsers/onebox-field-parser.ts b/src/app/shared/form/builder/parsers/onebox-field-parser.ts index c5a4f50e0c..6c7b16cf10 100644 --- a/src/app/shared/form/builder/parsers/onebox-field-parser.ts +++ b/src/app/shared/form/builder/parsers/onebox-field-parser.ts @@ -81,7 +81,6 @@ export class OneboxFieldParser extends FieldParser { const typeaheadModelConfig: DsDynamicTypeaheadModelConfig = this.initModel(); this.setAuthorityOptions(typeaheadModelConfig, this.authorityUuid); this.setValues(typeaheadModelConfig, fieldValue, true); - typeaheadModelConfig.minChars = 3; const typeaheadModel = new DynamicTypeaheadModel(typeaheadModelConfig); return typeaheadModel; } else { diff --git a/src/app/shared/form/builder/parsers/tag-field-parser.ts b/src/app/shared/form/builder/parsers/tag-field-parser.ts index fc153337e6..9520589f51 100644 --- a/src/app/shared/form/builder/parsers/tag-field-parser.ts +++ b/src/app/shared/form/builder/parsers/tag-field-parser.ts @@ -20,7 +20,6 @@ export class TagFieldParser extends FieldParser { this.setAuthorityOptions(tagModelConfig, this.authorityUuid); } - tagModelConfig.minChars = 3; this.setValues(tagModelConfig, fieldValue, null, true); const tagModel = new DynamicTagModel(tagModelConfig);