1- import { Component , ViewChild , DebugElement } from '@angular/core' ;
1+ import { Component , ViewChild , DebugElement , EventEmitter , QueryList } from '@angular/core' ;
22import { TestBed , fakeAsync , tick , ComponentFixture , waitForAsync } from '@angular/core/testing' ;
33import { FormControl , FormsModule } from '@angular/forms' ;
44import { By } from '@angular/platform-browser' ;
55import { NoopAnimationsModule } from '@angular/platform-browser/animations' ;
66import { IgxTimePickerComponent , IgxTimePickerModule , IgxTimePickerValidationFailedEventArgs } from './time-picker.component' ;
77import { UIInteractions } from '../test-utils/ui-interactions.spec' ;
88import {
9- IgxHintDirective , IgxInputGroupComponent , IgxInputGroupModule , IgxLabelDirective , IgxPrefixDirective , IgxSuffixDirective
9+ IgxHintDirective , IgxInputGroupComponent , IgxInputGroupModule , IgxInputState , IgxLabelDirective , IgxPrefixDirective , IgxSuffixDirective
1010} from '../input-group/public_api' ;
1111import { configureTestSuite } from '../test-utils/configure-suite' ;
1212import { PickerInteractionMode } from '../date-common/types' ;
@@ -17,6 +17,7 @@ import { DatePart } from '../directives/date-time-editor/public_api';
1717import { DateTimeUtil } from '../date-common/util/date-time.util' ;
1818import { IgxTimeItemDirective } from './time-picker.directives' ;
1919import { IgxPickerClearComponent , IgxPickerToggleComponent } from '../date-common/public_api' ;
20+ import { Subscription } from 'rxjs' ;
2021
2122const CSS_CLASS_TIMEPICKER = 'igx-time-picker' ;
2223const 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>
0 commit comments