@@ -231,5 +231,319 @@ describe('SelectComponent', () => {
231231 const allValues = options . options . map ( ( option ) => option . key ) ;
232232 expect ( testHostComponent . control . value ) . toEqual ( allValues ) ;
233233 } ) ;
234+
235+ it ( 'should only select chosen options and not others' , async ( ) => {
236+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
237+ labelElement . click ( ) ;
238+
239+ const selectOptions = await selectHarness . getOptions ( ) ;
240+ await selectOptions [ 1 ] . click ( ) ;
241+ await selectOptions [ 2 ] . click ( ) ;
242+
243+ expect ( testHostComponent . control . value ) . toEqual ( [ 'option1' , 'option2' ] ) ;
244+ expect ( testHostComponent . control . value ) . not . toContain ( 'option3' ) ;
245+ expect ( testHostComponent . control . value ) . not . toContain ( 'something-else' ) ;
246+ } ) ;
247+
248+ it ( 'should correctly deselect an option without affecting others' , async ( ) => {
249+ testHostComponent . control . setValue ( [ 'option1' , 'option2' , 'option3' ] ) ;
250+ hostFixture . detectChanges ( ) ;
251+ await hostFixture . whenStable ( ) ;
252+
253+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
254+ labelElement . click ( ) ;
255+
256+ const selectOptions = await selectHarness . getOptions ( ) ;
257+ await selectOptions [ 2 ] . click ( ) ;
258+
259+ expect ( testHostComponent . control . value ) . toEqual ( [ 'option1' , 'option3' ] ) ;
260+ expect ( testHostComponent . control . value ) . not . toContain ( 'option2' ) ;
261+ } ) ;
262+
263+ it ( 'should display preselected values correctly on load' , async ( ) => {
264+ const preselectedValues = [ 'option2' , 'option3' ] ;
265+ testHostComponent . control . setValue ( preselectedValues ) ;
266+ hostFixture . detectChanges ( ) ;
267+ await hostFixture . whenStable ( ) ;
268+
269+ const valueElement = hostFixture . debugElement . query ( By . css ( 'mat-select-trigger' ) ) . nativeElement ;
270+ expect ( valueElement . textContent . trim ( ) . startsWith ( '2' ) ) . toBeTrue ( ) ;
271+ } ) ;
272+
273+ it ( 'should maintain selection integrity when options are reloaded' , async ( ) => {
274+ testHostComponent . control . setValue ( [ 'option1' , 'option3' ] ) ;
275+ hostFixture . detectChanges ( ) ;
276+ await hostFixture . whenStable ( ) ;
277+
278+ testHostComponent . options = {
279+ options : [
280+ { key : 'option1' , label : 'Updated Option 1' } ,
281+ { key : 'option2' , label : 'Updated Option 2' } ,
282+ { key : 'option3' , label : 'Updated Option 3' } ,
283+ ] ,
284+ } ;
285+ hostFixture . detectChanges ( ) ;
286+ await hostFixture . whenStable ( ) ;
287+
288+ expect ( testHostComponent . control . value ) . toEqual ( [ 'option1' , 'option3' ] ) ;
289+ } ) ;
290+ } ) ;
291+
292+ describe ( 'in NON multiple mode (selection integrity)' , ( ) => {
293+ beforeEach ( init ( false ) ) ;
294+
295+ it ( 'should only select one option at a time' , async ( ) => {
296+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
297+
298+ labelElement . click ( ) ;
299+ const selectOptions = await selectHarness . getOptions ( ) ;
300+ await selectOptions [ 0 ] . click ( ) ;
301+
302+ expect ( testHostComponent . control . value ) . toBe ( 'option1' ) ;
303+
304+ labelElement . click ( ) ;
305+ const selectOptions2 = await selectHarness . getOptions ( ) ;
306+ await selectOptions2 [ 1 ] . click ( ) ;
307+
308+ expect ( testHostComponent . control . value ) . toBe ( 'option2' ) ;
309+ } ) ;
310+
311+ it ( 'should only select the chosen value and not others' , async ( ) => {
312+ const labelElement = hostFixture . debugElement . query ( By . css ( 'mat-label' ) ) . nativeElement ;
313+
314+ labelElement . click ( ) ;
315+ const selectOptions = await selectHarness . getOptions ( ) ;
316+ await selectOptions [ 1 ] . click ( ) ;
317+
318+ expect ( testHostComponent . control . value ) . toBe ( 'option2' ) ;
319+ expect ( testHostComponent . control . value ) . not . toBe ( 'option1' ) ;
320+ expect ( testHostComponent . control . value ) . not . toBe ( 'option3' ) ;
321+ expect ( testHostComponent . control . value ) . not . toBe ( 'something-else' ) ;
322+ } ) ;
323+
324+ it ( 'should display preselected value correctly on load' , async ( ) => {
325+ testHostComponent . control . setValue ( 'option3' ) ;
326+ hostFixture . detectChanges ( ) ;
327+ await hostFixture . whenStable ( ) ;
328+
329+ const valueElement = hostFixture . debugElement . query ( By . css ( 'mat-select-trigger' ) ) . nativeElement ;
330+ expect ( valueElement . textContent . trim ( ) ) . toBe ( 'option 3' ) ;
331+ } ) ;
332+ } ) ;
333+
334+ describe ( 'compareOptions method' , ( ) => {
335+ describe ( 'comparing null and undefined values' , ( ) => {
336+ beforeEach ( init ( false ) ) ;
337+
338+ it ( 'should return true when both values are null' , ( ) => {
339+ const result = testHostComponent . selectComponent . compareOptions ( null , null ) ;
340+ expect ( result ) . toBeTrue ( ) ;
341+ } ) ;
342+
343+ it ( 'should return true when both values are undefined' , ( ) => {
344+ const result = testHostComponent . selectComponent . compareOptions ( undefined , undefined ) ;
345+ expect ( result ) . toBeTrue ( ) ;
346+ } ) ;
347+
348+ it ( 'should return false when one value is null and the other is undefined' , ( ) => {
349+ const result = testHostComponent . selectComponent . compareOptions ( null , undefined ) ;
350+ expect ( result ) . toBeFalse ( ) ;
351+ } ) ;
352+
353+ it ( 'should return false when one value is null and the other is a string' , ( ) => {
354+ const result = testHostComponent . selectComponent . compareOptions ( null , 'option1' ) ;
355+ expect ( result ) . toBeFalse ( ) ;
356+ } ) ;
357+
358+ it ( 'should return false when one value is undefined and the other is a string' , ( ) => {
359+ const result = testHostComponent . selectComponent . compareOptions ( undefined , 'option1' ) ;
360+ expect ( result ) . toBeFalse ( ) ;
361+ } ) ;
362+ } ) ;
363+
364+ describe ( 'comparing string values' , ( ) => {
365+ beforeEach ( init ( false ) ) ;
366+
367+ it ( 'should return true when both strings are identical' , ( ) => {
368+ const result = testHostComponent . selectComponent . compareOptions ( 'option1' , 'option1' ) ;
369+ expect ( result ) . toBeTrue ( ) ;
370+ } ) ;
371+
372+ it ( 'should return false when strings are different' , ( ) => {
373+ const result = testHostComponent . selectComponent . compareOptions ( 'option1' , 'option2' ) ;
374+ expect ( result ) . toBeFalse ( ) ;
375+ } ) ;
376+
377+ it ( 'should return true when comparing string with numeric value as string' , ( ) => {
378+ const result = testHostComponent . selectComponent . compareOptions ( '123' , 123 ) ;
379+ expect ( result ) . toBeTrue ( ) ;
380+ } ) ;
381+
382+ it ( 'should return false when comparing different numeric values as strings' , ( ) => {
383+ const result = testHostComponent . selectComponent . compareOptions ( '123' , 456 ) ;
384+ expect ( result ) . toBeFalse ( ) ;
385+ } ) ;
386+ } ) ;
387+
388+ describe ( 'comparing object values with key property' , ( ) => {
389+ beforeEach ( init ( false ) ) ;
390+
391+ it ( 'should return true when both objects have the same key' , ( ) => {
392+ const obj1 = { key : 'option1' , label : 'Option 1' } ;
393+ const obj2 = { key : 'option1' , label : 'Option 1' } ;
394+ const result = testHostComponent . selectComponent . compareOptions ( obj1 , obj2 ) ;
395+ expect ( result ) . toBeTrue ( ) ;
396+ } ) ;
397+
398+ it ( 'should return false when objects have different keys' , ( ) => {
399+ const obj1 = { key : 'option1' , label : 'Option 1' } ;
400+ const obj2 = { key : 'option2' , label : 'Option 2' } ;
401+ const result = testHostComponent . selectComponent . compareOptions ( obj1 , obj2 ) ;
402+ expect ( result ) . toBeFalse ( ) ;
403+ } ) ;
404+ } ) ;
405+
406+ describe ( 'comparing array values (multiple mode)' , ( ) => {
407+ beforeEach ( init ( true ) ) ;
408+
409+ it ( 'should return true when both arrays are empty' , ( ) => {
410+ const result = testHostComponent . selectComponent . compareOptions ( [ ] , [ ] ) ;
411+ expect ( result ) . toBeTrue ( ) ;
412+ } ) ;
413+
414+ it ( 'should return true when both arrays contain the same values in the same order' , ( ) => {
415+ const arr1 = [ 'option1' , 'option2' , 'option3' ] ;
416+ const arr2 = [ 'option1' , 'option2' , 'option3' ] ;
417+ const result = testHostComponent . selectComponent . compareOptions ( arr1 , arr2 ) ;
418+ expect ( result ) . toBeTrue ( ) ;
419+ } ) ;
420+
421+ it ( 'should return true when both arrays contain the same values in different order' , ( ) => {
422+ const arr1 = [ 'option1' , 'option2' , 'option3' ] ;
423+ const arr2 = [ 'option3' , 'option1' , 'option2' ] ;
424+ const result = testHostComponent . selectComponent . compareOptions ( arr1 , arr2 ) ;
425+ expect ( result ) . toBeTrue ( ) ;
426+ } ) ;
427+
428+ it ( 'should return false when arrays have different lengths' , ( ) => {
429+ const arr1 = [ 'option1' , 'option2' ] ;
430+ const arr2 = [ 'option1' , 'option2' , 'option3' ] ;
431+ const result = testHostComponent . selectComponent . compareOptions ( arr1 , arr2 ) ;
432+ expect ( result ) . toBeFalse ( ) ;
433+ } ) ;
434+
435+ it ( 'should return false when arrays have same length but different values' , ( ) => {
436+ const arr1 = [ 'option1' , 'option2' , 'option3' ] ;
437+ const arr2 = [ 'option1' , 'option2' , 'option4' ] ;
438+ const result = testHostComponent . selectComponent . compareOptions ( arr1 , arr2 ) ;
439+ expect ( result ) . toBeFalse ( ) ;
440+ } ) ;
441+ } ) ;
442+
443+ describe ( 'comparing mixed types' , ( ) => {
444+ beforeEach ( init ( false ) ) ;
445+
446+ it ( 'should return true when comparing number to string representation' , ( ) => {
447+ const result = testHostComponent . selectComponent . compareOptions ( 123 , '123' ) ;
448+ expect ( result ) . toBeTrue ( ) ;
449+ } ) ;
450+
451+ it ( 'should return true when comparing boolean true to string "true"' , ( ) => {
452+ const result = testHostComponent . selectComponent . compareOptions ( true , 'true' ) ;
453+ expect ( result ) . toBeTrue ( ) ;
454+ } ) ;
455+
456+ it ( 'should return true when comparing boolean false to string "false"' , ( ) => {
457+ const result = testHostComponent . selectComponent . compareOptions ( false , 'false' ) ;
458+ expect ( result ) . toBeTrue ( ) ;
459+ } ) ;
460+
461+ it ( 'should handle empty string comparison' , ( ) => {
462+ const result = testHostComponent . selectComponent . compareOptions ( '' , '' ) ;
463+ expect ( result ) . toBeTrue ( ) ;
464+ } ) ;
465+
466+ it ( 'should return false when comparing empty string to null' , ( ) => {
467+ const result = testHostComponent . selectComponent . compareOptions ( '' , null ) ;
468+ expect ( result ) . toBeFalse ( ) ;
469+ } ) ;
470+ } ) ;
471+
472+ describe ( 'edge cases' , ( ) => {
473+ beforeEach ( init ( false ) ) ;
474+
475+ it ( 'should not match similar but different option keys' , ( ) => {
476+ // This test ensures that "option1" is not matched with "option10" or "option12"
477+ const result1 = testHostComponent . selectComponent . compareOptions ( 'option1' , 'option10' ) ;
478+ expect ( result1 ) . toBeFalse ( ) ;
479+
480+ const result2 = testHostComponent . selectComponent . compareOptions ( 'option1' , 'option12' ) ;
481+ expect ( result2 ) . toBeFalse ( ) ;
482+ } ) ;
483+
484+ it ( 'should correctly compare options with special characters' , ( ) => {
485+ const result1 = testHostComponent . selectComponent . compareOptions ( 'option-1' , 'option-1' ) ;
486+ expect ( result1 ) . toBeTrue ( ) ;
487+
488+ const result2 = testHostComponent . selectComponent . compareOptions ( 'option_1' , 'option-1' ) ;
489+ expect ( result2 ) . toBeFalse ( ) ;
490+ } ) ;
491+
492+ it ( 'should handle whitespace in option values' , ( ) => {
493+ const result1 = testHostComponent . selectComponent . compareOptions ( 'option 1' , 'option 1' ) ;
494+ expect ( result1 ) . toBeTrue ( ) ;
495+
496+ const result2 = testHostComponent . selectComponent . compareOptions ( 'option 1' , 'option 1' ) ;
497+ expect ( result2 ) . toBeFalse ( ) ;
498+ } ) ;
499+
500+ it ( 'should not partially match option keys' , ( ) => {
501+ // Ensures that selecting "usage" doesn't match "usage_report" or vice versa
502+ const result = testHostComponent . selectComponent . compareOptions ( 'usage' , 'usage_report' ) ;
503+ expect ( result ) . toBeFalse ( ) ;
504+ } ) ;
505+ } ) ;
506+ } ) ;
507+
508+ describe ( 'regression test for bug (Referential fields appear empty in edit mode despite a saved value)' , ( ) => {
509+ beforeEach ( async ( ) => {
510+ await TestBed . configureTestingModule ( {
511+ imports : [ NoopAnimationsModule , TranslateModule . forRoot ( ) , TestHostComponent ] ,
512+ } ) . compileComponents ( ) ;
513+
514+ hostFixture = TestBed . createComponent ( TestHostComponent ) ;
515+ testHostComponent = hostFixture . componentInstance ;
516+ } ) ;
517+
518+ it ( 'should display preselected value in edit mode even when it is far down in a long list' , async ( ) => {
519+ // Create a large list of options (100 items)
520+ const largeOptionList = Array . from ( { length : 100 } , ( _ , i ) => ( {
521+ key : `option${ i } ` ,
522+ label : `Option ${ i } ` ,
523+ } ) ) ;
524+
525+ testHostComponent . options = { options : largeOptionList } ;
526+ testHostComponent . multiple = false ;
527+
528+ // Preselect an option far down the list (option #95)
529+ testHostComponent . control . setValue ( 'option95' ) ;
530+
531+ hostFixture . detectChanges ( ) ;
532+ await hostFixture . whenStable ( ) ;
533+
534+ selectHarness = await TestbedHarnessEnvironment . loader ( hostFixture ) . getHarness ( MatSelectHarness ) ;
535+
536+ // Verify the selected value is displayed in the trigger
537+ const valueText = await selectHarness . getValueText ( ) ;
538+ expect ( valueText ) . toBe ( 'Option 95' ) ;
539+
540+ // Open the select
541+ await selectHarness . open ( ) ;
542+ await hostFixture . whenStable ( ) ;
543+
544+ // Verify the value is still displayed after opening
545+ const valueTextAfterOpen = await selectHarness . getValueText ( ) ;
546+ expect ( valueTextAfterOpen ) . toBe ( 'Option 95' ) ;
547+ } ) ;
234548 } ) ;
235549} ) ;
0 commit comments