diff --git a/src/app/core/integration/integration-response-parsing.service.spec.ts b/src/app/core/integration/integration-response-parsing.service.spec.ts new file mode 100644 index 0000000000..e2e2f92d5a --- /dev/null +++ b/src/app/core/integration/integration-response-parsing.service.spec.ts @@ -0,0 +1,196 @@ +import { ErrorResponse, IntegrationSuccessResponse } from '../cache/response-cache.models'; + +import { ObjectCacheService } from '../cache/object-cache.service'; +import { GlobalConfig } from '../../../config/global-config.interface'; + +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { IntegrationResponseParsingService } from './integration-response-parsing.service'; +import { IntegrationRequest } from '../data/request.models'; +import { AuthorityValueModel } from './models/authority-value.model'; + +describe('IntegrationResponseParsingService', () => { + let service: IntegrationResponseParsingService; + + const EnvConfig = {} as GlobalConfig; + const store = {} as Store; + const objectCacheService = new ObjectCacheService(store); + const name = 'type'; + const metadata = 'dc.type'; + const query = ''; + const uuid = 'd9d30c0c-69b7-4369-8397-ca67c888974d'; + const integrationEndpoint = 'https://rest.api/integration/authorities'; + const entriesEndpoint = `${integrationEndpoint}/${name}/entries?query=${query}&metadata=${metadata}&uuid=${uuid}`; + + beforeEach(() => { + service = new IntegrationResponseParsingService(EnvConfig, objectCacheService); + }); + + describe('parse', () => { + const validRequest = new IntegrationRequest('69f375b5-19f4-4453-8c7a-7dc5c55aafbb', entriesEndpoint); + + const validResponse = { + payload: { + page: { + number: 0, + size: 5, + totalElements: 5, + totalPages: 1 + }, + _embedded: { + authorityEntries: [ + { + display: 'One', + id: 'One', + otherInformation: {}, + type: 'authority', + value: 'One' + }, + { + display: 'Two', + id: 'Two', + otherInformation: {}, + type: 'authority', + value: 'Two' + }, + { + display: 'Three', + id: 'Three', + otherInformation: {}, + type: 'authority', + value: 'Three' + }, + { + display: 'Four', + id: 'Four', + otherInformation: {}, + type: 'authority', + value: 'Four' + }, + { + display: 'Five', + id: 'Five', + otherInformation: {}, + type: 'authority', + value: 'Five' + }, + ], + + }, + _links: { + self: 'https://rest.api/integration/authorities/type/entries' + } + }, + statusCode: '200' + }; + + const invalidResponse1 = { + payload: {}, + statusCode: '200' + }; + + const invalidResponse2 = { + payload: { + page: { + number: 0, + size: 5, + totalElements: 5, + totalPages: 1 + }, + _embedded: { + authorityEntries: [ + { + display: 'One', + id: 'One', + otherInformation: {}, + type: 'authority', + value: 'One' + }, + { + display: 'Two', + id: 'Two', + otherInformation: {}, + type: 'authority', + value: 'Two' + }, + { + display: 'Three', + id: 'Three', + otherInformation: {}, + type: 'authority', + value: 'Three' + }, + { + display: 'Four', + id: 'Four', + otherInformation: {}, + type: 'authority', + value: 'Four' + }, + { + display: 'Five', + id: 'Five', + otherInformation: {}, + type: 'authority', + value: 'Five' + }, + ], + + }, + _links: {} + }, + statusCode: '200' + }; + + const definitions = [ + Object.assign(new AuthorityValueModel(), { + display: 'One', + id: 'One', + otherInformation: {}, + value: 'One' + }), + Object.assign(new AuthorityValueModel(), { + display: 'Two', + id: 'Two', + otherInformation: {}, + value: 'Two' + }), + Object.assign(new AuthorityValueModel(), { + display: 'Three', + id: 'Three', + otherInformation: {}, + value: 'Three' + }), + Object.assign(new AuthorityValueModel(), { + display: 'Four', + id: 'Four', + otherInformation: {}, + value: 'Four' + }), + Object.assign(new AuthorityValueModel(), { + display: 'Five', + id: 'Five', + otherInformation: {}, + value: 'Five' + }) + ]; + + it('should return a IntegrationSuccessResponse if data contains a valid endpoint response', () => { + const response = service.parse(validRequest, validResponse); + expect(response.constructor).toBe(IntegrationSuccessResponse); + }); + + it('should return an ErrorResponse if data contains an invalid config endpoint response', () => { + const response1 = service.parse(validRequest, invalidResponse1); + const response2 = service.parse(validRequest, invalidResponse2); + expect(response1.constructor).toBe(ErrorResponse); + expect(response2.constructor).toBe(ErrorResponse); + }); + + it('should return a IntegrationSuccessResponse with data definition', () => { + const response = service.parse(validRequest, validResponse); + expect((response as any).dataDefinition).toEqual(definitions); + }); + + }); +}); diff --git a/src/app/core/integration/integration.service.spec.ts b/src/app/core/integration/integration.service.spec.ts index fda3008bec..b7f4e019f7 100644 --- a/src/app/core/integration/integration.service.spec.ts +++ b/src/app/core/integration/integration.service.spec.ts @@ -80,15 +80,4 @@ describe('IntegrationService', () => { }); }); - // describe('getConfigBySearch', () => { - // - // it('should configure a new ConfigRequest', () => { - // findOptions.uuid = uuid; - // const expected = new ConfigRequest(requestService.generateRequestId(), searchEndpoint); - // scheduler.schedule(() => service.getConfigBySearch(findOptions).subscribe()); - // scheduler.flush(); - // - // expect(requestService.configure).toHaveBeenCalledWith(expected); - // }); - // }); }); diff --git a/src/app/shared/chips/chips.component.spec.ts b/src/app/shared/chips/chips.component.spec.ts index 20cd22488f..b060b93012 100644 --- a/src/app/shared/chips/chips.component.spec.ts +++ b/src/app/shared/chips/chips.component.spec.ts @@ -6,7 +6,7 @@ import 'rxjs/add/observable/of'; import { Chips } from './models/chips.model'; import { UploaderService } from '../uploader/uploader.service'; import { ChipsComponent } from './chips.component'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModule, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { SortablejsModule } from 'angular-sortablejs'; import { By } from '@angular/platform-browser'; import { PaginationComponent } from '../pagination/pagination.component'; @@ -22,7 +22,7 @@ function createTestComponent(html: string, type: { new(...args: any[]): T }): return fixture as ComponentFixture; } -describe('Chips component', () => { +describe('ChipsComponent test suite', () => { let testComp: TestComponent; let chipsComp: ChipsComponent; @@ -125,10 +125,31 @@ describe('Chips component', () => { expect(chipsComp.chips.remove).toHaveBeenCalledWith(item); })); - // it('should chipsSelected', inject([ChipsComponent], (app: ChipsComponent) => { - // app.chipsSelected(new Event('click'), 1); - // expect(app).toBeDefined(); - // })); + it('should save chips item index when drag and drop start', fakeAsync(() => { + const de = chipsFixture.debugElement.query(By.css('li.nav-item')); + + de.triggerEventHandler('dragstart', null); + + expect(chipsComp.dragged).toBe(0) + })); + + it('should update chips item order when drag and drop end', fakeAsync(() => { + spyOn(chipsComp.chips, 'updateOrder'); + const de = chipsFixture.debugElement.query(By.css('li.nav-item')); + + de.triggerEventHandler('dragend', null); + + expect(chipsComp.dragged).toBe(-1) + expect(chipsComp.chips.updateOrder).toHaveBeenCalled(); + })); + + it('should show item tooltip on mouse over', fakeAsync(() => { + const de = chipsFixture.debugElement.query(By.css('li.nav-item')); + + de.triggerEventHandler('mouseover', null); + + expect(chipsComp.tipText).toBe('a') + })); }); // declare a test component diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts index bf3a5ed98e..17a4e9e750 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/date-picker/date-picker.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { DynamicDsDatePickerModel } from './date-picker.model'; -import { hasValue, isNotEmpty } from '../../../../../empty.util'; +import { hasNoValue, hasValue, isNotEmpty } from '../../../../../empty.util'; export const DS_DATE_PICKER_SEPARATOR = '-'; @@ -91,6 +91,8 @@ export class DsDatePickerComponent implements OnInit { this.year = undefined; this.month = undefined; this.day = undefined; + this.disabledMonth = true; + this.disabledDay = true; } break; } @@ -100,6 +102,7 @@ export class DsDatePickerComponent implements OnInit { } else { this.month = undefined; this.day = undefined; + this.disabledDay = true; } break; } @@ -124,28 +127,28 @@ export class DsDatePickerComponent implements OnInit { } // Manage disable - if (!this.model.value && event.field === 'year') { + if (hasValue(this.year) && event.field === 'year') { this.disabledMonth = false; - } else if (this.disabledDay && event.field === 'month') { + } else if (hasValue(this.month) && event.field === 'month') { this.disabledDay = false; } // update value let value = null; - if (this.year) { + if (hasValue(this.year)) { let yyyy = this.year.toString(); while (yyyy.length < 4) { yyyy = '0' + yyyy; } value = yyyy; } - if (this.month) { + if (hasValue(this.month)) { const mm = this.month.toString().length === 1 ? '0' + this.month.toString() : this.month.toString(); value += DS_DATE_PICKER_SEPARATOR + mm; } - if (this.day) { + if (hasValue(this.day)) { const dd = this.day.toString().length === 1 ? '0' + this.day.toString() : this.day.toString(); diff --git a/src/app/shared/form/form.reducers.ts b/src/app/shared/form/form.reducers.ts index c65f2405ec..69f84978f7 100644 --- a/src/app/shared/form/form.reducers.ts +++ b/src/app/shared/form/form.reducers.ts @@ -148,14 +148,7 @@ function initForm(state: FormState, action: FormInitAction): FormState { * the new state, with the data changed. */ function changeDataForm(state: FormState, action: FormChangeAction): FormState { - if (!hasValue(state[action.payload.formId])) { - return Object.assign({}, state, { - [action.payload.formId]: { - data: action.payload.formData, - valid: state[action.payload.formId].valid - } - }); - } else { + if (hasValue(state[action.payload.formId])) { const newState = Object.assign({}, state); newState[action.payload.formId] = Object.assign({}, newState[action.payload.formId], { data: action.payload.formData, @@ -163,6 +156,8 @@ function changeDataForm(state: FormState, action: FormChangeAction): FormState { } ); return newState; + } else { + return state; } } diff --git a/src/app/shared/form/form.service.spec.ts b/src/app/shared/form/form.service.spec.ts index dcea3b6cf2..4c7e84b4f2 100644 --- a/src/app/shared/form/form.service.spec.ts +++ b/src/app/shared/form/form.service.spec.ts @@ -3,7 +3,7 @@ import { async, inject, TestBed } from '@angular/core/testing'; import { FormGroup } from '@angular/forms'; import { - DynamicFormControlModel, + DynamicFormControlModel, DynamicFormGroupModel, DynamicFormService, DynamicFormValidationService, DynamicInputModel @@ -33,18 +33,47 @@ describe('FormService test suite', () => { }), new DynamicInputModel({id: 'date'}), new DynamicInputModel({id: 'description'}), + new DynamicFormGroupModel({ + + id: 'addressLocation', + group: [ + new DynamicInputModel({ + + id: 'zipCode', + label: 'Zip Code', + placeholder: 'ZIP' + }), + new DynamicInputModel({ + + id: 'state', + label: 'State', + placeholder: 'State' + }), + new DynamicInputModel({ + + id: 'city', + label: 'City', + placeholder: 'City' + }) + ] + }), ]; const formData = { author: ['test'], title: null, date: null, - description: null + description: null, + addressLocation: { + zipCode: null, + state: null, + city: null + } }; const formState = { testForm: { data: formData, - valid: true, + valid: false, errors: [] } }; @@ -80,7 +109,7 @@ describe('FormService test suite', () => { it('should return form status when isValid is called', () => { service.isValid(formId).subscribe((status) => { - expect(status).toBe(true); + expect(status).toBe(false); }); }); @@ -166,4 +195,12 @@ describe('FormService test suite', () => { expect(formGroup.controls.description.touched).toBe(false); }); + + it('should reset form group', () => { + const control = builderService.getFormControlById('author', formGroup, formModel); + + service.resetForm(formGroup, formModel, formId); + + expect(control.value).toBeNull(); + }); }); diff --git a/src/app/shared/number-picker/number-picker.component.html b/src/app/shared/number-picker/number-picker.component.html index 55e0c39078..e1e559c535 100644 --- a/src/app/shared/number-picker/number-picker.component.html +++ b/src/app/shared/number-picker/number-picker.component.html @@ -4,7 +4,7 @@ type="button" tabindex="-1" [disabled]="disabled" - (click)="increment()"> + (click)="toggleUp()"> Increment @@ -28,7 +28,7 @@ type="button" tabindex="-1" [disabled]="disabled" - (click)="decrement()"> + (click)="toggleDown()"> Decrement diff --git a/src/app/shared/number-picker/number-picker.component.spec.ts b/src/app/shared/number-picker/number-picker.component.spec.ts new file mode 100644 index 0000000000..b7f696a42e --- /dev/null +++ b/src/app/shared/number-picker/number-picker.component.spec.ts @@ -0,0 +1,173 @@ +// Load the implementations that should be tested +import { ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, inject, TestBed, } from '@angular/core/testing'; +import 'rxjs/add/observable/of'; + +import { UploaderService } from '../uploader/uploader.service'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { By } from '@angular/platform-browser'; +import { NumberPickerComponent } from './number-picker.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +function createTestComponent(html: string, type: { new(...args: any[]): T }): ComponentFixture { + TestBed.overrideComponent(type, { + set: {template: html} + }); + const fixture = TestBed.createComponent(type); + + fixture.detectChanges(); + return fixture as ComponentFixture; +} + +describe('NumberPickerComponent test suite', () => { + + let testComp: TestComponent; + let numberPickerComp: NumberPickerComponent; + let testFixture: ComponentFixture; + let numberPickerFixture: ComponentFixture; + let html; + + // async beforeEach + beforeEach(async(() => { + + TestBed.configureTestingModule({ + imports: [ + FormsModule, + ReactiveFormsModule, + NgbModule.forRoot() + ], + declarations: [ + NumberPickerComponent, + TestComponent, + ], // declare the test component + providers: [ + ChangeDetectorRef, + NumberPickerComponent, + UploaderService + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }); + + })); + + // synchronous beforeEach + beforeEach(() => { + html = ` + `; + + testFixture = createTestComponent(html, TestComponent) as ComponentFixture; + testComp = testFixture.componentInstance; + }); + + it('should create NumberPickerComponent', inject([NumberPickerComponent], (app: NumberPickerComponent) => { + expect(app).toBeDefined(); + })); + + beforeEach(() => { + numberPickerFixture = TestBed.createComponent(NumberPickerComponent); + numberPickerComp = numberPickerFixture.componentInstance; // NumberPickerComponent test instance + numberPickerFixture.detectChanges(); + }); + + afterEach(() => { + numberPickerFixture.destroy(); + numberPickerComp = null; + }); + + it('should use default value when component\'s property is not passed', () => { + + expect(numberPickerComp.min).toBe(0); + expect(numberPickerComp.max).toBe(100); + expect(numberPickerComp.size).toBe(1); + expect(numberPickerComp.step).toBe(1); + }); + + it('should increase value', () => { + numberPickerComp.startValue = 0; + numberPickerComp.toggleUp(); + + expect(numberPickerComp.value).toBe(0); + + numberPickerComp.toggleUp(); + + expect(numberPickerComp.value).toBe(1); + }); + + it('should set min value when the value exceeds the max', () => { + numberPickerComp.value = 100; + numberPickerComp.toggleUp(); + + expect(numberPickerComp.value).toBe(0); + + }); + + it('should decrease value', () => { + numberPickerComp.startValue = 2; + numberPickerComp.toggleDown(); + + expect(numberPickerComp.value).toBe(2); + + numberPickerComp.toggleDown(); + + expect(numberPickerComp.value).toBe(1); + }); + + it('should set max value when the value is less than the min', () => { + numberPickerComp.value = 0; + numberPickerComp.toggleDown(); + + expect(numberPickerComp.value).toBe(100); + + }); + + it('should update value on input type', () => { + const de = numberPickerFixture.debugElement.query(By.css('input.form-control')); + const inputEl = de.nativeElement; + + inputEl.value = 99; + inputEl.dispatchEvent(new Event('change')); + + expect(numberPickerComp.value).toBe(99); + }); + + it('should not update value when input value is invalid', () => { + const de = numberPickerFixture.debugElement.query(By.css('input.form-control')); + const inputEl = de.nativeElement; + + inputEl.value = 101; + inputEl.dispatchEvent(new Event('change')); + + expect(numberPickerComp.value).toBe(undefined); + }); + +}); + +// declare a test component +@Component({ + selector: 'ds-test-cmp', + template: `` +}) +class TestComponent { + + public disabled = false; + public max = 100; + public min = 0; + public initValue = 0; + public showErrorMessages = false; + public size = 4; + public value; + +} diff --git a/src/app/shared/number-picker/number-picker.component.ts b/src/app/shared/number-picker/number-picker.component.ts index b4172b7182..56be174055 100644 --- a/src/app/shared/number-picker/number-picker.component.ts +++ b/src/app/shared/number-picker/number-picker.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, SimpleChanges, } from '@angular/core'; import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { isEmpty } from '../empty.util'; @Component({ selector: 'ds-number-picker', @@ -27,16 +28,17 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor { @Output() change = new EventEmitter(); @Output() focus = new EventEmitter(); - lastValue: number; + startValue: number; constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) { } ngOnInit() { - // this.lastValue = this.value; + // this.startValue = this.value; this.step = this.step || 1; this.min = this.min || 0; this.max = this.max || 100; + this.size = this.size || 1; this.disabled = this.disabled || false; this.invalid = this.invalid || false; this.cd.detectChanges(); @@ -57,12 +59,13 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor { } } - increment(reverse?: boolean) { + private changeValue(reverse: boolean = false) { + // First after init - if (!this.value) { - this.value = this.lastValue; + if (isEmpty(this.value)) { + this.value = this.startValue; } else { - this.lastValue = this.value; + this.startValue = this.value; let newValue = this.value; if (reverse) { @@ -85,8 +88,12 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor { this.emitChange(); } - decrement() { - this.increment(true); + toggleDown() { + this.changeValue(true); + } + + toggleUp() { + this.changeValue(); } update(event) { @@ -109,18 +116,18 @@ export class NumberPickerComponent implements OnInit, ControlValueAccessor { onFocus(event) { if (this.value) { - this.lastValue = this.value; + this.startValue = this.value; } this.focus.emit(event); } writeValue(value) { - if (this.lastValue) { - this.lastValue = this.value; + if (this.startValue) { + this.startValue = this.value; this.value = value; } else { // First init - this.lastValue = value; + this.startValue = value || this.min; } } diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts index 48767cf582..d5d0c4980c 100644 --- a/src/app/shared/pagination/pagination.component.spec.ts +++ b/src/app/shared/pagination/pagination.component.spec.ts @@ -300,7 +300,7 @@ describe('Pagination component', () => { it('should get parameters from route', () => { - activatedRouteStub = testFixture.debugElement.injector.get(ActivatedRoute) as any;; + activatedRouteStub = testFixture.debugElement.injector.get(ActivatedRoute) as any; activatedRouteStub.testParams = { pageId: 'test', page: 2,