Skip to content

Commit 12fd908

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

File tree

2 files changed

+314
-85
lines changed

2 files changed

+314
-85
lines changed

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

Lines changed: 184 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* knowledge of the CeCILL-C license and that you accept its terms.
3636
*/
3737
import { ComponentFixture, TestBed } from '@angular/core/testing';
38-
import { SelectComponent } from './select.component';
38+
import { SelectComponent, VitamuiSelectOptions } from './select.component';
3939
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4040
import { Component, ViewChild } from '@angular/core';
4141
import { FormControl, ReactiveFormsModule } from '@angular/forms';
@@ -47,7 +47,7 @@ import { input } from '../../../../testing/src';
4747

4848
const placeholder = 'test';
4949
const 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
})
6464
class 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

Comments
 (0)