3535 * knowledge of the CeCILL-C license and that you accept its terms.
3636 */
3737import { ComponentFixture , TestBed } from '@angular/core/testing' ;
38- import { SelectComponent } from './select.component' ;
38+ import { SelectComponent , VitamuiSelectOptions } from './select.component' ;
3939import { NoopAnimationsModule } from '@angular/platform-browser/animations' ;
4040import { Component , ViewChild } from '@angular/core' ;
4141import { FormControl , ReactiveFormsModule } from '@angular/forms' ;
@@ -47,7 +47,7 @@ import { input } from '../../../../testing/src';
4747
4848const placeholder = 'test' ;
4949const searchBarPlaceHolder = 'search test' ;
50- const options = {
50+ const defaultOptions = {
5151 options : [
5252 { key : 'option1' , label : 'option 1' } ,
5353 { key : 'option2' , label : 'option 2' } ,
@@ -58,14 +58,15 @@ const options = {
5858
5959@Component ( {
6060 template :
61- '<vitamui-select [placeholder]="placeholder" [options]="options" [formControl]="control" [multiple]="multiple"></vitamui-select>' ,
61+ '<vitamui-select [placeholder]="placeholder" [options]="options" [formControl]="control" [multiple]="multiple" [enableSelectAll]="enableSelectAll" ></vitamui-select>' ,
6262 imports : [ ReactiveFormsModule , SelectComponent ] ,
6363} )
6464class TestHostComponent {
6565 @ViewChild ( SelectComponent )
6666 selectComponent : SelectComponent ;
6767
68- options = options ;
68+ options : VitamuiSelectOptions | any [ ] ;
69+ enableSelectAll : boolean ;
6970 placeholder = placeholder ;
7071 searchBarPlaceHolder = searchBarPlaceHolder ;
7172 multiple ?: boolean ;
@@ -77,14 +78,22 @@ describe('SelectComponent', () => {
7778 let testHostComponent : TestHostComponent ;
7879 let selectHarness : MatSelectHarness ;
7980
80- function init ( isMultiple ?: boolean ) {
81+ function init (
82+ isMultiple ?: boolean ,
83+ { enableSelectAll, options } : { enableSelectAll : boolean ; options : VitamuiSelectOptions | any [ ] } = {
84+ enableSelectAll : true ,
85+ options : defaultOptions ,
86+ } ,
87+ ) {
8188 return async ( ) => {
8289 await TestBed . configureTestingModule ( {
8390 imports : [ NoopAnimationsModule , TranslateModule . forRoot ( ) , TestHostComponent ] ,
8491 } ) . compileComponents ( ) ;
8592
8693 hostFixture = TestBed . createComponent ( TestHostComponent ) ;
94+ hostFixture . componentInstance . options = options ;
8795 hostFixture . componentInstance . multiple = isMultiple ;
96+ hostFixture . componentInstance . enableSelectAll = enableSelectAll ;
8897 testHostComponent = hostFixture . componentInstance ;
8998 hostFixture . detectChanges ( ) ;
9099
@@ -114,7 +123,7 @@ describe('SelectComponent', () => {
114123 input ( document . querySelector ( 'input' ) , search ) ;
115124 hostFixture . detectChanges ( ) ;
116125
117- const expectedOptions = options . options . filter ( ( option ) => option . label . includes ( search ) ) ;
126+ const expectedOptions = defaultOptions . options . filter ( ( option ) => option . label . includes ( search ) ) ;
118127 expect ( testHostComponent . selectComponent . displayedOptions . length ) . toEqual ( expectedOptions . length ) ;
119128 }
120129 } ) ;
@@ -135,11 +144,11 @@ describe('SelectComponent', () => {
135144
136145 expect ( testHostComponent . control . value ) . toBeNull ( ) ;
137146
138- for ( const option of options . options ) {
139- const i = options . options . indexOf ( option ) ;
147+ for ( const option of defaultOptions . options ) {
148+ const i = defaultOptions . options . indexOf ( option ) ;
140149 labelElement . click ( ) ;
141150 const selectOptions = await selectHarness . getOptions ( ) ;
142- expect ( selectOptions . length ) . toBe ( options . options . length ) ;
151+ expect ( selectOptions . length ) . toBe ( defaultOptions . options . length ) ;
143152
144153 await selectOptions [ i ] . click ( ) ;
145154 const valueElement = hostFixture . debugElement . query ( By . css ( 'mat-select-trigger' ) ) . nativeElement ;
@@ -152,9 +161,9 @@ describe('SelectComponent', () => {
152161 const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
153162
154163 // Set 1st value
155- testHostComponent . control . setValue ( options . options [ 0 ] . key ) ;
164+ testHostComponent . control . setValue ( defaultOptions . options [ 0 ] . key ) ;
156165 // Check value is set
157- expect ( testHostComponent . control . value ) . toEqual ( options . options [ 0 ] . key ) ;
166+ expect ( testHostComponent . control . value ) . toEqual ( defaultOptions . options [ 0 ] . key ) ;
158167
159168 // Click on 1st value (should deselect it)
160169 labelElement . click ( ) ;
@@ -185,12 +194,12 @@ describe('SelectComponent', () => {
185194
186195 labelElement . click ( ) ;
187196 const selectOptions = await selectHarness . getOptions ( ) ;
188- expect ( selectOptions . length ) . toBe ( options . options . length + 1 ) ; // +1 for select all
197+ expect ( selectOptions . length ) . toBe ( defaultOptions . options . length + 1 ) ; // +1 for select all
189198
190199 const expectedValues = [ ] ;
191200
192- for ( const option of options . options ) {
193- const i = options . options . indexOf ( option ) ;
201+ for ( const option of defaultOptions . options ) {
202+ const i = defaultOptions . options . indexOf ( option ) ;
194203
195204 await selectOptions [ i + 1 ] . click ( ) ;
196205 expectedValues . push ( option . key ) ;
@@ -201,7 +210,7 @@ describe('SelectComponent', () => {
201210 } ) ;
202211
203212 it ( 'should toggle selecting all values when user clicks on select all' , async ( ) => {
204- const allValues = options . options . map ( ( option ) => option . key ) ;
213+ const allValues = defaultOptions . options . map ( ( option ) => option . key ) ;
205214
206215 const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
207216 await labelElement . click ( ) ;
@@ -228,8 +237,167 @@ describe('SelectComponent', () => {
228237 await selectOptions [ 0 ] . click ( ) ;
229238 await selectOptions [ 0 ] . click ( ) ;
230239
231- const allValues = options . options . map ( ( option ) => option . key ) ;
240+ const allValues = defaultOptions . options . map ( ( option ) => option . key ) ;
232241 expect ( testHostComponent . control . value ) . toEqual ( allValues ) ;
233242 } ) ;
243+
244+ it ( 'should correctly deselect an option without affecting others' , async ( ) => {
245+ testHostComponent . control . setValue ( [ 'option1' , 'option2' , 'option3' ] ) ;
246+ hostFixture . detectChanges ( ) ;
247+ await hostFixture . whenStable ( ) ;
248+
249+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
250+ labelElement . click ( ) ;
251+
252+ const selectOptions = await selectHarness . getOptions ( ) ;
253+ await selectOptions [ 2 ] . click ( ) ;
254+
255+ expect ( testHostComponent . control . value ) . toEqual ( [ 'option1' , 'option3' ] ) ;
256+ expect ( testHostComponent . control . value ) . not . toContain ( 'option2' ) ;
257+ } ) ;
258+
259+ it ( 'should display preselected values correctly on load' , async ( ) => {
260+ const preselectedValues = [ 'option2' , 'option3' ] ;
261+ testHostComponent . control . setValue ( preselectedValues ) ;
262+ hostFixture . detectChanges ( ) ;
263+ await hostFixture . whenStable ( ) ;
264+
265+ const valueElement = hostFixture . debugElement . query ( By . css ( 'mat-select-trigger' ) ) . nativeElement ;
266+ expect ( valueElement . textContent . trim ( ) . startsWith ( '2' ) ) . toBeTrue ( ) ;
267+ } ) ;
268+
269+ it ( 'should maintain selection integrity when options are reloaded' , async ( ) => {
270+ testHostComponent . control . setValue ( [ 'option1' , 'option3' ] ) ;
271+ hostFixture . detectChanges ( ) ;
272+ await hostFixture . whenStable ( ) ;
273+
274+ testHostComponent . options = {
275+ options : [
276+ { key : 'option1' , label : 'Updated Option 1' } ,
277+ { key : 'option2' , label : 'Updated Option 2' } ,
278+ { key : 'option3' , label : 'Updated Option 3' } ,
279+ ] ,
280+ } ;
281+ hostFixture . detectChanges ( ) ;
282+ await hostFixture . whenStable ( ) ;
283+
284+ expect ( testHostComponent . control . value ) . toEqual ( [ 'option1' , 'option3' ] ) ;
285+ } ) ;
286+ } ) ;
287+
288+ describe ( 'in NON multiple mode (selection integrity)' , ( ) => {
289+ beforeEach ( init ( false ) ) ;
290+
291+ it ( 'should only select one option at a time' , async ( ) => {
292+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
293+
294+ labelElement . click ( ) ;
295+ const selectOptions = await selectHarness . getOptions ( ) ;
296+ await selectOptions [ 0 ] . click ( ) ;
297+
298+ expect ( testHostComponent . control . value ) . toBe ( 'option1' ) ;
299+
300+ labelElement . click ( ) ;
301+ const selectOptions2 = await selectHarness . getOptions ( ) ;
302+ await selectOptions2 [ 1 ] . click ( ) ;
303+
304+ expect ( testHostComponent . control . value ) . toBe ( 'option2' ) ;
305+ } ) ;
306+
307+ it ( 'should only select the chosen value and not others' , async ( ) => {
308+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
309+
310+ labelElement . click ( ) ;
311+ const selectOptions = await selectHarness . getOptions ( ) ;
312+ await selectOptions [ 1 ] . click ( ) ;
313+
314+ expect ( testHostComponent . control . value ) . toBe ( 'option2' ) ;
315+ expect ( testHostComponent . control . value ) . not . toBe ( 'option1' ) ;
316+ expect ( testHostComponent . control . value ) . not . toBe ( 'option3' ) ;
317+ expect ( testHostComponent . control . value ) . not . toBe ( 'something-else' ) ;
318+ } ) ;
319+
320+ it ( 'should display preselected value correctly on load' , async ( ) => {
321+ testHostComponent . control . setValue ( 'option3' ) ;
322+ hostFixture . detectChanges ( ) ;
323+ await hostFixture . whenStable ( ) ;
324+
325+ const valueElement = hostFixture . debugElement . query ( By . css ( 'mat-select-trigger' ) ) . nativeElement ;
326+ expect ( valueElement . textContent . trim ( ) ) . toBe ( 'option 3' ) ;
327+ } ) ;
328+ } ) ;
329+
330+ describe ( 'in multiple mode, without select all and with custom options' , ( ) => {
331+ beforeEach (
332+ init ( true , {
333+ enableSelectAll : false ,
334+ options : {
335+ options : [
336+ { key : 'DE' , label : 'Allemagne' , disabled : false } ,
337+ { key : 'BE' , label : 'Belgique' , disabled : false } ,
338+ { key : 'DK' , label : 'Danemark' , disabled : true } ,
339+ { key : 'ES' , label : 'Espagne' , disabled : false } ,
340+ { key : 'FR' , label : 'France' , disabled : false } ,
341+ { key : 'IT' , label : 'Italie' , disabled : false } ,
342+ { key : 'PT' , label : 'Portugal' , disabled : false } ,
343+ { key : 'GB' , label : 'Royaume-Uni' , disabled : false } ,
344+ ] ,
345+ } ,
346+ } ) ,
347+ ) ;
348+
349+ it ( 'should only select chosen options and not others' , async ( ) => {
350+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
351+ labelElement . click ( ) ;
352+
353+ const selectOptions = await selectHarness . getOptions ( ) ;
354+ await selectOptions [ 0 ] . click ( ) ;
355+ await selectOptions [ 3 ] . click ( ) ;
356+
357+ expect ( testHostComponent . control . value ) . toEqual ( [ 'DE' , 'ES' ] ) ;
358+ expect ( testHostComponent . control . value ) . not . toContain ( 'BE' ) ;
359+ } ) ;
360+ } ) ;
361+
362+ describe ( 'regression test for bug (Referential fields appear empty in edit mode despite a saved value)' , ( ) => {
363+ beforeEach ( async ( ) => {
364+ await TestBed . configureTestingModule ( {
365+ imports : [ NoopAnimationsModule , TranslateModule . forRoot ( ) , TestHostComponent ] ,
366+ } ) . compileComponents ( ) ;
367+
368+ hostFixture = TestBed . createComponent ( TestHostComponent ) ;
369+ testHostComponent = hostFixture . componentInstance ;
370+ } ) ;
371+
372+ it ( 'should display preselected value in edit mode even when it is far down in a long list' , async ( ) => {
373+ // Create a large list of options (100 items)
374+ const largeOptionList = Array . from ( { length : 100 } , ( _ , i ) => ( {
375+ key : `option${ i } ` ,
376+ label : `Option ${ i } ` ,
377+ } ) ) ;
378+
379+ testHostComponent . options = { options : largeOptionList } ;
380+ testHostComponent . multiple = false ;
381+
382+ // Preselect an option far down the list (option #95)
383+ testHostComponent . control . setValue ( 'option95' ) ;
384+
385+ hostFixture . detectChanges ( ) ;
386+ await hostFixture . whenStable ( ) ;
387+
388+ selectHarness = await TestbedHarnessEnvironment . loader ( hostFixture ) . getHarness ( MatSelectHarness ) ;
389+
390+ // Verify the selected value is displayed in the trigger
391+ const valueText = await selectHarness . getValueText ( ) ;
392+ expect ( valueText ) . toBe ( 'Option 95' ) ;
393+
394+ // Open the select
395+ await selectHarness . open ( ) ;
396+ await hostFixture . whenStable ( ) ;
397+
398+ // Verify the value is still displayed after opening
399+ const valueTextAfterOpen = await selectHarness . getValueText ( ) ;
400+ expect ( valueTextAfterOpen ) . toBe ( 'Option 95' ) ;
401+ } ) ;
234402 } ) ;
235403} ) ;
0 commit comments