1
1
import { TestBed , async , ComponentFixture } from '@angular/core/testing' ;
2
- import { Component , OnDestroy , ViewChild } from '@angular/core' ;
2
+ import { Component , OnDestroy , QueryList , ViewChild , ViewChildren } from '@angular/core' ;
3
3
import { By } from '@angular/platform-browser' ;
4
4
import { MdAutocompleteModule , MdAutocompleteTrigger } from './index' ;
5
5
import { OverlayContainer } from '../core/overlay/overlay-container' ;
6
6
import { MdInputModule } from '../input/index' ;
7
7
import { FormControl , ReactiveFormsModule } from '@angular/forms' ;
8
8
import { Subscription } from 'rxjs/Subscription' ;
9
+ import { ENTER , DOWN_ARROW , SPACE } from '../core/keyboard/keycodes' ;
10
+ import { MdOption } from '../core/option/option' ;
9
11
10
12
describe ( 'MdAutocomplete' , ( ) => {
11
13
let overlayContainerElement : HTMLElement ;
14
+ let fixture : ComponentFixture < SimpleAutocomplete > ;
15
+ let input : HTMLInputElement ;
12
16
13
17
beforeEach ( async ( ( ) => {
14
18
TestBed . configureTestingModule ( {
@@ -33,17 +37,14 @@ describe('MdAutocomplete', () => {
33
37
TestBed . compileComponents ( ) ;
34
38
} ) ) ;
35
39
36
- describe ( 'panel toggling' , ( ) => {
37
- let fixture : ComponentFixture < SimpleAutocomplete > ;
38
- let input : HTMLInputElement ;
39
-
40
- beforeEach ( ( ) => {
41
- fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
42
- fixture . detectChanges ( ) ;
40
+ beforeEach ( ( ) => {
41
+ fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
42
+ fixture . detectChanges ( ) ;
43
43
44
- input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
45
- } ) ;
44
+ input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
45
+ } ) ;
46
46
47
+ describe ( 'panel toggling' , ( ) => {
47
48
it ( 'should open the panel when the input is focused' , ( ) => {
48
49
expect ( fixture . componentInstance . trigger . panelOpen ) . toBe ( false ) ;
49
50
dispatchEvent ( 'focus' , input ) ;
@@ -165,15 +166,6 @@ describe('MdAutocomplete', () => {
165
166
} ) ;
166
167
167
168
describe ( 'forms integration' , ( ) => {
168
- let fixture : ComponentFixture < SimpleAutocomplete > ;
169
- let input : HTMLInputElement ;
170
-
171
- beforeEach ( ( ) => {
172
- fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
173
- fixture . detectChanges ( ) ;
174
-
175
- input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
176
- } ) ;
177
169
178
170
it ( 'should fill the text field when an option is selected' , ( ) => {
179
171
fixture . componentInstance . trigger . openPanel ( ) ;
@@ -185,7 +177,7 @@ describe('MdAutocomplete', () => {
185
177
fixture . detectChanges ( ) ;
186
178
187
179
expect ( input . value )
188
- . toContain ( 'California' , `Expected text field to be filled with selected value.` ) ;
180
+ . toContain ( 'California' , `Expected text field to fill with selected value.` ) ;
189
181
} ) ;
190
182
191
183
it ( 'should mark the autocomplete control as dirty when an option is selected' , ( ) => {
@@ -216,6 +208,153 @@ describe('MdAutocomplete', () => {
216
208
217
209
} ) ;
218
210
211
+ describe ( 'keyboard events' , ( ) => {
212
+ let DOWN_ARROW_EVENT : KeyboardEvent ;
213
+ let ENTER_EVENT : KeyboardEvent ;
214
+
215
+ beforeEach ( ( ) => {
216
+ DOWN_ARROW_EVENT = new FakeKeyboardEvent ( DOWN_ARROW ) as KeyboardEvent ;
217
+ ENTER_EVENT = new FakeKeyboardEvent ( ENTER ) as KeyboardEvent ;
218
+ } ) ;
219
+
220
+ it ( 'should should not focus the option when DOWN key is pressed' , ( ) => {
221
+ fixture . componentInstance . trigger . openPanel ( ) ;
222
+ fixture . detectChanges ( ) ;
223
+
224
+ spyOn ( fixture . componentInstance . options . first , 'focus' ) ;
225
+
226
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
227
+ expect ( fixture . componentInstance . options . first . focus ) . not . toHaveBeenCalled ( ) ;
228
+ } ) ;
229
+
230
+ it ( 'should set the active item to the first option when DOWN key is pressed' , async ( ( ) => {
231
+ fixture . componentInstance . trigger . openPanel ( ) ;
232
+ fixture . detectChanges ( ) ;
233
+
234
+ const optionEls =
235
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
236
+
237
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
238
+
239
+ fixture . whenStable ( ) . then ( ( ) => {
240
+ fixture . detectChanges ( ) ;
241
+ expect ( fixture . componentInstance . trigger . activeOption )
242
+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
243
+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
244
+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
245
+
246
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
247
+
248
+ fixture . whenStable ( ) . then ( ( ) => {
249
+ fixture . detectChanges ( ) ;
250
+ expect ( fixture . componentInstance . trigger . activeOption )
251
+ . toBe ( fixture . componentInstance . options . toArray ( ) [ 1 ] ,
252
+ 'Expected second option to be active.' ) ;
253
+ expect ( optionEls [ 0 ] . classList ) . not . toContain ( 'md-active' ) ;
254
+ expect ( optionEls [ 1 ] . classList ) . toContain ( 'md-active' ) ;
255
+ } ) ;
256
+ } ) ;
257
+ } ) ) ;
258
+
259
+ it ( 'should set the active item properly after filtering' , async ( ( ) => {
260
+ fixture . componentInstance . trigger . openPanel ( ) ;
261
+ fixture . detectChanges ( ) ;
262
+
263
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
264
+
265
+ fixture . whenStable ( ) . then ( ( ) => {
266
+ fixture . detectChanges ( ) ;
267
+ input . value = 'o' ;
268
+ dispatchEvent ( 'input' , input ) ;
269
+ fixture . detectChanges ( ) ;
270
+
271
+ const optionEls =
272
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
273
+
274
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
275
+
276
+ fixture . whenStable ( ) . then ( ( ) => {
277
+ fixture . detectChanges ( ) ;
278
+ expect ( fixture . componentInstance . trigger . activeOption )
279
+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
280
+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
281
+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
282
+ } ) ;
283
+ } ) ;
284
+ } ) ) ;
285
+
286
+ it ( 'should fill the text field when an option is selected with ENTER' , ( ) => {
287
+ fixture . componentInstance . trigger . openPanel ( ) ;
288
+ fixture . detectChanges ( ) ;
289
+
290
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
291
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
292
+ fixture . detectChanges ( ) ;
293
+
294
+ expect ( input . value )
295
+ . toContain ( 'Alabama' , `Expected text field to fill with selected value on ENTER.` ) ;
296
+ } ) ;
297
+
298
+ it ( 'should fill the text field, not select an option, when SPACE is entered' , ( ) => {
299
+ fixture . componentInstance . trigger . openPanel ( ) ;
300
+ fixture . detectChanges ( ) ;
301
+
302
+ input . value = 'New' ;
303
+ dispatchEvent ( 'input' , input ) ;
304
+ fixture . detectChanges ( ) ;
305
+
306
+ const SPACE_EVENT = new FakeKeyboardEvent ( SPACE ) as KeyboardEvent ;
307
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
308
+ fixture . componentInstance . trigger . _handleKeydown ( SPACE_EVENT ) ;
309
+ fixture . detectChanges ( ) ;
310
+
311
+ expect ( input . value )
312
+ . not . toContain ( 'New York' , `Expected option not to be selected on SPACE.` ) ;
313
+ } ) ;
314
+
315
+ it ( 'should mark the control as dirty when an option is selected from the keyboard' , ( ) => {
316
+ fixture . componentInstance . trigger . openPanel ( ) ;
317
+ fixture . detectChanges ( ) ;
318
+
319
+ expect ( fixture . componentInstance . stateCtrl . dirty )
320
+ . toBe ( false , `Expected control to start out pristine.` ) ;
321
+
322
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
323
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
324
+ fixture . detectChanges ( ) ;
325
+
326
+ expect ( fixture . componentInstance . stateCtrl . dirty )
327
+ . toBe ( true , `Expected control to become dirty when option was selected by ENTER.` ) ;
328
+ } ) ;
329
+
330
+ it ( 'should open the panel again when typing after making a selection' , async ( ( ) => {
331
+ fixture . componentInstance . trigger . openPanel ( ) ;
332
+ fixture . detectChanges ( ) ;
333
+
334
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
335
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
336
+ fixture . detectChanges ( ) ;
337
+
338
+ fixture . whenStable ( ) . then ( ( ) => {
339
+ expect ( fixture . componentInstance . trigger . panelOpen )
340
+ . toBe ( false , `Expected panel state to read closed after ENTER key.` ) ;
341
+ expect ( overlayContainerElement . textContent )
342
+ . toEqual ( '' , `Expected panel to close after ENTER key.` ) ;
343
+
344
+ // 65 is the keycode for "a"
345
+ const A_KEY = new FakeKeyboardEvent ( 65 ) as KeyboardEvent ;
346
+ fixture . componentInstance . trigger . _handleKeydown ( A_KEY ) ;
347
+ fixture . detectChanges ( ) ;
348
+
349
+ expect ( fixture . componentInstance . trigger . panelOpen )
350
+ . toBe ( true , `Expected panel state to read open when typing in input.` ) ;
351
+ expect ( overlayContainerElement . textContent )
352
+ . toContain ( 'Alabama' , `Expected panel to display when typing in input.` ) ;
353
+ } ) ;
354
+ } ) ) ;
355
+
356
+ } ) ;
357
+
219
358
} ) ;
220
359
221
360
@Component ( {
@@ -237,6 +376,7 @@ class SimpleAutocomplete implements OnDestroy {
237
376
valueSub : Subscription ;
238
377
239
378
@ViewChild ( MdAutocompleteTrigger ) trigger : MdAutocompleteTrigger ;
379
+ @ViewChildren ( MdOption ) options : QueryList < MdOption > ;
240
380
241
381
states = [
242
382
{ code : 'AL' , name : 'Alabama' } ,
@@ -281,4 +421,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
281
421
element . dispatchEvent ( event ) ;
282
422
}
283
423
424
+ /** This is a mock keyboard event to test keyboard events in the autocomplete. */
425
+ class FakeKeyboardEvent {
426
+ constructor ( public keyCode : number ) { }
427
+ preventDefault ( ) { }
428
+ }
429
+
430
+
284
431
0 commit comments