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 ;
13
15
let dir : LayoutDirection ;
16
+ let fixture : ComponentFixture < SimpleAutocomplete > ;
17
+ let input : HTMLInputElement ;
14
18
15
19
beforeEach ( async ( ( ) => {
16
20
dir = 'ltr' ;
@@ -39,17 +43,14 @@ describe('MdAutocomplete', () => {
39
43
TestBed . compileComponents ( ) ;
40
44
} ) ) ;
41
45
42
- describe ( 'panel toggling' , ( ) => {
43
- let fixture : ComponentFixture < SimpleAutocomplete > ;
44
- let input : HTMLInputElement ;
45
-
46
- beforeEach ( ( ) => {
47
- fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
48
- fixture . detectChanges ( ) ;
46
+ beforeEach ( ( ) => {
47
+ fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
48
+ fixture . detectChanges ( ) ;
49
49
50
- input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
51
- } ) ;
50
+ input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
51
+ } ) ;
52
52
53
+ describe ( 'panel toggling' , ( ) => {
53
54
it ( 'should open the panel when the input is focused' , ( ) => {
54
55
expect ( fixture . componentInstance . trigger . panelOpen ) . toBe ( false ) ;
55
56
dispatchEvent ( 'focus' , input ) ;
@@ -185,15 +186,6 @@ describe('MdAutocomplete', () => {
185
186
} ) ;
186
187
187
188
describe ( 'forms integration' , ( ) => {
188
- let fixture : ComponentFixture < SimpleAutocomplete > ;
189
- let input : HTMLInputElement ;
190
-
191
- beforeEach ( ( ) => {
192
- fixture = TestBed . createComponent ( SimpleAutocomplete ) ;
193
- fixture . detectChanges ( ) ;
194
-
195
- input = fixture . debugElement . query ( By . css ( 'input' ) ) . nativeElement ;
196
- } ) ;
197
189
198
190
it ( 'should fill the text field when an option is selected' , ( ) => {
199
191
fixture . componentInstance . trigger . openPanel ( ) ;
@@ -205,7 +197,7 @@ describe('MdAutocomplete', () => {
205
197
fixture . detectChanges ( ) ;
206
198
207
199
expect ( input . value )
208
- . toContain ( 'California' , `Expected text field to be filled with selected value.` ) ;
200
+ . toContain ( 'California' , `Expected text field to fill with selected value.` ) ;
209
201
} ) ;
210
202
211
203
it ( 'should mark the autocomplete control as dirty when an option is selected' , ( ) => {
@@ -236,6 +228,153 @@ describe('MdAutocomplete', () => {
236
228
237
229
} ) ;
238
230
231
+ describe ( 'keyboard events' , ( ) => {
232
+ let DOWN_ARROW_EVENT : KeyboardEvent ;
233
+ let ENTER_EVENT : KeyboardEvent ;
234
+
235
+ beforeEach ( ( ) => {
236
+ DOWN_ARROW_EVENT = new FakeKeyboardEvent ( DOWN_ARROW ) as KeyboardEvent ;
237
+ ENTER_EVENT = new FakeKeyboardEvent ( ENTER ) as KeyboardEvent ;
238
+ } ) ;
239
+
240
+ it ( 'should should not focus the option when DOWN key is pressed' , ( ) => {
241
+ fixture . componentInstance . trigger . openPanel ( ) ;
242
+ fixture . detectChanges ( ) ;
243
+
244
+ spyOn ( fixture . componentInstance . options . first , 'focus' ) ;
245
+
246
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
247
+ expect ( fixture . componentInstance . options . first . focus ) . not . toHaveBeenCalled ( ) ;
248
+ } ) ;
249
+
250
+ it ( 'should set the active item to the first option when DOWN key is pressed' , async ( ( ) => {
251
+ fixture . componentInstance . trigger . openPanel ( ) ;
252
+ fixture . detectChanges ( ) ;
253
+
254
+ const optionEls =
255
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
256
+
257
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
258
+
259
+ fixture . whenStable ( ) . then ( ( ) => {
260
+ fixture . detectChanges ( ) ;
261
+ expect ( fixture . componentInstance . trigger . activeOption )
262
+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
263
+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
264
+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
265
+
266
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
267
+
268
+ fixture . whenStable ( ) . then ( ( ) => {
269
+ fixture . detectChanges ( ) ;
270
+ expect ( fixture . componentInstance . trigger . activeOption )
271
+ . toBe ( fixture . componentInstance . options . toArray ( ) [ 1 ] ,
272
+ 'Expected second option to be active.' ) ;
273
+ expect ( optionEls [ 0 ] . classList ) . not . toContain ( 'md-active' ) ;
274
+ expect ( optionEls [ 1 ] . classList ) . toContain ( 'md-active' ) ;
275
+ } ) ;
276
+ } ) ;
277
+ } ) ) ;
278
+
279
+ it ( 'should set the active item properly after filtering' , async ( ( ) => {
280
+ fixture . componentInstance . trigger . openPanel ( ) ;
281
+ fixture . detectChanges ( ) ;
282
+
283
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
284
+
285
+ fixture . whenStable ( ) . then ( ( ) => {
286
+ fixture . detectChanges ( ) ;
287
+ input . value = 'o' ;
288
+ dispatchEvent ( 'input' , input ) ;
289
+ fixture . detectChanges ( ) ;
290
+
291
+ const optionEls =
292
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
293
+
294
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
295
+
296
+ fixture . whenStable ( ) . then ( ( ) => {
297
+ fixture . detectChanges ( ) ;
298
+ expect ( fixture . componentInstance . trigger . activeOption )
299
+ . toBe ( fixture . componentInstance . options . first , 'Expected first option to be active.' ) ;
300
+ expect ( optionEls [ 0 ] . classList ) . toContain ( 'md-active' ) ;
301
+ expect ( optionEls [ 1 ] . classList ) . not . toContain ( 'md-active' ) ;
302
+ } ) ;
303
+ } ) ;
304
+ } ) ) ;
305
+
306
+ it ( 'should fill the text field when an option is selected with ENTER' , ( ) => {
307
+ fixture . componentInstance . trigger . openPanel ( ) ;
308
+ fixture . detectChanges ( ) ;
309
+
310
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
311
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
312
+ fixture . detectChanges ( ) ;
313
+
314
+ expect ( input . value )
315
+ . toContain ( 'Alabama' , `Expected text field to fill with selected value on ENTER.` ) ;
316
+ } ) ;
317
+
318
+ it ( 'should fill the text field, not select an option, when SPACE is entered' , ( ) => {
319
+ fixture . componentInstance . trigger . openPanel ( ) ;
320
+ fixture . detectChanges ( ) ;
321
+
322
+ input . value = 'New' ;
323
+ dispatchEvent ( 'input' , input ) ;
324
+ fixture . detectChanges ( ) ;
325
+
326
+ const SPACE_EVENT = new FakeKeyboardEvent ( SPACE ) as KeyboardEvent ;
327
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
328
+ fixture . componentInstance . trigger . _handleKeydown ( SPACE_EVENT ) ;
329
+ fixture . detectChanges ( ) ;
330
+
331
+ expect ( input . value )
332
+ . not . toContain ( 'New York' , `Expected option not to be selected on SPACE.` ) ;
333
+ } ) ;
334
+
335
+ it ( 'should mark the control as dirty when an option is selected from the keyboard' , ( ) => {
336
+ fixture . componentInstance . trigger . openPanel ( ) ;
337
+ fixture . detectChanges ( ) ;
338
+
339
+ expect ( fixture . componentInstance . stateCtrl . dirty )
340
+ . toBe ( false , `Expected control to start out pristine.` ) ;
341
+
342
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
343
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
344
+ fixture . detectChanges ( ) ;
345
+
346
+ expect ( fixture . componentInstance . stateCtrl . dirty )
347
+ . toBe ( true , `Expected control to become dirty when option was selected by ENTER.` ) ;
348
+ } ) ;
349
+
350
+ it ( 'should open the panel again when typing after making a selection' , async ( ( ) => {
351
+ fixture . componentInstance . trigger . openPanel ( ) ;
352
+ fixture . detectChanges ( ) ;
353
+
354
+ fixture . componentInstance . trigger . _handleKeydown ( DOWN_ARROW_EVENT ) ;
355
+ fixture . componentInstance . trigger . _handleKeydown ( ENTER_EVENT ) ;
356
+ fixture . detectChanges ( ) ;
357
+
358
+ fixture . whenStable ( ) . then ( ( ) => {
359
+ expect ( fixture . componentInstance . trigger . panelOpen )
360
+ . toBe ( false , `Expected panel state to read closed after ENTER key.` ) ;
361
+ expect ( overlayContainerElement . textContent )
362
+ . toEqual ( '' , `Expected panel to close after ENTER key.` ) ;
363
+
364
+ // 65 is the keycode for "a"
365
+ const A_KEY = new FakeKeyboardEvent ( 65 ) as KeyboardEvent ;
366
+ fixture . componentInstance . trigger . _handleKeydown ( A_KEY ) ;
367
+ fixture . detectChanges ( ) ;
368
+
369
+ expect ( fixture . componentInstance . trigger . panelOpen )
370
+ . toBe ( true , `Expected panel state to read open when typing in input.` ) ;
371
+ expect ( overlayContainerElement . textContent )
372
+ . toContain ( 'Alabama' , `Expected panel to display when typing in input.` ) ;
373
+ } ) ;
374
+ } ) ) ;
375
+
376
+ } ) ;
377
+
239
378
} ) ;
240
379
241
380
@Component ( {
@@ -257,6 +396,7 @@ class SimpleAutocomplete implements OnDestroy {
257
396
valueSub : Subscription ;
258
397
259
398
@ViewChild ( MdAutocompleteTrigger ) trigger : MdAutocompleteTrigger ;
399
+ @ViewChildren ( MdOption ) options : QueryList < MdOption > ;
260
400
261
401
states = [
262
402
{ code : 'AL' , name : 'Alabama' } ,
@@ -301,4 +441,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
301
441
element . dispatchEvent ( event ) ;
302
442
}
303
443
444
+ /** This is a mock keyboard event to test keyboard events in the autocomplete. */
445
+ class FakeKeyboardEvent {
446
+ constructor ( public keyCode : number ) { }
447
+ preventDefault ( ) { }
448
+ }
449
+
450
+
304
451
0 commit comments