/
githubmirror
/
components
Обзор
Документация
Войти
/
githubmirror
/
components
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/material/chips/chip-input.spec.ts
367 строк
13 KB
Kristiyan Kostadinov
refactor(material/chips): switch tests away from fakeAsync (#33112)
21 апр 2026, 10:15
Не верифицирован
21 апр 2026, 10:15
b04751c
Код
Авторство
О чём код?
import {COMMA, ENTER, TAB} from '@angular/cdk/keycodes'; import { createKeyboardEvent, dispatchEvent, dispatchKeyboardEvent, provideFakeDirectionality, } from '@angular/cdk/testing/private'; import {Component, DebugElement, ViewChild, ChangeDetectionStrategy} from '@angular/core'; import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {MATERIAL_ANIMATIONS} from '../core'; import {MatFormField} from '../form-field'; import { MAT_CHIPS_DEFAULT_OPTIONS, MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRow, MatChipsDefaultOptions, } from './index'; describe('MatChipInput', () => { let fixture: ComponentFixture<TestChipInput>; let testChipInput: TestChipInput; let inputDebugElement: DebugElement; let inputNativeElement: HTMLInputElement; let chipInputDirective: MatChipInput; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ providers: [ provideFakeDirectionality('ltr'), {provide: MATERIAL_ANIMATIONS, useValue: {animationsDisabled: true}}, ], }); })); beforeEach(waitForAsync(() => { fixture = TestBed.createComponent(TestChipInput); testChipInput = fixture.debugElement.componentInstance; fixture.detectChanges(); inputDebugElement = fixture.debugElement.query(By.directive(MatChipInput))!; chipInputDirective = inputDebugElement.injector.get<MatChipInput>(MatChipInput); inputNativeElement = inputDebugElement.nativeElement; })); describe('basic behavior', () => { it('emits the (chipEnd) on enter keyup', () => { spyOn(testChipInput, 'add'); dispatchKeyboardEvent(inputNativeElement, 'keydown', ENTER); expect(testChipInput.add).toHaveBeenCalled(); }); it('should have a default id', () => { expect(inputNativeElement.getAttribute('id')).toBeTruthy(); }); it('should allow binding to the `placeholder` input', () => { expect(inputNativeElement.hasAttribute('placeholder')).toBe(false); testChipInput.placeholder = 'bound placeholder'; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.getAttribute('placeholder')).toBe('bound placeholder'); }); it('should become disabled if the list is disabled', () => { expect(inputNativeElement.hasAttribute('disabled')).toBe(false); expect(chipInputDirective.disabled).toBe(false); fixture.componentInstance.chipGridInstance.disabled = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.disabled).toBe(true); expect(chipInputDirective.disabled).toBe(true); }); it('should be able to set an input as being disabled and interactive', () => { fixture.componentInstance.chipGridInstance.disabled = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.disabled).toBe(true); expect(inputNativeElement.readOnly).toBe(false); expect(inputNativeElement.hasAttribute('aria-disabled')).toBe(false); fixture.componentInstance.disabledInteractive = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.disabled).toBe(false); expect(inputNativeElement.readOnly).toBe(true); expect(inputNativeElement.getAttribute('aria-disabled')).toBe('true'); }); it('should be aria-required if the list is required', () => { expect(inputNativeElement.hasAttribute('aria-required')).toBe(false); fixture.componentInstance.required = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.getAttribute('aria-required')).toBe('true'); }); it('should be required if the list is required', () => { expect(inputNativeElement.hasAttribute('required')).toBe(false); fixture.componentInstance.required = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.getAttribute('required')).toBe('true'); }); it('should allow focus to escape when tabbing forwards', async () => { const gridElement: HTMLElement = fixture.nativeElement.querySelector('mat-chip-grid'); expect(gridElement.getAttribute('tabindex')).toBe('0'); dispatchKeyboardEvent(gridElement, 'keydown', TAB); fixture.detectChanges(); expect(gridElement.getAttribute('tabindex')) .withContext('Expected tabIndex to be set to -1 temporarily.') .toBe('-1'); await new Promise(r => setTimeout(r, 0)); fixture.detectChanges(); expect(gridElement.getAttribute('tabindex')) .withContext('Expected tabIndex to be reset back to 0') .toBe('0'); }); it('should set input styling classes', () => { expect(inputNativeElement.classList).toContain('mat-mdc-input-element'); expect(inputNativeElement.classList).toContain('mat-mdc-form-field-input-control'); expect(inputNativeElement.classList).toContain('mat-mdc-chip-input'); expect(inputNativeElement.classList).toContain('mdc-text-field__input'); }); it('should set `aria-describedby` to the id of the mat-hint', () => { expect(inputNativeElement.getAttribute('aria-describedby')).toBeNull(); fixture.componentInstance.hint = 'test'; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const hint = fixture.debugElement.query(By.css('mat-hint')).nativeElement; expect(inputNativeElement.getAttribute('aria-describedby')).toBe(hint.getAttribute('id')); expect(inputNativeElement.getAttribute('aria-describedby')).toMatch(/^mat-mdc-hint-\w+\d+$/); }); it('should support user binding to `aria-describedby`', () => { inputNativeElement.setAttribute('aria-describedby', 'test'); fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); expect(inputNativeElement.getAttribute('aria-describedby')).toBe('test'); }); it('should preserve aria-describedby set directly in the DOM', () => { inputNativeElement.setAttribute('aria-describedby', 'custom'); fixture.componentInstance.hint = 'test'; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); const hint = fixture.debugElement.query(By.css('mat-hint')).nativeElement; expect(inputNativeElement.getAttribute('aria-describedby')).toBe( `${hint.getAttribute('id')} custom`, ); }); }); describe('[addOnBlur]', () => { it('allows (chipEnd) when true', () => { spyOn(testChipInput, 'add'); testChipInput.addOnBlur = true; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); chipInputDirective._blur(); expect(testChipInput.add).toHaveBeenCalled(); }); it('disallows (chipEnd) when false', () => { spyOn(testChipInput, 'add'); testChipInput.addOnBlur = false; fixture.changeDetectorRef.markForCheck(); fixture.detectChanges(); chipInputDirective._blur(); expect(testChipInput.add).not.toHaveBeenCalled(); }); }); describe('[separatorKeyCodes]', () => { it('does not emit (chipEnd) when a non-separator key is pressed', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = [COMMA]; fixture.detectChanges(); dispatchKeyboardEvent(inputNativeElement, 'keydown', ENTER); expect(testChipInput.add).not.toHaveBeenCalled(); }); it('emits (chipEnd) when a custom separator keys is pressed', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = [COMMA]; fixture.detectChanges(); dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA); expect(testChipInput.add).toHaveBeenCalled(); }); it('emits accepts the custom separator keys in a Set', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = new Set([COMMA]); fixture.detectChanges(); dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA); expect(testChipInput.add).toHaveBeenCalled(); }); it('emits (chipEnd) when the separator keys are configured globally', () => { fixture.destroy(); TestBed.resetTestingModule().configureTestingModule({ providers: [ { provide: MAT_CHIPS_DEFAULT_OPTIONS, useValue: {separatorKeyCodes: [COMMA]} as MatChipsDefaultOptions, }, {provide: MATERIAL_ANIMATIONS, useValue: {animationsDisabled: true}}, ], }); fixture = TestBed.createComponent(TestChipInput); testChipInput = fixture.debugElement.componentInstance; fixture.detectChanges(); inputDebugElement = fixture.debugElement.query(By.directive(MatChipInput))!; chipInputDirective = inputDebugElement.injector.get<MatChipInput>(MatChipInput); inputNativeElement = inputDebugElement.nativeElement; spyOn(testChipInput, 'add'); fixture.detectChanges(); dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA); expect(testChipInput.add).toHaveBeenCalled(); }); it('should not emit the chipEnd event if a separator is pressed with a modifier key', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = [ENTER]; fixture.detectChanges(); dispatchKeyboardEvent(inputNativeElement, 'keydown', ENTER, undefined, {shift: true}); expect(testChipInput.add).not.toHaveBeenCalled(); }); it('should set aria-describedby correctly when a non-empty list of ids is passed to setDescribedByIds', () => { const ids = ['a', 'b', 'c']; testChipInput.chipGridInstance.setDescribedByIds(ids); fixture.detectChanges(); expect(inputNativeElement.getAttribute('aria-describedby')).toEqual('a b c'); }); it('should set aria-describedby correctly when an empty list of ids is passed to setDescribedByIds', () => { const ids: string[] = []; testChipInput.chipGridInstance.setDescribedByIds(ids); fixture.detectChanges(); expect(inputNativeElement.getAttribute('aria-describedby')).toBeNull(); }); it('should not emit chipEnd if the key is repeated', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = [COMMA]; fixture.detectChanges(); const event = createKeyboardEvent('keydown', COMMA); Object.defineProperty(event, 'repeat', {get: () => true}); dispatchEvent(inputNativeElement, event); fixture.detectChanges(); expect(testChipInput.add).not.toHaveBeenCalled(); }); it('should ignore modifier keys when `SeparatorKey.modifiers` is empty', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = [{keyCode: COMMA, modifiers: []}]; fixture.detectChanges(); // With a modifier. dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA, undefined, {shift: true}); expect(testChipInput.add).not.toHaveBeenCalled(); // Without a modifier. dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA); expect(testChipInput.add).toHaveBeenCalledTimes(1); }); it('should only allow modifiers from the `SeparatorKey.modifiers` array', () => { spyOn(testChipInput, 'add'); chipInputDirective.separatorKeyCodes = [{keyCode: COMMA, modifiers: ['ctrlKey']}]; fixture.detectChanges(); // Without a modifier. dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA); expect(testChipInput.add).not.toHaveBeenCalled(); // With a different modifier. dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA, undefined, {shift: true}); expect(testChipInput.add).not.toHaveBeenCalled(); // With the correct modifier. dispatchKeyboardEvent(inputNativeElement, 'keydown', COMMA, undefined, {control: true}); expect(testChipInput.add).toHaveBeenCalledTimes(1); }); }); }); @Component({ template: ` <mat-form-field [hintLabel]="hint"> <mat-chip-grid #chipGrid [required]="required"> <mat-chip-row>Hello</mat-chip-row> <input [matChipInputFor]="chipGrid" [matChipInputAddOnBlur]="addOnBlur" [matChipInputDisabledInteractive]="disabledInteractive" (matChipInputTokenEnd)="add($event)" [placeholder]="placeholder" /> </mat-chip-grid> </mat-form-field> `, imports: [MatFormField, MatChipGrid, MatChipRow, MatChipInput], changeDetection: ChangeDetectionStrategy.Eager, }) class TestChipInput { @ViewChild(MatChipGrid) chipGridInstance!: MatChipGrid; addOnBlur: boolean = false; placeholder = ''; required = false; disabledInteractive = false; hint = ''; add(_: MatChipInputEvent) {} }