362 lines
12 KiB
TypeScript
362 lines
12 KiB
TypeScript
|
|
import {Component, signal} from '@angular/core';
|
||
|
|
import {ComponentFixture, inject, TestBed} from '@angular/core/testing';
|
||
|
|
import {OverlayContainer} from '@angular/cdk/overlay';
|
||
|
|
import {HarnessLoader, parallel} from '@angular/cdk/testing';
|
||
|
|
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
|
||
|
|
import {FormControl, ReactiveFormsModule, Validators} from '@angular/forms';
|
||
|
|
import {MatFormFieldModule} from '@angular/material/form-field';
|
||
|
|
import {MatSelectModule} from '@angular/material/select';
|
||
|
|
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||
|
|
import {MatSelectHarness} from './select-harness';
|
||
|
|
|
||
|
|
describe('MatSelectHarness', () => {
|
||
|
|
let fixture: ComponentFixture<SelectHarnessTest>;
|
||
|
|
let loader: HarnessLoader;
|
||
|
|
let overlayContainer: OverlayContainer;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
TestBed.configureTestingModule({
|
||
|
|
imports: [
|
||
|
|
MatSelectModule,
|
||
|
|
MatFormFieldModule,
|
||
|
|
NoopAnimationsModule,
|
||
|
|
ReactiveFormsModule,
|
||
|
|
SelectHarnessTest,
|
||
|
|
],
|
||
|
|
});
|
||
|
|
|
||
|
|
fixture = TestBed.createComponent(SelectHarnessTest);
|
||
|
|
fixture.detectChanges();
|
||
|
|
loader = TestbedHarnessEnvironment.loader(fixture);
|
||
|
|
inject([OverlayContainer], (oc: OverlayContainer) => {
|
||
|
|
overlayContainer = oc;
|
||
|
|
})();
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
// Angular won't call this for us so we need to do it ourselves to avoid leaks.
|
||
|
|
overlayContainer.ngOnDestroy();
|
||
|
|
overlayContainer = null!;
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should load all select harnesses', async () => {
|
||
|
|
const selects = await loader.getAllHarnesses(MatSelectHarness);
|
||
|
|
expect(selects.length).toBe(4);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should filter by whether a select is disabled', async () => {
|
||
|
|
let enabledSelects = await loader.getAllHarnesses(MatSelectHarness.with({disabled: false}));
|
||
|
|
let disabledSelects = await loader.getAllHarnesses(MatSelectHarness.with({disabled: true}));
|
||
|
|
|
||
|
|
expect(enabledSelects.length).toBe(4);
|
||
|
|
expect(disabledSelects.length).toBe(0);
|
||
|
|
|
||
|
|
fixture.componentInstance.isDisabled.set(true);
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
enabledSelects = await loader.getAllHarnesses(MatSelectHarness.with({disabled: false}));
|
||
|
|
disabledSelects = await loader.getAllHarnesses(MatSelectHarness.with({disabled: true}));
|
||
|
|
|
||
|
|
expect(enabledSelects.length).toBe(3);
|
||
|
|
expect(disabledSelects.length).toBe(1);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to check whether a select is in multi-selection mode', async () => {
|
||
|
|
const singleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({
|
||
|
|
selector: '#single-selection',
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
const multipleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({selector: '#multiple-selection'}),
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(await singleSelection.isMultiple()).toBe(false);
|
||
|
|
expect(await multipleSelection.isMultiple()).toBe(true);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should get disabled state', async () => {
|
||
|
|
const singleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({
|
||
|
|
selector: '#single-selection',
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
const multipleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({selector: '#multiple-selection'}),
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(await singleSelection.isDisabled()).toBe(false);
|
||
|
|
expect(await multipleSelection.isDisabled()).toBe(false);
|
||
|
|
|
||
|
|
fixture.componentInstance.isDisabled.set(true);
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
expect(await singleSelection.isDisabled()).toBe(true);
|
||
|
|
expect(await multipleSelection.isDisabled()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should get required state', async () => {
|
||
|
|
const singleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({
|
||
|
|
selector: '#single-selection',
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
const multipleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({selector: '#multiple-selection'}),
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(await singleSelection.isRequired()).toBe(false);
|
||
|
|
expect(await multipleSelection.isRequired()).toBe(false);
|
||
|
|
|
||
|
|
fixture.componentInstance.isRequired.set(true);
|
||
|
|
fixture.detectChanges();
|
||
|
|
|
||
|
|
expect(await singleSelection.isRequired()).toBe(true);
|
||
|
|
expect(await multipleSelection.isRequired()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should get valid state', async () => {
|
||
|
|
const singleSelection = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({
|
||
|
|
selector: '#single-selection',
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
const withFormControl = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({
|
||
|
|
selector: '#with-form-control',
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(await singleSelection.isValid()).toBe(true);
|
||
|
|
expect(await withFormControl.isValid()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should focus and blur a select', async () => {
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#single-selection'}));
|
||
|
|
expect(await select.isFocused()).toBe(false);
|
||
|
|
await select.focus();
|
||
|
|
expect(await select.isFocused()).toBe(true);
|
||
|
|
await select.blur();
|
||
|
|
expect(await select.isFocused()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to open and close a single-selection select', async () => {
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#single-selection'}));
|
||
|
|
|
||
|
|
expect(await select.isOpen()).toBe(false);
|
||
|
|
|
||
|
|
await select.open();
|
||
|
|
expect(await select.isOpen()).toBe(true);
|
||
|
|
|
||
|
|
await select.close();
|
||
|
|
expect(await select.isOpen()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to open and close a multi-selection select', async () => {
|
||
|
|
const select = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({selector: '#multiple-selection'}),
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(await select.isOpen()).toBe(false);
|
||
|
|
|
||
|
|
await select.open();
|
||
|
|
expect(await select.isOpen()).toBe(true);
|
||
|
|
|
||
|
|
await select.close();
|
||
|
|
expect(await select.isOpen()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get the select options', async () => {
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#single-selection'}));
|
||
|
|
await select.open();
|
||
|
|
const options = await select.getOptions();
|
||
|
|
|
||
|
|
expect(options.length).toBe(11);
|
||
|
|
expect(await options[5].getText()).toBe('New York');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get the select panel groups', async () => {
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#grouped'}));
|
||
|
|
await select.open();
|
||
|
|
const groups = await select.getOptionGroups();
|
||
|
|
const options = await select.getOptions();
|
||
|
|
|
||
|
|
expect(groups.length).toBe(3);
|
||
|
|
expect(options.length).toBe(14);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get the select options when there are multiple open selects', async () => {
|
||
|
|
const singleSelect = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({
|
||
|
|
selector: '#single-selection',
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
await singleSelect.open();
|
||
|
|
|
||
|
|
const groupedSelect = await loader.getHarness(MatSelectHarness.with({selector: '#grouped'}));
|
||
|
|
await groupedSelect.open();
|
||
|
|
|
||
|
|
const [singleOptions, groupedOptions] = await parallel(() => [
|
||
|
|
singleSelect.getOptions(),
|
||
|
|
groupedSelect.getOptions(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect(await singleOptions[0].getText()).toBe('Alabama');
|
||
|
|
expect(singleOptions.length).toBe(11);
|
||
|
|
|
||
|
|
expect(await groupedOptions[0].getText()).toBe('Iowa');
|
||
|
|
expect(groupedOptions.length).toBe(14);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get the value text from a single-selection select', async () => {
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#single-selection'}));
|
||
|
|
await select.open();
|
||
|
|
const options = await select.getOptions();
|
||
|
|
|
||
|
|
await options[3].click();
|
||
|
|
|
||
|
|
expect(await select.getValueText()).toBe('Kansas');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get the value text from a multi-selection select', async () => {
|
||
|
|
const select = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({selector: '#multiple-selection'}),
|
||
|
|
);
|
||
|
|
await select.open();
|
||
|
|
const options = await select.getOptions();
|
||
|
|
|
||
|
|
await options[3].click();
|
||
|
|
await options[5].click();
|
||
|
|
|
||
|
|
expect(await select.getValueText()).toBe('Kansas, New York');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get whether a single-selection select is empty', async () => {
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#single-selection'}));
|
||
|
|
|
||
|
|
expect(await select.isEmpty()).toBe(true);
|
||
|
|
|
||
|
|
await select.open();
|
||
|
|
const options = await select.getOptions();
|
||
|
|
await options[3].click();
|
||
|
|
|
||
|
|
expect(await select.isEmpty()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to get whether a multi-selection select is empty', async () => {
|
||
|
|
const select = await loader.getHarness(
|
||
|
|
MatSelectHarness.with({selector: '#multiple-selection'}),
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(await select.isEmpty()).toBe(true);
|
||
|
|
|
||
|
|
await select.open();
|
||
|
|
const options = await select.getOptions();
|
||
|
|
await options[3].click();
|
||
|
|
await options[5].click();
|
||
|
|
|
||
|
|
expect(await select.isEmpty()).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should be able to click an option', async () => {
|
||
|
|
const control = fixture.componentInstance.formControl;
|
||
|
|
const select = await loader.getHarness(MatSelectHarness.with({selector: '#with-form-control'}));
|
||
|
|
|
||
|
|
expect(control.value).toBeFalsy();
|
||
|
|
|
||
|
|
await select.open();
|
||
|
|
await (await select.getOptions())[1].click();
|
||
|
|
|
||
|
|
expect(control.value).toBe('CA');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
@Component({
|
||
|
|
template: `
|
||
|
|
<mat-form-field>
|
||
|
|
<mat-select [disabled]="isDisabled()" [required]="isRequired()" id="single-selection">
|
||
|
|
@for (state of states; track state) {
|
||
|
|
<mat-option [value]="state.code">{{ state.name }}</mat-option>
|
||
|
|
}
|
||
|
|
</mat-select>
|
||
|
|
</mat-form-field>
|
||
|
|
<mat-form-field>
|
||
|
|
<mat-select multiple id="multiple-selection">
|
||
|
|
@for (state of states; track state) {
|
||
|
|
<mat-option [value]="state.code">{{ state.name }}</mat-option>
|
||
|
|
}
|
||
|
|
</mat-select>
|
||
|
|
</mat-form-field>
|
||
|
|
<mat-form-field>
|
||
|
|
<mat-select id="grouped">
|
||
|
|
@for (group of stateGroups; track group) {
|
||
|
|
<mat-optgroup [label]="group.name">
|
||
|
|
@for (state of group.states; track state) {
|
||
|
|
<mat-option [value]="state.code">{{ state.name }}</mat-option>
|
||
|
|
}
|
||
|
|
</mat-optgroup>
|
||
|
|
}
|
||
|
|
</mat-select>
|
||
|
|
</mat-form-field>
|
||
|
|
<mat-form-field>
|
||
|
|
<mat-select [formControl]="formControl" id="with-form-control">
|
||
|
|
@for (state of states; track state) {
|
||
|
|
<mat-option [value]="state.code">{{ state.name }}</mat-option>
|
||
|
|
}
|
||
|
|
</mat-select>
|
||
|
|
</mat-form-field>
|
||
|
|
`,
|
||
|
|
standalone: true,
|
||
|
|
imports: [MatSelectModule, MatFormFieldModule, ReactiveFormsModule],
|
||
|
|
})
|
||
|
|
class SelectHarnessTest {
|
||
|
|
formControl = new FormControl(undefined as string | undefined, [Validators.required]);
|
||
|
|
isDisabled = signal(false);
|
||
|
|
isRequired = signal(false);
|
||
|
|
states = [
|
||
|
|
{code: 'AL', name: 'Alabama'},
|
||
|
|
{code: 'CA', name: 'California'},
|
||
|
|
{code: 'FL', name: 'Florida'},
|
||
|
|
{code: 'KS', name: 'Kansas'},
|
||
|
|
{code: 'MA', name: 'Massachusetts'},
|
||
|
|
{code: 'NY', name: 'New York'},
|
||
|
|
{code: 'OR', name: 'Oregon'},
|
||
|
|
{code: 'PA', name: 'Pennsylvania'},
|
||
|
|
{code: 'TN', name: 'Tennessee'},
|
||
|
|
{code: 'VA', name: 'Virginia'},
|
||
|
|
{code: 'WY', name: 'Wyoming'},
|
||
|
|
];
|
||
|
|
|
||
|
|
stateGroups = [
|
||
|
|
{
|
||
|
|
name: 'One',
|
||
|
|
states: [
|
||
|
|
{code: 'IA', name: 'Iowa'},
|
||
|
|
{code: 'KS', name: 'Kansas'},
|
||
|
|
{code: 'KY', name: 'Kentucky'},
|
||
|
|
{code: 'LA', name: 'Louisiana'},
|
||
|
|
{code: 'ME', name: 'Maine'},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: 'Two',
|
||
|
|
states: [
|
||
|
|
{code: 'RI', name: 'Rhode Island'},
|
||
|
|
{code: 'SC', name: 'South Carolina'},
|
||
|
|
{code: 'SD', name: 'South Dakota'},
|
||
|
|
{code: 'TN', name: 'Tennessee'},
|
||
|
|
{code: 'TX', name: 'Texas'},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: 'Three',
|
||
|
|
states: [
|
||
|
|
{code: 'UT', name: 'Utah'},
|
||
|
|
{code: 'WA', name: 'Washington'},
|
||
|
|
{code: 'WV', name: 'West Virginia'},
|
||
|
|
{code: 'WI', name: 'Wisconsin'},
|
||
|
|
],
|
||
|
|
},
|
||
|
|
];
|
||
|
|
}
|