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,145 @@ 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' , ( ) => {
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' , async ( ( ) => {
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
+ fixture . whenStable ( ) . then ( ( ) => {
331
+ expect ( fixture . componentInstance . trigger . panelOpen )
332
+ . toBe ( false , `Expected panel state to read closed after ENTER key.` ) ;
333
+ expect ( overlayContainerElement . textContent )
334
+ . toEqual ( '' , `Expected panel to close after ENTER key.` ) ;
335
+
336
+ // 65 is the keycode for "a"
337
+ const A_KEY = new FakeKeyboardEvent ( 65 ) as KeyboardEvent ;
338
+ fixture . componentInstance . trigger . _handleKeydown ( A_KEY ) ;
339
+ fixture . detectChanges ( ) ;
340
+
341
+ expect ( fixture . componentInstance . trigger . panelOpen )
342
+ . toBe ( true , `Expected panel state to read open when typing in input.` ) ;
343
+ expect ( overlayContainerElement . textContent )
344
+ . toContain ( 'Alabama' , `Expected panel to display when typing in input.` ) ;
345
+ } ) ;
346
+ } ) ) ;
347
+
348
+ } ) ;
349
+
219
350
} ) ;
220
351
221
352
@Component ( {
@@ -237,6 +368,7 @@ class SimpleAutocomplete implements OnDestroy {
237
368
valueSub : Subscription ;
238
369
239
370
@ViewChild ( MdAutocompleteTrigger ) trigger : MdAutocompleteTrigger ;
371
+ @ViewChildren ( MdOption ) options : QueryList < MdOption > ;
240
372
241
373
states = [
242
374
{ code : 'AL' , name : 'Alabama' } ,
@@ -281,4 +413,11 @@ function dispatchEvent(eventName: string, element: HTMLElement): void {
281
413
element . dispatchEvent ( event ) ;
282
414
}
283
415
416
+ /** This is a mock keyboard event to test keyboard events in the autocomplete. */
417
+ class FakeKeyboardEvent {
418
+ constructor ( public keyCode : number ) { }
419
+ preventDefault ( ) { }
420
+ }
421
+
422
+
284
423
0 commit comments