Skip to content

Commit 40e8333

Browse files
ViktorSlavovLipata
andauthored
test(time-picker): add ngControl unit tests (#9450)
Co-authored-by: Nikolay Alipiev <[email protected]>
1 parent 68c2ae9 commit 40e8333

File tree

2 files changed

+242
-16
lines changed

2 files changed

+242
-16
lines changed

projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts

Lines changed: 241 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { Component, ViewChild, DebugElement } from '@angular/core';
1+
import { Component, ViewChild, DebugElement, EventEmitter, QueryList } from '@angular/core';
22
import { TestBed, fakeAsync, tick, ComponentFixture, waitForAsync } from '@angular/core/testing';
33
import { FormControl, FormsModule } from '@angular/forms';
44
import { By } from '@angular/platform-browser';
55
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
66
import { IgxTimePickerComponent, IgxTimePickerModule, IgxTimePickerValidationFailedEventArgs } from './time-picker.component';
77
import { UIInteractions } from '../test-utils/ui-interactions.spec';
88
import {
9-
IgxHintDirective, IgxInputGroupComponent, IgxInputGroupModule, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective
9+
IgxHintDirective, IgxInputGroupComponent, IgxInputGroupModule, IgxInputState, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective
1010
} from '../input-group/public_api';
1111
import { configureTestSuite } from '../test-utils/configure-suite';
1212
import { PickerInteractionMode } from '../date-common/types';
@@ -17,6 +17,7 @@ import { DatePart } from '../directives/date-time-editor/public_api';
1717
import { DateTimeUtil } from '../date-common/util/date-time.util';
1818
import { IgxTimeItemDirective } from './time-picker.directives';
1919
import { IgxPickerClearComponent, IgxPickerToggleComponent } from '../date-common/public_api';
20+
import { Subscription } from 'rxjs';
2021

2122
const CSS_CLASS_TIMEPICKER = 'igx-time-picker';
2223
const CSS_CLASS_INPUTGROUP = 'igx-input-group';
@@ -39,18 +40,238 @@ describe('IgxTimePicker', () => {
3940
let timePicker: IgxTimePickerComponent;
4041

4142
describe('Unit tests', () => {
42-
const elementRef = { nativeElement: null };
43-
const mockNgControl = jasmine.createSpyObj('NgControl',
44-
['registerOnChangeCb',
45-
'registerOnTouchedCb',
46-
'registerOnValidatorChangeCb']);
47-
const mockInjector = jasmine.createSpyObj('Injector', { get: mockNgControl });
48-
const mockDateTimeEditorDirective = jasmine.createSpyObj('IgxDateTimeEditorDirective', ['increment', 'decrement'], { value: null });
49-
const mockInputDirective = jasmine.createSpyObj('IgxInputDirective', { value: null });
50-
51-
it('should open/close the dropdown with open()/close() method', () => {
43+
let mockControlInstance: any;
44+
let elementRef;
45+
let mockNgControl;
46+
let mockInjector;
47+
let mockDateTimeEditorDirective;
48+
let mockInputGroup: Partial<IgxInputGroupComponent>;
49+
let mockInputDirective;
50+
51+
beforeEach(() => {
52+
mockDateTimeEditorDirective = {
53+
_value: null,
54+
get value() {
55+
return this._value;
56+
},
57+
clear() {
58+
this.valueChange.emit(null);
59+
},
60+
increment: () => {},
61+
decrement: () => {},
62+
set value(val: any) {
63+
this._value = val;
64+
},
65+
valueChange: new EventEmitter<any>(),
66+
validationFailed: new EventEmitter<any>()
67+
};
68+
spyOn(mockDateTimeEditorDirective, 'increment');
69+
spyOn(mockDateTimeEditorDirective, 'decrement');
70+
71+
mockInputGroup = {
72+
_isFocused: false,
73+
get isFocused() {
74+
return this._isFocused;
75+
},
76+
set isFocused(val: boolean) {
77+
this._isFocused = val;
78+
},
79+
_isRequired: false,
80+
get isRequired() {
81+
return this._isRequired;
82+
},
83+
set isRequired(val: boolean) {
84+
this._isRequired = val;
85+
},
86+
element: {
87+
nativeElement: jasmine.createSpyObj('mockElement',
88+
['focus', 'blur', 'click', 'addEventListener', 'removeEventListener'])
89+
}
90+
} as any;
91+
92+
elementRef = {
93+
nativeElement: jasmine.createSpyObj<HTMLElement>('mockElement', ['blur', 'click', 'focus'])
94+
};
95+
mockControlInstance = {
96+
_touched: false,
97+
get touched() {
98+
return this._touched;
99+
},
100+
set touched(val: boolean) {
101+
this._touched = val;
102+
},
103+
_dirty: false,
104+
get dirty() {
105+
return this._dirty;
106+
},
107+
set dirty(val: boolean) {
108+
this._dirty = val;
109+
},
110+
_asyncValidator: () => { },
111+
get asyncValidator() {
112+
return this._asyncValidator;
113+
},
114+
set asyncValidator(val: () => boolean) {
115+
this._asyncValidator = val;
116+
},
117+
_validator: () => { },
118+
get validator() {
119+
return this._validator;
120+
},
121+
set validator(val: () => boolean) {
122+
this._validator = val;
123+
}
124+
};
125+
mockNgControl = {
126+
registerOnChangeCb: () => { },
127+
registerOnTouchedCb: () => { },
128+
registerOnValidatorChangeCb: () => { },
129+
statusChanges: new EventEmitter(),
130+
_control: mockControlInstance,
131+
get control() {
132+
return this._control;
133+
},
134+
set control(val: any) {
135+
this._control = val;
136+
},
137+
valid: true
138+
};
139+
mockInputDirective = {
140+
valid: 'mock',
141+
nativeElement: {
142+
_listeners: {
143+
none: []
144+
},
145+
addEventListener(event: string, cb: () => void) {
146+
let target = this._listeners[event];
147+
if (!target) {
148+
this._listeners[event] = [];
149+
target = this._listeners[event];
150+
}
151+
target.push(cb);
152+
},
153+
removeEventListener(event: string, cb: () => void) {
154+
const target = this._listeners[event];
155+
if (!target) {
156+
return;
157+
}
158+
const index = target.indexOf(cb);
159+
if (index !== -1) {
160+
target.splice(index, 1);
161+
}
162+
},
163+
dispatchEvent(event: string) {
164+
const target = this._listeners[event];
165+
if (!target) {
166+
return;
167+
}
168+
target.forEach(e => {
169+
e();
170+
});
171+
},
172+
focus() {
173+
this.dispatchEvent('focus');
174+
},
175+
click() {
176+
this.dispatchEvent('click');
177+
},
178+
blur() {
179+
this.dispatchEvent('blur');
180+
}
181+
},
182+
focus: () => {}
183+
};
184+
mockInjector = jasmine.createSpyObj('Injector', {
185+
get: mockNgControl
186+
});
52187
timePicker = new IgxTimePickerComponent(elementRef, null, null, null, mockInjector, null);
53188
(timePicker as any).dateTimeEditor = mockDateTimeEditorDirective;
189+
(timePicker as any)._inputGroup = mockInputGroup;
190+
(timePicker as any).inputDirective = mockInputDirective;
191+
timePicker.toggleComponents = new QueryList<any>();
192+
timePicker.clearComponents = new QueryList<any>();
193+
});
194+
195+
it('should properly initialize w/ ngControl', () => {
196+
const mockSub = jasmine.createSpyObj<Subscription>('mockSub', ['unsubscribe']);
197+
spyOn(mockNgControl.statusChanges, 'subscribe').and.returnValue(mockSub);
198+
timePicker.ngOnInit();
199+
timePicker.ngAfterViewInit();
200+
expect(mockNgControl.statusChanges.subscribe).toHaveBeenCalledTimes(1);
201+
timePicker.ngOnDestroy();
202+
expect(mockSub.unsubscribe).toHaveBeenCalledTimes(1);
203+
});
204+
205+
it('should properly subscribe to ngControl status changes', () => {
206+
timePicker.ngOnInit();
207+
timePicker.ngAfterViewInit();
208+
const touchedSpy = spyOnProperty(mockControlInstance, 'touched', 'get');
209+
const dirtySpy = spyOnProperty(mockControlInstance, 'dirty', 'get');
210+
const validatorSpy = spyOnProperty(mockControlInstance, 'validator');
211+
const asyncValidatorSpy = spyOnProperty(mockControlInstance, 'asyncValidator');
212+
const inputGroupFocusedSpy = spyOnProperty(mockInputGroup, 'isFocused', 'get');
213+
const inputGroupRequiredGet = spyOnProperty(mockInputGroup, 'isRequired', 'get');
214+
const inputGroupRequiredSet = spyOnProperty(mockInputGroup, 'isRequired', 'set');
215+
inputGroupRequiredGet.and.returnValue(false);
216+
inputGroupFocusedSpy.and.returnValue(false);
217+
expect(touchedSpy).not.toHaveBeenCalled();
218+
expect(dirtySpy).not.toHaveBeenCalled();
219+
expect(validatorSpy).not.toHaveBeenCalled();
220+
expect(asyncValidatorSpy).not.toHaveBeenCalled();
221+
222+
touchedSpy.and.returnValue(false);
223+
dirtySpy.and.returnValue(false);
224+
mockNgControl.statusChanges.emit();
225+
expect(touchedSpy).toHaveBeenCalledTimes(1);
226+
expect(dirtySpy).toHaveBeenCalledTimes(1);
227+
// required getter
228+
expect(validatorSpy).toHaveBeenCalledTimes(1);
229+
230+
touchedSpy.and.returnValue(true);
231+
dirtySpy.and.returnValue(true);
232+
validatorSpy.and.returnValue(false);
233+
asyncValidatorSpy.and.returnValue(false);
234+
mockNgControl.statusChanges.emit();
235+
expect(validatorSpy).toHaveBeenCalledTimes(3);
236+
expect(asyncValidatorSpy).toHaveBeenCalledTimes(1);
237+
expect(inputGroupFocusedSpy).not.toHaveBeenCalled();
238+
239+
validatorSpy.and.returnValue(() => {});
240+
asyncValidatorSpy.and.returnValue(() => {});
241+
242+
mockNgControl.statusChanges.emit();
243+
expect(inputGroupFocusedSpy).toHaveBeenCalledTimes(1);
244+
expect(inputGroupRequiredSet).not.toHaveBeenCalled();
245+
246+
inputGroupRequiredGet.and.returnValue(false);
247+
validatorSpy.and.returnValue(() => ({ required: true }));
248+
mockNgControl.statusChanges.emit();
249+
expect(inputGroupFocusedSpy).toHaveBeenCalledTimes(2);
250+
expect(inputGroupRequiredSet).toHaveBeenCalledTimes(1);
251+
expect(inputGroupRequiredSet).toHaveBeenCalledWith(true);
252+
console.log(inputGroupRequiredSet.calls);
253+
inputGroupRequiredGet.and.returnValue(true);
254+
255+
mockNgControl.statusChanges.emit();
256+
expect(inputGroupFocusedSpy).toHaveBeenCalledTimes(3);
257+
258+
expect(mockInputDirective.valid).toBe(IgxInputState.INITIAL);
259+
mockNgControl.valid = false;
260+
261+
mockNgControl.statusChanges.emit();
262+
expect(mockInputDirective.valid).toBe(IgxInputState.INVALID);
263+
264+
inputGroupFocusedSpy.and.returnValue(true);
265+
mockNgControl.statusChanges.emit();
266+
expect(mockInputDirective.valid).toBe(IgxInputState.INVALID);
267+
268+
mockNgControl.valid = true;
269+
mockNgControl.statusChanges.emit();
270+
expect(mockInputDirective.valid).toBe(IgxInputState.VALID);
271+
timePicker.ngOnDestroy();
272+
});
273+
274+
it('should open/close the dropdown with open()/close() method', () => {
54275
const mockToggleDirective = jasmine.createSpyObj('IgxToggleDirective', ['open', 'close'], { collapsed: true });
55276
(timePicker as any).toggleRef = mockToggleDirective;
56277
timePicker.ngOnInit();
@@ -186,6 +407,8 @@ describe('IgxTimePicker', () => {
186407
timePicker.minDropdownValue = timePicker.minDateValue;
187408
timePicker.maxDropdownValue = timePicker.maxDateValue;
188409
timePicker.ngOnInit();
410+
spyOn(mockNgControl, 'registerOnChangeCb');
411+
spyOn(mockNgControl, 'registerOnTouchedCb');
189412
timePicker.registerOnChange(mockNgControl.registerOnChangeCb);
190413
timePicker.registerOnTouched(mockNgControl.registerOnTouchedCb);
191414

@@ -213,6 +436,9 @@ describe('IgxTimePicker', () => {
213436
timePicker['inputDirective'] = mockInputDirective;
214437
timePicker.ngOnInit();
215438

439+
spyOn(mockNgControl, 'registerOnChangeCb');
440+
spyOn(mockNgControl, 'registerOnValidatorChangeCb');
441+
216442
timePicker.registerOnChange(mockNgControl.registerOnChangeCb);
217443
timePicker.registerOnValidatorChange(mockNgControl.registerOnValidatorChangeCb);
218444

@@ -499,7 +725,7 @@ describe('IgxTimePicker', () => {
499725
});
500726

501727
xit('should open/close the dropdown and keep the current selection on Space/Enter key press', fakeAsync(() => {
502-
timePicker.itemsDelta = {hours: 4, minutes: 7, seconds: 1};
728+
timePicker.itemsDelta = { hours: 4, minutes: 7, seconds: 1 };
503729
fixture.detectChanges();
504730

505731
timePicker.open();
@@ -692,7 +918,7 @@ describe('IgxTimePicker', () => {
692918
timePicker.value = new Date(2021, 24, 2, 6, 42, 0);
693919
fixture.componentInstance.minValue = '06:30:00';
694920
fixture.componentInstance.maxValue = '18:30:00';
695-
timePicker.itemsDelta = {hours: 3, minutes: 7, seconds: 1};
921+
timePicker.itemsDelta = { hours: 3, minutes: 7, seconds: 1 };
696922
fixture.detectChanges();
697923

698924
timePicker.open();
@@ -921,7 +1147,7 @@ export class IgxTimePickerTestComponent {
9211147
}
9221148

9231149
@Component({
924-
template:`
1150+
template: `
9251151
<igx-time-picker [mode]="mode">
9261152
<label igxLabel>Label</label>
9271153
<igx-picker-toggle igxPrefix *ngIf="showCustomToggle">CustomToggle</igx-picker-toggle>

projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective
421421
if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) {
422422
// Run the validation with empty object to check if required is enabled.
423423
const error = this._ngControl.control.validator({} as AbstractControl);
424-
return error && error.required;
424+
return !!(error && error.required);
425425
}
426426

427427
return false;

0 commit comments

Comments
 (0)