@@ -2,12 +2,18 @@ import {DOWN_ARROW, SPACE, UP_ARROW} from '@angular/cdk/keycodes';
22import { Platform } from '@angular/cdk/platform' ;
33import { createKeyboardEvent , dispatchFakeEvent } from '@angular/cdk/testing' ;
44import { Component , DebugElement } from '@angular/core' ;
5- import { async , ComponentFixture , inject , TestBed } from '@angular/core/testing' ;
5+ import { async , ComponentFixture , fakeAsync , inject , TestBed , tick } from '@angular/core/testing' ;
66import { By } from '@angular/platform-browser' ;
7- import { MatListModule , MatListOption , MatSelectionList , MatListOptionChange } from './index' ;
8-
9-
10- describe ( 'MatSelectionList' , ( ) => {
7+ import {
8+ MatListModule ,
9+ MatListOption ,
10+ MatListOptionChange ,
11+ MatSelectionList ,
12+ MatSelectionListChange
13+ } from './index' ;
14+ import { FormControl , FormsModule , NgModel , ReactiveFormsModule } from '@angular/forms' ;
15+
16+ describe ( 'MatSelectionList without forms' , ( ) => {
1117 describe ( 'with list option' , ( ) => {
1218 let fixture : ComponentFixture < SelectionListWithListOptions > ;
1319 let listOptions : DebugElement [ ] ;
@@ -61,6 +67,44 @@ describe('MatSelectionList', () => {
6167 } ) ;
6268 } ) ;
6369
70+ it ( 'should not emit a selectionChange event if an option changed programmatically' , ( ) => {
71+ spyOn ( fixture . componentInstance , 'onValueChange' ) ;
72+
73+ expect ( fixture . componentInstance . onValueChange ) . toHaveBeenCalledTimes ( 0 ) ;
74+
75+ listOptions [ 2 ] . componentInstance . toggle ( ) ;
76+ fixture . detectChanges ( ) ;
77+
78+ expect ( fixture . componentInstance . onValueChange ) . toHaveBeenCalledTimes ( 0 ) ;
79+ } ) ;
80+
81+ it ( 'should emit a selectionChange event if an option got clicked' , ( ) => {
82+ spyOn ( fixture . componentInstance , 'onValueChange' ) ;
83+
84+ expect ( fixture . componentInstance . onValueChange ) . toHaveBeenCalledTimes ( 0 ) ;
85+
86+ dispatchFakeEvent ( listOptions [ 2 ] . nativeElement , 'click' ) ;
87+ fixture . detectChanges ( ) ;
88+
89+ expect ( fixture . componentInstance . onValueChange ) . toHaveBeenCalledTimes ( 1 ) ;
90+ } ) ;
91+
92+ it ( 'should emit a deprecated selectionChange event on the list option that got clicked' , ( ) => {
93+ const optionInstance = listOptions [ 2 ] . componentInstance as MatListOption ;
94+ let lastChangeEvent : MatListOptionChange | null = null ;
95+
96+ optionInstance . selectionChange . subscribe ( ev => lastChangeEvent = ev ) ;
97+
98+ expect ( lastChangeEvent ) . toBeNull ( ) ;
99+
100+ dispatchFakeEvent ( listOptions [ 2 ] . nativeElement , 'click' ) ;
101+ fixture . detectChanges ( ) ;
102+
103+ expect ( lastChangeEvent ) . not . toBeNull ( ) ;
104+ expect ( lastChangeEvent ! . source ) . toBe ( optionInstance ) ;
105+ expect ( lastChangeEvent ! . selected ) . toBe ( true ) ;
106+ } ) ;
107+
64108 it ( 'should be able to dispatch one selected item' , ( ) => {
65109 let testListItem = listOptions [ 2 ] . injector . get < MatListOption > ( MatListOption ) ;
66110 let selectList =
@@ -480,90 +524,167 @@ describe('MatSelectionList', () => {
480524 expect ( listItemContent . nativeElement . classList ) . toContain ( 'mat-list-item-content-reverse' ) ;
481525 } ) ;
482526 } ) ;
527+ } ) ;
483528
529+ describe ( 'MatSelectionList with forms' , ( ) => {
484530
485- describe ( 'with multiple values' , ( ) => {
486- let fixture : ComponentFixture < SelectionListWithMultipleValues > ;
487- let listOption : DebugElement [ ] ;
488- let listItemEl : DebugElement ;
489- let selectionList : DebugElement ;
531+ beforeEach ( async ( ( ) => {
532+ TestBed . configureTestingModule ( {
533+ imports : [ MatListModule , FormsModule , ReactiveFormsModule ] ,
534+ declarations : [
535+ SelectionListWithModel ,
536+ SelectionListWithFormControl
537+ ]
538+ } ) ;
490539
491- beforeEach ( async ( ( ) => {
492- TestBed . configureTestingModule ( {
493- imports : [ MatListModule ] ,
494- declarations : [
495- SelectionListWithMultipleValues
496- ] ,
497- } ) ;
540+ TestBed . compileComponents ( ) ;
541+ } ) ) ;
498542
499- TestBed . compileComponents ( ) ;
543+ describe ( 'and ngModel' , ( ) => {
544+ let fixture : ComponentFixture < SelectionListWithModel > ;
545+ let selectionListDebug : DebugElement ;
546+ let selectionList : MatSelectionList ;
547+ let listOptions : MatListOption [ ] ;
548+ let ngModel : NgModel ;
549+
550+ beforeEach ( ( ) => {
551+ fixture = TestBed . createComponent ( SelectionListWithModel ) ;
552+ fixture . detectChanges ( ) ;
553+
554+ selectionListDebug = fixture . debugElement . query ( By . directive ( MatSelectionList ) ) ;
555+ selectionList = selectionListDebug . componentInstance ;
556+ ngModel = selectionListDebug . injector . get < NgModel > ( NgModel ) ;
557+ listOptions = fixture . debugElement . queryAll ( By . directive ( MatListOption ) )
558+ . map ( optionDebugEl => optionDebugEl . componentInstance ) ;
559+ } ) ;
560+
561+ it ( 'should update the model if an option got selected programmatically' , fakeAsync ( ( ) => {
562+ expect ( fixture . componentInstance . selectedOptions . length )
563+ . toBe ( 0 , 'Expected no options to be selected by default' ) ;
564+
565+ listOptions [ 0 ] . toggle ( ) ;
566+ fixture . detectChanges ( ) ;
567+
568+ tick ( ) ;
569+
570+ expect ( fixture . componentInstance . selectedOptions . length )
571+ . toBe ( 1 , 'Expected first list option to be selected' ) ;
500572 } ) ) ;
501573
502- beforeEach ( async ( ( ) => {
503- fixture = TestBed . createComponent ( SelectionListWithMultipleValues ) ;
504- listOption = fixture . debugElement . queryAll ( By . directive ( MatListOption ) ) ;
505- listItemEl = fixture . debugElement . query ( By . css ( '.mat-list-item' ) ) ;
506- selectionList = fixture . debugElement . query ( By . directive ( MatSelectionList ) ) ;
574+ it ( 'should update the model if an option got clicked' , fakeAsync ( ( ) => {
575+ expect ( fixture . componentInstance . selectedOptions . length )
576+ . toBe ( 0 , 'Expected no options to be selected by default' ) ;
577+
578+ dispatchFakeEvent ( listOptions [ 0 ] . _getHostElement ( ) , 'click' ) ;
507579 fixture . detectChanges ( ) ;
580+
581+ tick ( ) ;
582+
583+ expect ( fixture . componentInstance . selectedOptions . length )
584+ . toBe ( 1 , 'Expected first list option to be selected' ) ;
508585 } ) ) ;
509586
510- it ( 'should have a value for each item' , ( ) => {
511- expect ( listOption [ 0 ] . componentInstance . value ) . toBe ( 1 ) ;
512- expect ( listOption [ 1 ] . componentInstance . value ) . toBe ( 'a' ) ;
513- expect ( listOption [ 2 ] . componentInstance . value ) . toBe ( true ) ;
514- } ) ;
587+ it ( 'should update the options if a model value is set' , fakeAsync ( ( ) => {
588+ expect ( fixture . componentInstance . selectedOptions . length )
589+ . toBe ( 0 , 'Expected no options to be selected by default' ) ;
515590
516- } ) ;
591+ fixture . componentInstance . selectedOptions = [ 'opt3' ] ;
592+ fixture . detectChanges ( ) ;
517593
518- describe ( 'with option selected events' , ( ) => {
519- let fixture : ComponentFixture < SelectionListWithOptionEvents > ;
520- let testComponent : SelectionListWithOptionEvents ;
521- let listOption : DebugElement [ ] ;
522- let selectionList : DebugElement ;
594+ tick ( ) ;
523595
524- beforeEach ( async ( ( ) => {
525- TestBed . configureTestingModule ( {
526- imports : [ MatListModule ] ,
527- declarations : [
528- SelectionListWithOptionEvents
529- ] ,
530- } ) ;
596+ expect ( fixture . componentInstance . selectedOptions . length )
597+ . toBe ( 1 , 'Expected first list option to be selected' ) ;
598+ } ) ) ;
531599
532- TestBed . compileComponents ( ) ;
600+ it ( 'should set the selection-list to touched on blur' , fakeAsync ( ( ) => {
601+ expect ( ngModel . touched )
602+ . toBe ( false , 'Expected the selection-list to be untouched by default.' ) ;
603+
604+ dispatchFakeEvent ( selectionListDebug . nativeElement , 'blur' ) ;
605+ fixture . detectChanges ( ) ;
606+
607+ tick ( ) ;
608+
609+ expect ( ngModel . touched ) . toBe ( true , 'Expected the selection-list to be touched after blur' ) ;
533610 } ) ) ;
534611
535- beforeEach ( async ( ( ) => {
536- fixture = TestBed . createComponent ( SelectionListWithOptionEvents ) ;
537- testComponent = fixture . debugElement . componentInstance ;
538- listOption = fixture . debugElement . queryAll ( By . directive ( MatListOption ) ) ;
539- selectionList = fixture . debugElement . query ( By . directive ( MatSelectionList ) ) ;
612+ it ( 'should be pristine by default' , fakeAsync ( ( ) => {
613+ fixture = TestBed . createComponent ( SelectionListWithModel ) ;
614+ fixture . componentInstance . selectedOptions = [ 'opt2' ] ;
615+ fixture . detectChanges ( ) ;
616+
617+ ngModel =
618+ fixture . debugElement . query ( By . directive ( MatSelectionList ) ) . injector . get < NgModel > ( NgModel ) ;
619+ listOptions = fixture . debugElement . queryAll ( By . directive ( MatListOption ) )
620+ . map ( optionDebugEl => optionDebugEl . componentInstance ) ;
621+
622+ // Flush the initial tick to ensure that every action from the ControlValueAccessor
623+ // happened before the actual test starts.
624+ tick ( ) ;
625+
626+ expect ( ngModel . pristine )
627+ . toBe ( true , 'Expected the selection-list to be pristine by default.' ) ;
628+
629+ listOptions [ 1 ] . toggle ( ) ;
540630 fixture . detectChanges ( ) ;
631+
632+ tick ( ) ;
633+
634+ expect ( ngModel . pristine )
635+ . toBe ( false , 'Expected the selection-list to be dirty after state change.' ) ;
541636 } ) ) ;
637+ } ) ;
638+
639+ describe ( 'and formControl' , ( ) => {
640+ let fixture : ComponentFixture < SelectionListWithFormControl > ;
641+ let selectionListDebug : DebugElement ;
642+ let selectionList : MatSelectionList ;
643+ let listOptions : MatListOption [ ] ;
542644
543- it ( 'should trigger the selected and deselected events when clicked in succession.' , ( ) => {
645+ beforeEach ( ( ) => {
646+ fixture = TestBed . createComponent ( SelectionListWithFormControl ) ;
647+ fixture . detectChanges ( ) ;
544648
545- let selected : boolean = false ;
649+ selectionListDebug = fixture . debugElement . query ( By . directive ( MatSelectionList ) ) ;
650+ selectionList = selectionListDebug . componentInstance ;
651+ listOptions = fixture . debugElement . queryAll ( By . directive ( MatListOption ) )
652+ . map ( optionDebugEl => optionDebugEl . componentInstance ) ;
653+ } ) ;
546654
547- spyOn ( testComponent , 'onOptionSelectionChange' )
548- . and . callFake ( ( event : MatListOptionChange ) => {
549- selected = event . selected ;
550- } ) ;
655+ it ( 'should be able to disable options from the control' , ( ) => {
656+ expect ( listOptions . every ( option => ! option . disabled ) )
657+ . toBe ( true , 'Expected every list option to be enabled.' ) ;
551658
552- listOption [ 0 ] . nativeElement . click ( ) ;
553- expect ( testComponent . onOptionSelectionChange ) . toHaveBeenCalledTimes ( 1 ) ;
554- expect ( selected ) . toBe ( true ) ;
659+ fixture . componentInstance . formControl . disable ( ) ;
660+ fixture . detectChanges ( ) ;
555661
556- listOption [ 0 ] . nativeElement . click ( ) ;
557- expect ( testComponent . onOptionSelectionChange ) . toHaveBeenCalledTimes ( 2 ) ;
558- expect ( selected ) . toBe ( false ) ;
662+ expect ( listOptions . every ( option => option . disabled ) )
663+ . toBe ( true , 'Expected every list option to be disabled.' ) ;
559664 } ) ;
560665
561- } ) ;
666+ it ( 'should be able to set the value through the form control' , ( ) => {
667+ expect ( listOptions . every ( option => ! option . selected ) )
668+ . toBe ( true , 'Expected every list option to be unselected.' ) ;
669+
670+ fixture . componentInstance . formControl . setValue ( [ 'opt2' , 'opt3' ] ) ;
671+ fixture . detectChanges ( ) ;
672+
673+ expect ( listOptions [ 1 ] . selected ) . toBe ( true , 'Expected second option to be selected.' ) ;
674+ expect ( listOptions [ 2 ] . selected ) . toBe ( true , 'Expected third option to be selected.' ) ;
562675
676+ fixture . componentInstance . formControl . setValue ( null ) ;
677+ fixture . detectChanges ( ) ;
678+
679+ expect ( listOptions . every ( option => ! option . selected ) )
680+ . toBe ( true , 'Expected every list option to be unselected.' ) ;
681+ } ) ;
682+ } ) ;
563683} ) ;
564684
685+
565686@Component ( { template : `
566- <mat-selection-list id="selection-list-1">
687+ <mat-selection-list id="selection-list-1" (selectionChange)="onValueChange($event)" >
567688 <mat-list-option checkboxPosition="before" disabled="true" value="inbox">
568689 Inbox (disabled selection-option)
569690 </mat-list-option>
@@ -580,6 +701,8 @@ describe('MatSelectionList', () => {
580701 </mat-selection-list>` } )
581702class SelectionListWithListOptions {
582703 showLastOption : boolean = true ;
704+
705+ onValueChange ( _change : MatSelectionListChange ) { }
583706}
584707
585708@Component ( { template : `
@@ -656,27 +779,27 @@ class SelectionListWithTabindexBinding {
656779 disabled : boolean ;
657780}
658781
659- @Component ( { template : `
660- <mat-selection-list id="selection-list-5">
661- <mat-list-option [value]="1" checkboxPosition="after">
662- 1
663- </mat-list-option>
664- <mat-list-option value="a" checkboxPosition="after">
665- a
666- </mat-list-option>
667- <mat-list-option [value]="true" checkboxPosition="after">
668- true
669- </mat-list-option>
670- </mat-selection-list>` } )
671- class SelectionListWithMultipleValues {
782+ @Component ( {
783+ template : `
784+ <mat-selection-list [(ngModel)]="selectedOptions">
785+ <mat-list-option value="opt1">Option 1</mat-list-option>
786+ <mat-list-option value="opt2">Option 2</mat-list-option>
787+ <mat-list-option value="opt3">Option 3</mat-list-option>
788+ </mat-selection-list>`
789+ } )
790+ class SelectionListWithModel {
791+ selectedOptions : string [ ] = [ ] ;
672792}
673793
674- @Component ( { template : `
675- <mat-selection-list id="selection-list-6">
676- <mat-list-option (selectionChange)="onOptionSelectionChange($event)">
677- Inbox
678- </mat-list-option>
679- </mat-selection-list>` } )
680- class SelectionListWithOptionEvents {
681- onOptionSelectionChange : ( event ?: MatListOptionChange ) => void = ( ) => { } ;
794+ @Component ( {
795+ template : `
796+ <mat-selection-list [formControl]="formControl">
797+ <mat-list-option value="opt1">Option 1</mat-list-option>
798+ <mat-list-option value="opt2">Option 2</mat-list-option>
799+ <mat-list-option value="opt3">Option 3</mat-list-option>
800+ </mat-selection-list>
801+ `
802+ } )
803+ class SelectionListWithFormControl {
804+ formControl = new FormControl ( ) ;
682805}
0 commit comments