11import { 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' ;
33import { By } from '@angular/platform-browser' ;
44import { MdAutocompleteModule , MdAutocompleteTrigger } from './index' ;
55import { OverlayContainer } from '../core/overlay/overlay-container' ;
66import { MdInputModule } from '../input/index' ;
77import { FormControl , ReactiveFormsModule } from '@angular/forms' ;
88import { Subscription } from 'rxjs/Subscription' ;
9+ import { ENTER , DOWN_ARROW , SPACE } from '../core/keyboard/keycodes' ;
10+ import { MdOption } from '../core/option/option' ;
911
1012describe ( 'MdAutocomplete' , ( ) => {
1113 let overlayContainerElement : HTMLElement ;
14+ let fixture : ComponentFixture < SimpleAutocomplete > ;
15+ let input : HTMLInputElement ;
1216
1317 beforeEach ( async ( ( ) => {
1418 TestBed . configureTestingModule ( {
@@ -33,17 +37,14 @@ describe('MdAutocomplete', () => {
3337 TestBed . compileComponents ( ) ;
3438 } ) ) ;
3539
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 ( ) ;
4343
44- input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
45- } ) ;
44+ input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
45+ } ) ;
4646
47+ describe ( 'panel toggling' , ( ) => {
4748 it ( 'should open the panel when the input is focused' , ( ) => {
4849 expect ( fixture . componentInstance . trigger . panelOpen ) . toBe ( false ) ;
4950 dispatchEvent ( 'focus' , input ) ;
@@ -165,15 +166,6 @@ describe('MdAutocomplete', () => {
165166 } ) ;
166167
167168 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- } ) ;
177169
178170 it ( 'should fill the text field when an option is selected' , ( ) => {
179171 fixture . componentInstance . trigger . openPanel ( ) ;
@@ -185,7 +177,7 @@ describe('MdAutocomplete', () => {
185177 fixture . detectChanges ( ) ;
186178
187179 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.` ) ;
189181 } ) ;
190182
191183 it ( 'should mark the autocomplete control as dirty when an option is selected' , ( ) => {
@@ -216,6 +208,137 @@ describe('MdAutocomplete', () => {
216208
217209 } ) ;
218210
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' , ( ) => {
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+ fixture . detectChanges ( ) ;
239+
240+ expect ( fixture . componentInstance . trigger . activeOption )
241+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
242+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
243+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
244+
245+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
246+ fixture . detectChanges ( ) ;
247+
248+ expect ( fixture . componentInstance . trigger . activeOption )
249+ . toBe ( fixture . componentInstance . options . toArray ( ) [ 1 ] ,
250+ 'Expected second option to be active.' ) ;
251+ expect ( optionEls [ 0 ] . classList ) . not . toContain ( 'md-active' ) ;
252+ expect ( optionEls [ 1 ] . classList ) . toContain ( 'md-active' ) ;
253+ } ) ;
254+
255+ it ( 'should set the active item properly after filtering' , ( ) => {
256+ fixture . componentInstance . trigger . openPanel ( ) ;
257+ fixture . detectChanges ( ) ;
258+
259+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
260+ fixture . detectChanges ( ) ;
261+
262+ input . value = 'o' ;
263+ dispatchEvent ( 'input' , input ) ;
264+ fixture . detectChanges ( ) ;
265+
266+ const optionEls =
267+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
268+
269+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
270+ fixture . detectChanges ( ) ;
271+
272+ expect ( fixture . componentInstance . trigger . activeOption )
273+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
274+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
275+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
276+ } ) ;
277+
278+ it ( 'should fill the text field when an option is selected with ENTER' , ( ) => {
279+ fixture . componentInstance . trigger . openPanel ( ) ;
280+ fixture . detectChanges ( ) ;
281+
282+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
283+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
284+ fixture . detectChanges ( ) ;
285+
286+ expect ( input . value )
287+ . toContain ( 'Alabama' , `Expected text field to fill with selected value on ENTER.` ) ;
288+ } ) ;
289+
290+ it ( 'should fill the text field, not select an option, when SPACE is entered' , ( ) => {
291+ fixture . componentInstance . trigger . openPanel ( ) ;
292+ fixture . detectChanges ( ) ;
293+
294+ input . value = 'New' ;
295+ dispatchEvent ( 'input' , input ) ;
296+ fixture . detectChanges ( ) ;
297+
298+ const SPACE_EVENT = new FakeKeyboardEvent ( SPACE ) as KeyboardEvent ;
299+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
300+ fixture . componentInstance . trigger . _handleKeydown ( SPACE_EVENT ) ;
301+ fixture . detectChanges ( ) ;
302+
303+ expect ( input . value )
304+ . not . toContain ( 'New York' , `Expected option not to be selected on SPACE.` ) ;
305+ } ) ;
306+
307+ it ( 'should mark the control as dirty when an option is selected from the keyboard' , ( ) => {
308+ fixture . componentInstance . trigger . openPanel ( ) ;
309+ fixture . detectChanges ( ) ;
310+
311+ expect ( fixture . componentInstance . stateCtrl . dirty )
312+ . toBe ( false , `Expected control to start out pristine.` ) ;
313+
314+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
315+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
316+ fixture . detectChanges ( ) ;
317+
318+ expect ( fixture . componentInstance . stateCtrl . dirty )
319+ . toBe ( true , `Expected control to become dirty when option was selected by ENTER.` ) ;
320+ } ) ;
321+
322+ it ( 'should open the panel again when typing after making a selection' , ( ) => {
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+ input . value = 'a' ;
331+ dispatchEvent ( 'input' , input ) ;
332+ fixture . detectChanges ( ) ;
333+
334+ expect ( fixture . componentInstance . trigger . panelOpen )
335+ . toBe ( true , `Expected panel state to read open when typing in input.` ) ;
336+ expect ( overlayContainerElement . textContent )
337+ . toContain ( 'Alabama' , `Expected panel to display when typing in input.` ) ;
338+ } ) ;
339+
340+ } ) ;
341+
219342} ) ;
220343
221344@Component ( {
@@ -237,6 +360,7 @@ class SimpleAutocomplete implements OnDestroy {
237360 valueSub : Subscription ;
238361
239362 @ViewChild ( MdAutocompleteTrigger ) trigger : MdAutocompleteTrigger ;
363+ @ViewChildren ( MdOption ) options : QueryList < MdOption > ;
240364
241365 states = [
242366 { code : 'AL' , name : 'Alabama' } ,
@@ -281,4 +405,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
281405 element . dispatchEvent ( event ) ;
282406}
283407
408+ /** This is a mock keyboard event to test keyboard events in the autocomplete. */
409+ class FakeKeyboardEvent {
410+ constructor ( public keyCode : number ) { }
411+ preventDefault ( ) { }
412+ }
413+
414+
284415
0 commit comments