Skip to content

Commit 3b3b642

Browse files
author
Salim Terres
committed
Bug #15375 [Vitam_ui] – In the usage selector, selecting one option automatically checks another option.
1 parent bb5c431 commit 3b3b642

File tree

2 files changed

+442
-67
lines changed

2 files changed

+442
-67
lines changed

ui/ui-frontend/projects/vitamui-library/src/lib/components/select/select.component.spec.ts

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)