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,102 @@ 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 ( ) ;
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 ) . toContain ( 'Pizza' ) ;
230
+
231
+ trigger . click ( ) ;
232
+ fixture . detectChanges ( ) ;
233
+
234
+ const options =
235
+ overlayContainerElement . querySelectorAll ( 'md-option' ) as NodeListOf < HTMLElement > ;
236
+ expect ( options [ 1 ] . classList ) . toContain ( 'md-selected' ) ;
237
+ } ) ;
238
+
239
+ it ( 'should update the form value when the view changes' , ( ) => {
240
+ expect ( fixture . componentInstance . control . value ) . toEqual ( null ) ;
241
+
242
+ trigger . click ( ) ;
243
+ fixture . detectChanges ( ) ;
244
+
245
+ const option = overlayContainerElement . querySelector ( 'md-option' ) as HTMLElement ;
246
+ option . click ( ) ;
247
+ fixture . detectChanges ( ) ;
248
+
249
+ expect ( fixture . componentInstance . control . value ) . toEqual ( 'steak-0' ) ;
250
+ } ) ;
251
+
252
+ it ( 'should set the control to touched when the select is touched' , ( ) => {
253
+ expect ( fixture . componentInstance . control . touched ) . toEqual ( false ) ;
254
+
255
+ trigger . click ( ) ;
256
+ fixture . detectChanges ( ) ;
257
+ dispatchEvent ( 'blur' , trigger ) ;
258
+ expect ( fixture . componentInstance . control . touched ) . toEqual ( false ) ;
259
+
260
+ const backdrop =
261
+ overlayContainerElement . querySelector ( '.md-overlay-backdrop' ) as HTMLElement ;
262
+ backdrop . click ( ) ;
263
+ dispatchEvent ( 'blur' , trigger ) ;
264
+ fixture . detectChanges ( ) ;
265
+
266
+ expect ( fixture . componentInstance . control . touched ) . toEqual ( true ) ;
267
+ } ) ;
268
+
269
+ it ( 'should set the control to dirty when the select\'s value changes in the DOM' , ( ) => {
270
+ expect ( fixture . componentInstance . control . dirty ) . toEqual ( false ) ;
271
+
272
+ trigger . click ( ) ;
273
+ fixture . detectChanges ( ) ;
274
+
275
+ const option = overlayContainerElement . querySelector ( 'md-option' ) as HTMLElement ;
276
+ option . click ( ) ;
277
+ fixture . detectChanges ( ) ;
278
+
279
+ expect ( fixture . componentInstance . control . dirty ) . toEqual ( true ) ;
280
+ } ) ;
281
+
282
+ it ( 'should not set the control to dirty when the value changes programmatically' , ( ) => {
283
+ expect ( fixture . componentInstance . control . dirty ) . toEqual ( false ) ;
284
+
285
+ fixture . componentInstance . control . setValue ( 'pizza-1' ) ;
286
+
287
+ expect ( fixture . componentInstance . control . dirty ) . toEqual ( false ) ;
288
+ } ) ;
289
+
290
+
291
+ it ( 'should set an asterisk after the placeholder if the control is required' , ( ) => {
292
+ const placeholder =
293
+ fixture . debugElement . query ( By . css ( '.md-select-placeholder' ) ) . nativeElement ;
294
+ const initialContent = getComputedStyle ( placeholder , '::after' ) . getPropertyValue ( 'content' ) ;
295
+
296
+ // must support both default cases to work in all browsers in Saucelabs
297
+ expect ( initialContent === 'none' || initialContent === '' ) . toBe ( true ) ;
298
+
299
+ fixture . componentInstance . isRequired = true ;
300
+ fixture . detectChanges ( ) ;
301
+ expect ( getComputedStyle ( placeholder , '::after' ) . getPropertyValue ( 'content' ) ) . toContain ( '*' ) ;
302
+ } ) ;
303
+
304
+ } ) ;
305
+
209
306
describe ( 'animations' , ( ) => {
210
307
let fixture : ComponentFixture < BasicSelect > ;
211
308
let trigger : HTMLElement ;
@@ -278,22 +375,40 @@ describe('MdSelect', () => {
278
375
} ) ;
279
376
280
377
describe ( 'for select' , ( ) => {
281
- let select : DebugElement ;
378
+ let select : HTMLElement ;
282
379
283
380
beforeEach ( ( ) => {
284
- select = fixture . debugElement . query ( By . css ( 'md-select' ) ) ;
381
+ select = fixture . debugElement . query ( By . css ( 'md-select' ) ) . nativeElement ;
285
382
} ) ;
286
383
287
384
it ( 'should set the role of the select to listbox' , ( ) => {
288
- expect ( select . nativeElement . getAttribute ( 'role' ) ) . toEqual ( 'listbox' ) ;
385
+ expect ( select . getAttribute ( 'role' ) ) . toEqual ( 'listbox' ) ;
289
386
} ) ;
290
387
291
388
it ( 'should set the aria label of the select to the placeholder' , ( ) => {
292
- expect ( select . nativeElement . getAttribute ( 'aria-label' ) ) . toEqual ( 'Food' ) ;
389
+ expect ( select . getAttribute ( 'aria-label' ) ) . toEqual ( 'Food' ) ;
293
390
} ) ;
294
391
295
392
it ( 'should set the tabindex of the select to 0' , ( ) => {
296
- expect ( select . nativeElement . getAttribute ( 'tabindex' ) ) . toEqual ( '0' ) ;
393
+ expect ( select . getAttribute ( 'tabindex' ) ) . toEqual ( '0' ) ;
394
+ } ) ;
395
+
396
+ it ( 'should set aria-required for required selects' , ( ) => {
397
+ expect ( select . getAttribute ( 'aria-required' ) ) . toEqual ( 'false' ) ;
398
+
399
+ fixture . componentInstance . isRequired = true ;
400
+ fixture . detectChanges ( ) ;
401
+
402
+ expect ( select . getAttribute ( 'aria-required' ) ) . toEqual ( 'true' ) ;
403
+ } ) ;
404
+
405
+ it ( 'should set aria-invalid for selects that are invalid' , ( ) => {
406
+ expect ( select . getAttribute ( 'aria-invalid' ) ) . toEqual ( 'false' ) ;
407
+
408
+ fixture . componentInstance . isRequired = true ;
409
+ fixture . detectChanges ( ) ;
410
+
411
+ expect ( select . getAttribute ( 'aria-invalid' ) ) . toEqual ( 'true' ) ;
297
412
} ) ;
298
413
299
414
} ) ;
@@ -347,19 +462,34 @@ describe('MdSelect', () => {
347
462
@Component ( {
348
463
selector : 'basic-select' ,
349
464
template : `
350
- <md-select placeholder="Food">
351
- <md-option *ngFor="let food of foods">{{ food.viewValue }}</md-option>
465
+ <md-select placeholder="Food" [formControl]="control" [required]="isRequired" >
466
+ <md-option *ngFor="let food of foods" [value]="food.value" >{{ food.viewValue }}</md-option>
352
467
</md-select>
353
468
`
354
469
} )
355
470
class BasicSelect {
356
471
foods = [
357
- { viewValue : 'Steak' } ,
358
- { viewValue : 'Pizza' } ,
359
- { viewValue : 'Tacos' } ,
472
+ { value : 'steak-0' , viewValue : 'Steak' } ,
473
+ { value : 'pizza-1' , viewValue : 'Pizza' } ,
474
+ { value : 'tacos-2' , viewValue : 'Tacos' } ,
360
475
] ;
476
+ control = new FormControl ( ) ;
477
+ isRequired : boolean ;
361
478
362
479
@ViewChild ( MdSelect ) select : MdSelect ;
363
480
@ViewChildren ( MdOption ) options : QueryList < MdOption > ;
481
+ }
364
482
483
+ /**
484
+ * TODO: Move this to core testing utility until Angular has event faking
485
+ * support.
486
+ *
487
+ * Dispatches an event from an element.
488
+ * @param eventName Name of the event
489
+ * @param element The element from which the event will be dispatched.
490
+ */
491
+ function dispatchEvent ( eventName : string , element : HTMLElement ) : void {
492
+ let event = document . createEvent ( 'Event' ) ;
493
+ event . initEvent ( eventName , true , true ) ;
494
+ element . dispatchEvent ( event ) ;
365
495
}
0 commit comments