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 { Dir , LayoutDirection } from '../core/rtl/dir' ;
8
8
import { FormControl , ReactiveFormsModule } from '@angular/forms' ;
9
9
import { Subscription } from 'rxjs/Subscription' ;
10
+ import { ENTER , DOWN_ARROW , SPACE } from '../core/keyboard/keycodes' ;
11
+ import { MdOption } from '../core/option/option' ;
10
12
11
13
describe ( 'MdAutocomplete' , ( ) => {
12
14
let overlayContainerElement : HTMLElement ;
@@ -205,7 +207,7 @@ describe('MdAutocomplete', () => {
205
207
fixture . detectChanges ( ) ;
206
208
207
209
expect ( input . value )
208
- . toContain ( 'California' , `Expected text field to be filled with selected value.` ) ;
210
+ . toContain ( 'California' , `Expected text field to fill with selected value.` ) ;
209
211
} ) ;
210
212
211
213
it ( 'should mark the autocomplete control as dirty when an option is selected' , ( ) => {
@@ -236,6 +238,159 @@ describe('MdAutocomplete', () => {
236
238
237
239
} ) ;
238
240
241
+ describe ( 'keyboard events' , ( ) => {
242
+ let fixture : ComponentFixture < SimpleAutocomplete > ;
243
+ let input : HTMLInputElement ;
244
+ let DOWN_ARROW_EVENT : KeyboardEvent ;
245
+ let ENTER_EVENT : KeyboardEvent ;
246
+
247
+ beforeEach ( ( ) => {
248
+ fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
249
+ fixture . detectChanges ( ) ;
250
+
251
+ input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
252
+ DOWN_ARROW_EVENT = new FakeKeyboardEvent ( DOWN_ARROW ) as KeyboardEvent ;
253
+ ENTER_EVENT = new FakeKeyboardEvent ( ENTER ) as KeyboardEvent ;
254
+ } ) ;
255
+
256
+ it ( 'should should not focus the option when DOWN key is pressed' , ( ) => {
257
+ fixture . componentInstance . trigger . openPanel ( ) ;
258
+ fixture . detectChanges ( ) ;
259
+
260
+ spyOn ( fixture . componentInstance . options . first , 'focus' ) ;
261
+
262
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
263
+ expect ( fixture . componentInstance . options . first . focus ) . not . toHaveBeenCalled ( ) ;
264
+ } ) ;
265
+
266
+ it ( 'should set the active item to the first option when DOWN key is pressed' , async ( ( ) => {
267
+ fixture . componentInstance . trigger . openPanel ( ) ;
268
+ fixture . detectChanges ( ) ;
269
+
270
+ const optionEls =
271
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
272
+
273
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
274
+
275
+ fixture . whenStable ( ) . then ( ( ) => {
276
+ fixture . detectChanges ( ) ;
277
+ expect ( fixture . componentInstance . trigger . activeOption )
278
+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
279
+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
280
+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
281
+
282
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
283
+
284
+ fixture . whenStable ( ) . then ( ( ) => {
285
+ fixture . detectChanges ( ) ;
286
+ expect ( fixture . componentInstance . trigger . activeOption )
287
+ . toBe ( fixture . componentInstance . options . toArray ( ) [ 1 ] ,
288
+ 'Expected second option to be active.' ) ;
289
+ expect ( optionEls [ 0 ] . classList ) . not . toContain ( 'md-active' ) ;
290
+ expect ( optionEls [ 1 ] . classList ) . toContain ( 'md-active' ) ;
291
+ } ) ;
292
+ } ) ;
293
+ } ) ) ;
294
+
295
+ it ( 'should set the active item properly after filtering' , async ( ( ) => {
296
+ fixture . componentInstance . trigger . openPanel ( ) ;
297
+ fixture . detectChanges ( ) ;
298
+
299
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
300
+
301
+ fixture . whenStable ( ) . then ( ( ) => {
302
+ fixture . detectChanges ( ) ;
303
+ input . value = 'o' ;
304
+ dispatchEvent ( 'input' , input ) ;
305
+ fixture . detectChanges ( ) ;
306
+
307
+ const optionEls =
308
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
309
+
310
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
311
+
312
+ fixture . whenStable ( ) . then ( ( ) => {
313
+ fixture . detectChanges ( ) ;
314
+ expect ( fixture . componentInstance . trigger . activeOption )
315
+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
316
+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
317
+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
318
+ } ) ;
319
+ } ) ;
320
+ } ) ) ;
321
+
322
+ it ( 'should fill the text field when an option is selected with ENTER' , ( ) => {
323
+ fixture . componentInstance . trigger . openPanel ( ) ;
324
+ fixture . detectChanges ( ) ;
325
+
326
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
327
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
328
+ fixture . detectChanges ( ) ;
329
+
330
+ expect ( input . value )
331
+ . toContain ( 'Alabama' , `Expected text field to fill with selected value on ENTER.` ) ;
332
+ } ) ;
333
+
334
+ it ( 'should fill the text field, not select an option, when SPACE is entered' , ( ) => {
335
+ fixture . componentInstance . trigger . openPanel ( ) ;
336
+ fixture . detectChanges ( ) ;
337
+
338
+ input . value = 'New' ;
339
+ dispatchEvent ( 'input' , input ) ;
340
+ fixture . detectChanges ( ) ;
341
+
342
+ const SPACE_EVENT = new FakeKeyboardEvent ( SPACE ) as KeyboardEvent ;
343
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
344
+ fixture . componentInstance . trigger . _handleKeydown ( SPACE_EVENT ) ;
345
+ fixture . detectChanges ( ) ;
346
+
347
+ expect ( input . value )
348
+ . not . toContain ( 'New York' , `Expected option not to be selected on SPACE.` ) ;
349
+ } ) ;
350
+
351
+ it ( 'should mark the control as dirty when an option is selected from the keyboard' , ( ) => {
352
+ fixture . componentInstance . trigger . openPanel ( ) ;
353
+ fixture . detectChanges ( ) ;
354
+
355
+ expect ( fixture . componentInstance . stateCtrl . dirty )
356
+ . toBe ( false , `Expected control to start out pristine.` ) ;
357
+
358
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
359
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
360
+ fixture . detectChanges ( ) ;
361
+
362
+ expect ( fixture . componentInstance . stateCtrl . dirty )
363
+ . toBe ( true , `Expected control to become dirty when option was selected by ENTER.` ) ;
364
+ } ) ;
365
+
366
+ it ( 'should open the panel again when typing after making a selection' , async ( ( ) => {
367
+ fixture . componentInstance . trigger . openPanel ( ) ;
368
+ fixture . detectChanges ( ) ;
369
+
370
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
371
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
372
+ fixture . detectChanges ( ) ;
373
+
374
+ fixture . whenStable ( ) . then ( ( ) => {
375
+ expect ( fixture . componentInstance . trigger . panelOpen )
376
+ . toBe ( false , `Expected panel state to read closed after ENTER key.` ) ;
377
+ expect ( overlayContainerElement . textContent )
378
+ . toEqual ( '' , `Expected panel to close after ENTER key.` ) ;
379
+
380
+ // 65 is the keycode for "a"
381
+ const A_KEY = new FakeKeyboardEvent ( 65 ) as KeyboardEvent ;
382
+ fixture . componentInstance . trigger . _handleKeydown ( A_KEY ) ;
383
+ fixture . detectChanges ( ) ;
384
+
385
+ expect ( fixture . componentInstance . trigger . panelOpen )
386
+ . toBe ( true , `Expected panel state to read open when typing in input.` ) ;
387
+ expect ( overlayContainerElement . textContent )
388
+ . toContain ( 'Alabama' , `Expected panel to display when typing in input.` ) ;
389
+ } ) ;
390
+ } ) ) ;
391
+
392
+ } ) ;
393
+
239
394
} ) ;
240
395
241
396
@Component ( {
@@ -257,6 +412,7 @@ class SimpleAutocomplete implements OnDestroy {
257
412
valueSub : Subscription ;
258
413
259
414
@ViewChild ( MdAutocompleteTrigger ) trigger : MdAutocompleteTrigger ;
415
+ @ViewChildren ( MdOption ) options : QueryList < MdOption > ;
260
416
261
417
states = [
262
418
{ code : 'AL' , name : 'Alabama' } ,
@@ -301,4 +457,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
301
457
element . dispatchEvent ( event ) ;
302
458
}
303
459
460
+ /** This is a mock keyboard event to test keyboard events in the autocomplete. */
461
+ class FakeKeyboardEvent {
462
+ constructor ( public keyCode : number ) { }
463
+ preventDefault ( ) { }
464
+ }
465
+
466
+
304
467
0 commit comments