1
1
import { TestBed , async , ComponentFixture } from '@angular/core/testing' ;
2
2
import { By } from '@angular/platform-browser' ;
3
- import { Component , DebugElement , QueryList , ViewChild , ViewChildren } from '@angular/core' ;
3
+ import { Component , QueryList , ViewChild , ViewChildren } from '@angular/core' ;
4
4
import { MdSelectModule } from './index' ;
5
5
import { OverlayContainer } from '../core/overlay/overlay-container' ;
6
6
import { MdSelect } from './select' ;
7
7
import { MdOption } from './option' ;
8
8
import { Dir } from '../core/rtl/dir' ;
9
+ import { FormControl , ReactiveFormsModule } from '@angular/forms' ;
9
10
10
11
describe ( 'MdSelect' , ( ) => {
11
12
let overlayContainerElement : HTMLElement ;
12
13
let dir : { value : string } ;
13
14
14
15
beforeEach ( async ( ( ) => {
15
16
TestBed . configureTestingModule ( {
16
- imports : [ MdSelectModule . forRoot ( ) ] ,
17
+ imports : [ MdSelectModule . forRoot ( ) , ReactiveFormsModule ] ,
17
18
declarations : [ BasicSelect ] ,
18
19
providers : [
19
20
{ provide : OverlayContainer , useFactory : ( ) => {
@@ -190,7 +191,7 @@ describe('MdSelect', () => {
190
191
} ) ) ;
191
192
192
193
it ( 'should select an option that was added after initialization' , ( ) => {
193
- fixture . componentInstance . foods . push ( { viewValue : 'Pasta' } ) ;
194
+ fixture . componentInstance . foods . push ( { viewValue : 'Pasta' , value : 'pasta-3' } ) ;
194
195
trigger . click ( ) ;
195
196
fixture . detectChanges ( ) ;
196
197
@@ -206,6 +207,114 @@ describe('MdSelect', () => {
206
207
207
208
} ) ;
208
209
210
+ describe ( 'forms integration' , ( ) => {
211
+ let fixture : ComponentFixture < BasicSelect > ;
212
+ let trigger : HTMLElement ;
213
+
214
+ beforeEach ( ( ) => {
215
+ fixture = TestBed . createComponent ( BasicSelect ) ;
216
+ fixture . detectChanges ( ) ;
217
+
218
+ trigger = fixture . debugElement . query ( By . css ( '.md-select-trigger' ) ) . nativeElement ;
219
+ } ) ;
220
+
221
+ it ( 'should set the view value from the form' , ( ) => {
222
+ let value = fixture . debugElement . query ( By . css ( '.md-select-value' ) ) ;
223
+ expect ( value ) . toBeNull ( 'Expected trigger to start with empty value.' ) ;
224
+
225
+ fixture . componentInstance . control . setValue ( 'pizza-1' ) ;
226
+ fixture . detectChanges ( ) ;
227
+
228
+ value = fixture . debugElement . query ( By . css ( '.md-select-value' ) ) ;
229
+ expect ( value . nativeElement . textContent )
230
+ . toContain ( 'Pizza' , `Expected trigger to be populated by the control's new value.` ) ;
231
+
232
+ trigger . click ( ) ;
233
+ fixture . detectChanges ( ) ;
234
+
235
+ const options =
236
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
237
+ expect ( options [ 1 ] . classList )
238
+ . toContain ( 'md-selected' , `Expected option with the control's new value to be selected.` ) ;
239
+ } ) ;
240
+
241
+ it ( 'should update the form value when the view changes' , ( ) => {
242
+ expect ( fixture . componentInstance . control . value )
243
+ . toEqual ( null , `Expected the control's value to be null initially.` ) ;
244
+
245
+ trigger . click ( ) ;
246
+ fixture . detectChanges ( ) ;
247
+
248
+ const option = overlayContainerElement . querySelector ( 'md-option' ) as HTMLElement ;
249
+ option . click ( ) ;
250
+ fixture . detectChanges ( ) ;
251
+
252
+ expect ( fixture . componentInstance . control . value )
253
+ . toEqual ( 'steak-0' , `Expected control's value to be set to the new option.` ) ;
254
+ } ) ;
255
+
256
+ it ( 'should set the control to touched when the select is touched' , ( ) => {
257
+ expect ( fixture . componentInstance . control . touched )
258
+ . toEqual ( false , `Expected the control to start off as untouched.` ) ;
259
+
260
+ trigger . click ( ) ;
261
+ dispatchEvent ( 'blur' , trigger ) ;
262
+ fixture . detectChanges ( ) ;
263
+ expect ( fixture . componentInstance . control . touched )
264
+ . toEqual ( false , `Expected the control to stay untouched when menu opened.` ) ;
265
+
266
+ const backdrop =
267
+ overlayContainerElement . querySelector ( '.md-overlay-backdrop' ) as HTMLElement ;
268
+ backdrop . click ( ) ;
269
+ dispatchEvent ( 'blur' , trigger ) ;
270
+ fixture . detectChanges ( ) ;
271
+ expect ( fixture . componentInstance . control . touched )
272
+ . toEqual ( true , `Expected the control to be touched as soon as focus left the select.` ) ;
273
+ } ) ;
274
+
275
+ it ( 'should set the control to dirty when the select\'s value changes in the DOM' , ( ) => {
276
+ expect ( fixture . componentInstance . control . dirty )
277
+ . toEqual ( false , `Expected control to start out pristine.` ) ;
278
+
279
+ trigger . click ( ) ;
280
+ fixture . detectChanges ( ) ;
281
+
282
+ const option = overlayContainerElement . querySelector ( 'md-option' ) as HTMLElement ;
283
+ option . click ( ) ;
284
+ fixture . detectChanges ( ) ;
285
+
286
+ expect ( fixture . componentInstance . control . dirty )
287
+ . toEqual ( true , `Expected control to be dirty after value was changed by user.` ) ;
288
+ } ) ;
289
+
290
+ it ( 'should not set the control to dirty when the value changes programmatically' , ( ) => {
291
+ expect ( fixture . componentInstance . control . dirty )
292
+ . toEqual ( false , `Expected control to start out pristine.` ) ;
293
+
294
+ fixture . componentInstance . control . setValue ( 'pizza-1' ) ;
295
+
296
+ expect ( fixture . componentInstance . control . dirty )
297
+ . toEqual ( false , `Expected control to stay pristine after programmatic change.` ) ;
298
+ } ) ;
299
+
300
+
301
+ it ( 'should set an asterisk after the placeholder if the control is required' , ( ) => {
302
+ const placeholder =
303
+ fixture . debugElement . query ( By . css ( '.md-select-placeholder' ) ) . nativeElement ;
304
+ const initialContent = getComputedStyle ( placeholder , '::after' ) . getPropertyValue ( 'content' ) ;
305
+
306
+ // must support both default cases to work in all browsers in Saucelabs
307
+ expect ( initialContent === 'none' || initialContent === '' )
308
+ . toBe ( true , `Expected placeholder not to have an asterisk, as control was not required.` ) ;
309
+
310
+ fixture . componentInstance . isRequired = true ;
311
+ fixture . detectChanges ( ) ;
312
+ expect ( getComputedStyle ( placeholder , '::after' ) . getPropertyValue ( 'content' ) )
313
+ . toContain ( '*' , `Expected placeholder to have an asterisk, as control was required.` ) ;
314
+ } ) ;
315
+
316
+ } ) ;
317
+
209
318
describe ( 'animations' , ( ) => {
210
319
let fixture : ComponentFixture < BasicSelect > ;
211
320
let trigger : HTMLElement ;
@@ -278,22 +387,44 @@ describe('MdSelect', () => {
278
387
} ) ;
279
388
280
389
describe ( 'for select' , ( ) => {
281
- let select : DebugElement ;
390
+ let select : HTMLElement ;
282
391
283
392
beforeEach ( ( ) => {
284
- select = fixture . debugElement . query ( By . css ( 'md-select' ) ) ;
393
+ select = fixture . debugElement . query ( By . css ( 'md-select' ) ) . nativeElement ;
285
394
} ) ;
286
395
287
396
it ( 'should set the role of the select to listbox' , ( ) => {
288
- expect ( select . nativeElement . getAttribute ( 'role' ) ) . toEqual ( 'listbox' ) ;
397
+ expect ( select . getAttribute ( 'role' ) ) . toEqual ( 'listbox' ) ;
289
398
} ) ;
290
399
291
400
it ( 'should set the aria label of the select to the placeholder' , ( ) => {
292
- expect ( select . nativeElement . getAttribute ( 'aria-label' ) ) . toEqual ( 'Food' ) ;
401
+ expect ( select . getAttribute ( 'aria-label' ) ) . toEqual ( 'Food' ) ;
293
402
} ) ;
294
403
295
404
it ( 'should set the tabindex of the select to 0' , ( ) => {
296
- expect ( select . nativeElement . getAttribute ( 'tabindex' ) ) . toEqual ( '0' ) ;
405
+ expect ( select . getAttribute ( 'tabindex' ) ) . toEqual ( '0' ) ;
406
+ } ) ;
407
+
408
+ it ( 'should set aria-required for required selects' , ( ) => {
409
+ expect ( select . getAttribute ( 'aria-required' ) )
410
+ . toEqual ( 'false' , `Expected aria-required attr to be false for normal selects.` ) ;
411
+
412
+ fixture . componentInstance . isRequired = true ;
413
+ fixture . detectChanges ( ) ;
414
+
415
+ expect ( select . getAttribute ( 'aria-required' ) )
416
+ . toEqual ( 'true' , `Expected aria-required attr to be true for required selects.` ) ;
417
+ } ) ;
418
+
419
+ it ( 'should set aria-invalid for selects that are invalid' , ( ) => {
420
+ expect ( select . getAttribute ( 'aria-invalid' ) )
421
+ . toEqual ( 'false' , `Expected aria-invalid attr to be false for valid selects.` ) ;
422
+
423
+ fixture . componentInstance . isRequired = true ;
424
+ fixture . detectChanges ( ) ;
425
+
426
+ expect ( select . getAttribute ( 'aria-invalid' ) )
427
+ . toEqual ( 'true' , `Expected aria-invalid attr to be true for invalid selects.` ) ;
297
428
} ) ;
298
429
299
430
} ) ;
@@ -347,19 +478,34 @@ describe('MdSelect', () => {
347
478
@Component ( {
348
479
selector : 'basic-select' ,
349
480
template : `
350
- <md-select placeholder="Food">
351
- <md-option *ngFor="let food of foods">{{ food.viewValue }}</md-option>
481
+ <md-select placeholder="Food" [formControl]="control" [required]="isRequired" >
482
+ <md-option *ngFor="let food of foods" [value]="food.value" >{{ food.viewValue }}</md-option>
352
483
</md-select>
353
484
`
354
485
} )
355
486
class BasicSelect {
356
487
foods = [
357
- { viewValue : 'Steak' } ,
358
- { viewValue : 'Pizza' } ,
359
- { viewValue : 'Tacos' } ,
488
+ { value : 'steak-0' , viewValue : 'Steak' } ,
489
+ { value : 'pizza-1' , viewValue : 'Pizza' } ,
490
+ { value : 'tacos-2' , viewValue : 'Tacos' } ,
360
491
] ;
492
+ control = new FormControl ( ) ;
493
+ isRequired : boolean ;
361
494
362
495
@ViewChild ( MdSelect ) select : MdSelect ;
363
496
@ViewChildren ( MdOption ) options : QueryList < MdOption > ;
497
+ }
364
498
499
+ /**
500
+ * TODO: Move this to core testing utility until Angular has event faking
501
+ * support.
502
+ *
503
+ * Dispatches an event from an element.
504
+ * @param eventName Name of the event
505
+ * @param element The element from which the event will be dispatched.
506
+ */
507
+ function dispatchEvent ( eventName : string , element : HTMLElement ) : void {
508
+ let event = document . createEvent ( 'Event' ) ;
509
+ event . initEvent ( eventName , true , true ) ;
510
+ element . dispatchEvent ( event ) ;
365
511
}
0 commit comments