为什么 HTML 组合框中的向下箭头会跳过一个但显示输入中的上一个选择?
Why does a down arrow in an HTML combo box skip one but shows the previous choice in the input?
我在 elasti-search 机制中有一个下拉框。
我正在使用 Angular,发生的事情是在代码重构之后,向下箭头跳过了一个选择,但它显示了输入中的先前选择。
我的意思是:
所以这是原始代码:
onKeydown(event: KeyboardEvent) {
Refactor this function to reduce its Cognitive Complexity from 52 to the 15 allowed.Why is this an issue?
let index = 0;
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.changeAria.emit('showall');
} else {
if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if(index >= 0 && index <= this.filteredItems.length - 1){
this.inputRef.nativeElement.value=this.filteredItems[index];
}
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
index = this.selectedItemIndex - 2;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if(index >= 0 && index <= this.filteredItems.length - 1){
this.inputRef.nativeElement.value=this.filteredItems[index];
}
} else if (index !== null && index >= 0) {
this.changeAria.emit(this.filteredItems[index]);
}
}
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
if (this.selectedItemIndex === 0) {
event.preventDefault();
const item = (this.filter !== undefined && this.filter !== null && this.filter !== '') ? this.filter : '';
this.onItemSelect(null, item);
} else if (this.selectedItemIndex > 0) {
event.preventDefault();
this.onItemSelect(null, this.filteredItems[this.selectedItemIndex - 1]);
}
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null || this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
} else {
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
}
}
} else if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else {
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
}else if ((event.keyCode==27) && this.isFocused) {
this.clearFocus();
this.inputRef.nativeElement.focus();
}
}
这是仅与 up/down 箭头移动相关的重构代码
/**
* @name: checkForSelected
* @description: Checks for Selected Index
* @argument: NONE
* @returns: boolean - meaning: there's no selected index found
*/
checkForSelected(): boolean {
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.hasNothingSelected = false;
} else {
this.hasNothingSelected = true;
}
return this.hasNothingSelected;
}
/**
* @name: checkForArrowDnUp
* @description: Checks for Arrow is up or down and has focused
* @param event as ANY
* @returns: boolean - meaning, whether the arrow is up or down 'and' focused
*/
checkForArrowDnUp(event: KeyboardEvent, index: number): string {
if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.autoCompleteInput.value = this.filteredItems[index];
}
this.arrowDnKeyDn = event.key;
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
index = this.selectedItemIndex - 2;
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.autoCompleteInput.value = this.filteredItems[index];
}
this.arrowDnKeyDn = event.key;
} else {
this.arrowDnKeyDn = '';
}
return this.arrowDnKeyDn;
}
/**
* @name: checkForNullOrZero
* @description: Checks for index being NULL or ZERO
* @param index
* @returns: boolean - Meaning, yes index is null OR Zero or greater than ZERO
*/
checkForNullOrZero(index: number): boolean {
this.isNdxNullOrZero = false;
if (index !== null && index >= 0) {
this.isNdxNullOrZero = true;
}
return this.isNdxNullOrZero;
}
/**
* @name: checkForEnterKeyUpDn
* @description: Checks for keyboard event if the user PRESSES the ENTER KEY
* @param event
* @returns: boolean - Meaning, YES the user has indeed pressed the ENTER KEY
* @default: variable DEFAULTS to FALSE
*/
checkForEnterKeyUpDn(event: KeyboardEvent): boolean {
this.isEnterKeyUpDn = false;
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
this.isEnterKeyUpDn = true;
}
return this.isEnterKeyUpDn;
}
/**
* @name: checkForSelectedItemIndexFiltered
* @description: Checks if the selected item has been filtered to a specific item index or not
* @param: NONE
* @returns: boolean - Meaning, YES the selected item has indeed been filter
* @default: variable DEFAULTS to FALSE
*/
checkForSelectedItemIndexFiltered(): boolean {
this.isSelectedItemNdxFoundFiltered = false;
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
return this.isSelectedItemNdxFoundFiltered;
}
checkForKeyEscape(event: KeyboardEvent): boolean {
this.isKeyDnUpEscape = false;
if ((event.key === 'Escape') && this.isFocused) {
this.isKeyDnUpEscape = true;
}
return this.isKeyDnUpEscape;
}
checkArrowKeysNullZero(arrownnullkey: string, nullzero: boolean, index: number): void {
this.autoCompleteInput.getAttribute('aria-activedescendant');
if (arrownnullkey) {
index = this.selectedItemIndex;
} else if (!arrownnullkey) {
index = this.selectedItemIndex - 2;
}
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.autoCompleteInput.value = this.filteredItems[index];
}
}
@HostListener('document:keydown', ['$event'])
handleOnKeydown(event: KeyboardEvent): void {
let item = this.filter;
let index = 0;
let nothingSelected = this.checkForSelected();
let arrowDnKeyUpDn = this.checkForArrowDnUp(event, index);
let enterKeyUpDn = this.checkForEnterKeyUpDn(event);
let indexNullOrZero = this.checkForNullOrZero(index);
let selectedItemNdxFiltered = this.checkForSelectedItemIndexFiltered();
let keyIsEscape = this.checkForKeyEscape(event);
if (nothingSelected) {
this.changeAria.emit('showall');
} else {
index = this.selectedItemIndex;
if (indexNullOrZero) {
this.changeAria.emit(this.filteredItems[index]);
} else {
this.checkArrowKeysNullZero(arrowDnKeyUpDn, indexNullOrZero, index);
}
if (index !== null && index >= 0) {
this.changeAria.emit(this.filteredItems[index]);
}
}
if (keyIsEscape) { // KeyCode = 27
this.clearFocus();
this.autoCompleteInput.focus();
} else {
if (enterKeyUpDn) {
this.enterKeyUpDown(item);
} else if (arrowDnKeyUpDn) {
this.arrowDownKeyUpDown(event, arrowDnKeyUpDn);
} else {
this.notArrowDownKeyUpDown(selectedItemNdxFiltered);
}
}
}
enterKeyUpDown(item: string): void {
switch (true) {
case this.selectedItemIndex === 0:
this.onItemSelect(null, item);
// this.onItemSelect(null, this.filteredItems[this.selectedItemIndex + 1]);
break;
case this.selectedItemIndex > 0:
this.onItemSelect(null, this.filteredItems[this.selectedItemIndex - 1]);
break;
default:
break;
}
}
arrowDownKeyUpDown(event: any, arrkeyvalue: string): void {
switch (arrkeyvalue) {
case 'ArrowUp':
case 'Up':
this.isArrowUp(event)
break;
case 'ArrowDown':
case 'Down':
this.isArrowDown(event);
break;
default:
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
}
break;
}
}
isArrowUp(event: KeyboardEvent): void {
if (this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null || this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
} else {
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
if (this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
}
}
}
}
}
isArrowDown(event: KeyboardEvent): void {
if (this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else {
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
}
}
notArrowDownKeyUpDown(selectedItemNdxFiltered: boolean): void {
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else if (selectedItemNdxFiltered) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
最后,向上箭头根本不起作用。
如果我放回旧代码,一切都很好。
所以,这一定是我在重构中所做的事情。
我重构的原因是 SonarQube 不喜欢所有的 if、then、else if、嵌套的 if。
从 52 期中,我将它减少到零。
我只需要克服这个障碍。
谢谢。
欧文,先生,你能像以前一样伸出援手吗?
更新!请参阅下面的答案。我修好了。
重构更多后,这是我的解决方案:
TS 文件:
import {
AfterViewInit, Component, ContentChild, ElementRef, EventEmitter, HostListener,
Input, Output, Renderer2, OnChanges
} from '@angular/core';
@Component({
selector: 'uhc-autocomplete',
templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss']
})
export class AutocompleteComponent implements OnChanges, AfterViewInit {
@ContentChild('secondarySearch') inputRef: ElementRef;
@Input() filter = '';
@Input() items: Array<any> = [];
@Output() itemSelect: EventEmitter<any> = new EventEmitter<any>();
@Output() changeAria: EventEmitter<any> = new EventEmitter<any>();
isFocused = true;
filteredItems = [];
selectedItemIndex = 0;
autoCompleteInput: HTMLInputElement;
keyBoardEvent: string;
item: string;
isArrowUpOrDown: string;
textSelected: any;
selectedEvent: Event;
constructor(private renderer: Renderer2) { }
ngOnChanges() {
this.filterItems();
}
ngAfterViewInit() {
this.autoCompleteInput = <HTMLInputElement>document.querySelector('.search-input');
this.bindOnBlurStateEventCallback();
this.bindOnInputStateEventCallback();
this.bindOnFocusStateEventCallback();
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
this.renderer.setAttribute(this.autoCompleteInput, 'aria-autocomplete', 'both');
if (this.filter !== undefined && this.filter !== null && this.filter !== '') {
this.filter = '';
}
}
}
bindOnBlurStateEventCallback(): void {
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
console.log('You selected: ', this.autoCompleteInput.value);
const blurCallback = (message: string) => {
console.log('Callback Message: ', message);
}
console.log('blurCallback ', blurCallback);
this.bindingBlur();
}
}
bindingBlur(): string {
console.log('Binding Blur called');
this.autoCompleteInput.addEventListener('blur', () => {
console.log('You selected: ', this.autoCompleteInput.value);
this.clearFocus();
});
return this.autoCompleteInput.value;
}
bindOnInputStateEventCallback(): void {
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
this.autoCompleteInput.addEventListener('onInput', () => {
console.log('You selected: ', this.inputRef.nativeElement.value);
});
this.bindingInput();
}
}
bindingInput(): string {
console.log('Binding Input called');
this.autoCompleteInput.addEventListener('onInput', () => {
console.log('You selected: ', this.autoCompleteInput.value);
});
return this.autoCompleteInput.value;
}
bindOnFocusStateEventCallback(): void {
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
this.inputRef.nativeElement.addEventListener('focus', this.onFocus.bind(this));
this.inputRef.nativeElement.addEventListener('onFocus', () => {
console.log('You selected: ', this.inputRef.nativeElement.value);
});
this.bindingFocus();
}
}
bindingFocus(): string {
console.log('Binding Focus called');
this.autoCompleteInput.addEventListener('onFocus', () => {
console.log('You selected: ', this.autoCompleteInput.value);
});
return this.autoCompleteInput.value;
}
clearFocus() {
if (this.autoCompleteInput !== undefined) {
console.log('Inside Clear Focus! ' + this.autoCompleteInput.value);
this.autoCompleteInput.blur();
}
}
onBlur() {
this.selectedItemIndex = 0;
this.isFocused = false;
}
onItemSelect(event: Event, item: string) {
if (item) {
this.selectedEvent = event;
this.itemSelect.emit(item);
this.clearFocus();
} else {
this.clearFocus();
}
}
onFocus(event: Event) {
this.selectedEvent = event;
this.filterItems();
this.isFocused = true;
}
onInput(event: Event) {
this.selectedEvent = event;
this.filterItems();
}
onMouseDown(event: { preventDefault: () => void; }) {
event.preventDefault();
}
mouseEnter(i: number) {
this.selectedItemIndex = i + 1;
}
uniq(a: Iterable<unknown>) {
return Array.from(new Set(a));
}
filterItems() {
this.filteredItems = this.uniq(this.items);
}
keyDownArrowDown(event: KeyboardEvent, index: number): void {
this.keyBoardEvent = event.key;
let kybrdEvnt = event.key
if ((kybrdEvnt === 'ArrowDown' || kybrdEvnt === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.inputRef.nativeElement.value = this.filteredItems[index];
}
} else if ((kybrdEvnt === 'ArrowUp' || kybrdEvnt === 'Up') && this.isFocused) {
index = this.selectedItemIndex - 2;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.inputRef.nativeElement.value = this.filteredItems[index];
}
} else if (index !== null && index >= 0) {
this.changeAria.emit(this.filteredItems[index]);
}
}
enterKeyDown(event: KeyboardEvent): void {
const item = (this.filter !== undefined && this.filter !== null && this.filter !== '') ? this.filter : '';
this.item = item;
if (this.selectedItemIndex === 0) {
event.preventDefault();
this.onItemSelect(null, item);
} else if (this.selectedItemIndex > 0) {
event.preventDefault();
this.onItemSelect(null, this.filteredItems[this.selectedItemIndex - 1]);
}
}
altArrowUpNoEnter(event: KeyboardEvent): void {
this.isArrowUpOrDown = event.key;
if (this.selectedItemIndex === null || this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
} else {
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
}
}
}
altArrowDnNoEnter(event: KeyboardEvent): void {
this.isArrowUpOrDown = event.key;
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else {
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
}
@HostListener('document:keydown', ['$event'])
handleOnKeydown(event: KeyboardEvent) {
let index = 0;
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.changeAria.emit('showall');
} else {
this.keyDownArrowDown(event, index);
}
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
this.enterKeyDown(event);
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
event.preventDefault();
this.altArrowUpNoEnter(event);
} else if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
event.preventDefault();
this.altArrowDnNoEnter(event);
} else if ((event.key === 'Escape') && this.isFocused) {
this.clearFocus();
this.inputRef.nativeElement.focus();
}
}
patternTextForIdAttribute(text: any): string {
this.textSelected = text;
return text.replace(/\ /g, '_');
}
handleEvent = (event: KeyboardEvent) => {
const { key } = event;
}
}
规范 TS 文件:
import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement, ɵɵNgOnChangesFeature } from '@angular/core';
import { AutocompleteComponent } from './autocomplete.component';
import { not } from '@angular/compiler/src/output/output_ast';
describe('Auto Complete Component', () => {
let autoCompleteComponent: AutocompleteComponent;
let fixture: ComponentFixture<AutocompleteComponent>;
let autocompleteInput: HTMLInputElement;
let filteredItems: string[] = [];
let item: string;
let nbr: number;
let selectedItemIndex: number;
let e1: { ElementRef: { blur: any; }; };
let e2: { ElementRef: { focus: any; }; };
let e3: { ElementRef: { input: any; }; };
let nothingSelected: boolean;
let rendered: DebugElement;
let event: Event;
let eventListenerService: jasmine.SpyObj<AutocompleteComponent>;
const spy = jasmine.createSpy();
beforeEach(async(() => {
eventListenerService = jasmine.createSpyObj('onBlur', ['blur']);
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([])
],
declarations: [
AutocompleteComponent
],
providers: [AutocompleteComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(AutocompleteComponent);
autoCompleteComponent = fixture.componentInstance;
autocompleteInput = <HTMLInputElement>document.querySelector('.search-input');
});
}));
it('adds listener events', function () {
spyOn(document, 'addEventListener').and.callThrough();
spyOn(window, 'addEventListener').and.callThrough();
expect(document.addEventListener.prototype).not.toBeTruthy;
expect(window.addEventListener.prototype).not.toBeTruthy;
expect(document.addEventListener.prototype).toBeTruthy;
expect(window.addEventListener.prototype).toBeTruthy;
// expect(autoCompleteComponent.autoCompleteInput.addEventListener.prototype).toBeTruthy;
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AutocompleteComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it("when called multiple times ngAfterViewInit() and fires all three callbacks in order", () => {
spyOn(autoCompleteComponent, 'ngAfterViewInit').and.callThrough();
autoCompleteComponent.ngAfterViewInit();
expect(autoCompleteComponent.ngAfterViewInit).toHaveBeenCalled();
spyOn(autoCompleteComponent, 'bindOnBlurStateEventCallback').and.callThrough();
autoCompleteComponent.bindOnBlurStateEventCallback();
expect(autoCompleteComponent.bindOnBlurStateEventCallback).toBeTruthy();
expect(autoCompleteComponent.bindOnBlurStateEventCallback).toHaveBeenCalled();
spyOn(autoCompleteComponent, 'bindOnInputStateEventCallback').and.callThrough();
autoCompleteComponent.bindOnInputStateEventCallback();
expect(autoCompleteComponent.bindOnInputStateEventCallback).toBeTruthy();
expect(autoCompleteComponent.bindOnInputStateEventCallback).toHaveBeenCalled();
spyOn(autoCompleteComponent, 'bindOnFocusStateEventCallback').and.callThrough();
autoCompleteComponent.bindOnFocusStateEventCallback();
expect(autoCompleteComponent.bindOnFocusStateEventCallback).toBeTruthy();
expect(autoCompleteComponent.bindOnFocusStateEventCallback).toHaveBeenCalled();
});
it('uniq function call..', () => {
const filterKeywords = ['drug', 'drug'];
var removeDupes = autoCompleteComponent.uniq(filterKeywords);
expect(removeDupes).toEqual(['drug']);
});
it('should cover test case for filterItems function', () => {
const filterKeywords = ['drug', 'drug'];
autoCompleteComponent.filterItems();
autoCompleteComponent.filteredItems = autoCompleteComponent.uniq(filterKeywords);
expect(autoCompleteComponent.filteredItems).toEqual(['drug']);
});
it('should call patternTextForIdAttribute function ', () => {
var result = autoCompleteComponent.patternTextForIdAttribute('site search');
expect(result).toBe('site_search');
});
it('should create call ngOnChanges ', () => {
spyOn(autoCompleteComponent, 'ngOnChanges').and.callThrough();
autoCompleteComponent.ngOnChanges();
spyOn(autoCompleteComponent, 'filterItems').and.callThrough();
autoCompleteComponent.filterItems();
expect(autoCompleteComponent.filterItems).toHaveBeenCalled();
expect(autoCompleteComponent.ngOnChanges).toHaveBeenCalled();
});
it('should call onBlur Event ', () => {
spyOn(autoCompleteComponent, 'onBlur').and.callThrough();
autoCompleteComponent.onBlur();
expect(autoCompleteComponent.onBlur).toHaveBeenCalled();
});
it('should create call clearFocus ', () => {
const checkForInputBlur = spyOn(autoCompleteComponent, 'onBlur');
checkForInputBlur.and.callThrough();
let autoCompValue = '';
if (autoCompleteComponent.autoCompleteInput === undefined) {
autoCompValue = 'Drugs';
}
fixture.whenStable().then(() => {
expect(autoCompleteComponent.autoCompleteInput.blur).toHaveBeenCalled();
// expect(autoCompleteComponent.autoCompleteInput.onblur).toContain(autocompleteInput);
});
if (autoCompValue !== '' || autoCompValue !== undefined) {
spyOn(autoCompleteComponent,'clearFocus');
// expect(autoCompleteComponent).toContain('clearFocus');
expect(autoCompleteComponent.autoCompleteInput).toBeUndefined();
autoCompleteComponent.clearFocus();
expect(autoCompleteComponent.clearFocus).toHaveBeenCalled();
} else {
expect(autoCompleteComponent.autoCompleteInput).toBeUndefined();
expect(autoCompleteComponent.clearFocus()).not.toBeUndefined();
}
});
it('should call onItemSelect Event ', () => {
let item = 'drug';
spyOn(autoCompleteComponent, 'onItemSelect').and.callThrough();
if (autoCompleteComponent.item) {
autoCompleteComponent.onItemSelect(event, item);
expect(autoCompleteComponent.onItemSelect(event, item)).toHaveBeenCalled();
} else {
expect(autoCompleteComponent.onItemSelect(event, item)).toBeUndefined();
}
spyOn(autoCompleteComponent, 'clearFocus').and.callThrough();
// expect(autoCompleteComponent.clearFocus).toHaveBeenCalled();
});
it('should call onFocus Event ', () => {
autoCompleteComponent.onFocus(event);
spyOn(autoCompleteComponent, 'onFocus').and.callThrough();
autoCompleteComponent.filterItems();
spyOn(autoCompleteComponent, 'filterItems').and.callThrough();
expect(autoCompleteComponent.isFocused).toEqual(true);
expect(autoCompleteComponent.filteredItems).not.toBeNull;
expect(autoCompleteComponent.filterItems).not.toHaveBeenCalled();
expect(autoCompleteComponent.onFocus).not.toHaveBeenCalled();
});
it('should call onInput Event ', () => {
spyOn(autoCompleteComponent, 'onInput').and.callThrough();
autoCompleteComponent.onInput(event);
expect(autoCompleteComponent.onInput).toHaveBeenCalled();
});
it('should call mouseEnter Event ', () => {
nbr = 1;
spyOn(autoCompleteComponent, 'mouseEnter').and.callThrough();
autoCompleteComponent.mouseEnter(nbr);
expect(autoCompleteComponent.mouseEnter).toHaveBeenCalled();
});
it('should call keyDownArrowDown Event ', () => {
// const keyDnEvent = window.addEventListener('keydown', e => {
// autoCompleteComponent.handleEvent(e); // e is KeyboardEvent
// });
// const keyUpEvent = window.addEventListener('keyup', e => {
// autoCompleteComponent.handleEvent(e); // e is KeyboardEvent
// });
spyOn(autoCompleteComponent, 'keyDownArrowDown').and.callThrough();
let e = jasmine.createSpyObj('e', ['KeyboardEvent']);
let index = 0;
let isFocused: boolean;
isFocused = true;
let eventKey = autoCompleteComponent.keyBoardEvent;
if ((eventKey === 'ArrowDown' || eventKey === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
autoCompleteComponent.autoCompleteInput.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
autoCompleteComponent.autoCompleteInput.value = autoCompleteComponent.filteredItems[index];
}
} else if ((eventKey === 'ArrowUp' || eventKey === 'Up') && autoCompleteComponent.isFocused) {
index = autoCompleteComponent.selectedItemIndex - 2;
autoCompleteComponent.autoCompleteInput.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
autoCompleteComponent.autoCompleteInput.value = autoCompleteComponent.filteredItems[index];
}
} else if (index !== null && index >= 0) {
autoCompleteComponent.changeAria.emit(autoCompleteComponent.filteredItems[index]);
}
// expect(autoCompleteComponent.keyDownArrowDown).toEqual('ArrowDown');
// expect(autoCompleteComponent.keyDownArrowDown).toBeUndefined();
expect(autoCompleteComponent.keyDownArrowDown(e, index)).toBeUndefined();
});
it('should call handleOnKeydown function ', () => {
let item = autoCompleteComponent.filter;
let index = 0;
spyOn(autoCompleteComponent, 'handleOnKeydown').and.callFake((event: any): void => {
let index = 0;
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.changeAria.emit('showall');
} else {
this.keyDownArrowDown(event, index);
}
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
this.enterKeyDown(event);
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
event.preventDefault();
this.altArrowUpNoEnter(event);
} else if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
event.preventDefault();
this.altArrowDnNoEnter(event);
} else if ((event.key === 'Escape') && this.isFocused) {
this.clearFocus();
this.inputRef.nativeElement.focus();
}
});
});
it('should call enterKeyDown function ', () => {
let item = autoCompleteComponent.item;
let selectedItemIndex = autoCompleteComponent.selectedItemIndex;
let filtereditems = autoCompleteComponent.filteredItems;
spyOn(autoCompleteComponent, 'enterKeyDown').and.callFake(() => {
if (selectedItemIndex === 0) {
event.preventDefault();
autoCompleteComponent.onItemSelect(null, item);
} else if (selectedItemIndex > 0) {
event.preventDefault();
autoCompleteComponent.onItemSelect(null, filtereditems[selectedItemIndex - 1]);
}
expect(autoCompleteComponent.enterKeyDown).toBeUndefined();
expect(autoCompleteComponent.enterKeyDown).toHaveBeenCalled();
});
});
it('should call altArrowUpNoEnter (ARROWUP UP) function ', () => {
let e = jasmine.createSpyObj('e', ['preventDefault']);
autoCompleteComponent.altArrowUpNoEnter(e);
spyOn(autoCompleteComponent, 'altArrowUpNoEnter').and.callFake((): void => {
if (autoCompleteComponent.selectedItemIndex === null || autoCompleteComponent.selectedItemIndex === 0) {
autoCompleteComponent.selectedItemIndex = autoCompleteComponent.filteredItems.length;
} else {
if (autoCompleteComponent.selectedItemIndex > 0) {
autoCompleteComponent.selectedItemIndex--;
}
}
});
autoCompleteComponent.altArrowUpNoEnter(e);
expect(autoCompleteComponent.altArrowUpNoEnter).toBeTruthy();
});
it('should call altArrowDnNoEnter (ARROWUP DN) function ', () => {
let e = jasmine.createSpyObj('e', ['preventDefault']);
autoCompleteComponent.altArrowDnNoEnter(e);
spyOn(autoCompleteComponent, 'altArrowDnNoEnter').and.callFake((): void => {
if (autoCompleteComponent.selectedItemIndex === null || autoCompleteComponent.selectedItemIndex === 0) {
autoCompleteComponent.selectedItemIndex = autoCompleteComponent.filteredItems.length;
} else {
if (autoCompleteComponent.selectedItemIndex > 0) {
autoCompleteComponent.selectedItemIndex--;
}
}
});
autoCompleteComponent.altArrowDnNoEnter(e);
expect(autoCompleteComponent.altArrowDnNoEnter).toBeTruthy();
});
it('should call patternTextForIdAttribute function ', () => {
let text = autoCompleteComponent.textSelected;
let retValText: string;
let retVal = spyOn(autoCompleteComponent, 'patternTextForIdAttribute').and.callFake(() => {
if (text) {
retValText = text.replace(/\ /g, '_');
} else {
retValText = '';
}
return retValText;
});
autoCompleteComponent.patternTextForIdAttribute(text);
expect(autoCompleteComponent.patternTextForIdAttribute).toBeTruthy();
});
});
我在 elasti-search 机制中有一个下拉框。
我正在使用 Angular,发生的事情是在代码重构之后,向下箭头跳过了一个选择,但它显示了输入中的先前选择。
我的意思是:
所以这是原始代码:
onKeydown(event: KeyboardEvent) {
Refactor this function to reduce its Cognitive Complexity from 52 to the 15 allowed.Why is this an issue?
let index = 0;
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.changeAria.emit('showall');
} else {
if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if(index >= 0 && index <= this.filteredItems.length - 1){
this.inputRef.nativeElement.value=this.filteredItems[index];
}
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
index = this.selectedItemIndex - 2;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if(index >= 0 && index <= this.filteredItems.length - 1){
this.inputRef.nativeElement.value=this.filteredItems[index];
}
} else if (index !== null && index >= 0) {
this.changeAria.emit(this.filteredItems[index]);
}
}
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
if (this.selectedItemIndex === 0) {
event.preventDefault();
const item = (this.filter !== undefined && this.filter !== null && this.filter !== '') ? this.filter : '';
this.onItemSelect(null, item);
} else if (this.selectedItemIndex > 0) {
event.preventDefault();
this.onItemSelect(null, this.filteredItems[this.selectedItemIndex - 1]);
}
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null || this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
} else {
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
}
}
} else if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else {
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
}else if ((event.keyCode==27) && this.isFocused) {
this.clearFocus();
this.inputRef.nativeElement.focus();
}
}
这是仅与 up/down 箭头移动相关的重构代码
/**
* @name: checkForSelected
* @description: Checks for Selected Index
* @argument: NONE
* @returns: boolean - meaning: there's no selected index found
*/
checkForSelected(): boolean {
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.hasNothingSelected = false;
} else {
this.hasNothingSelected = true;
}
return this.hasNothingSelected;
}
/**
* @name: checkForArrowDnUp
* @description: Checks for Arrow is up or down and has focused
* @param event as ANY
* @returns: boolean - meaning, whether the arrow is up or down 'and' focused
*/
checkForArrowDnUp(event: KeyboardEvent, index: number): string {
if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.autoCompleteInput.value = this.filteredItems[index];
}
this.arrowDnKeyDn = event.key;
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
index = this.selectedItemIndex - 2;
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.autoCompleteInput.value = this.filteredItems[index];
}
this.arrowDnKeyDn = event.key;
} else {
this.arrowDnKeyDn = '';
}
return this.arrowDnKeyDn;
}
/**
* @name: checkForNullOrZero
* @description: Checks for index being NULL or ZERO
* @param index
* @returns: boolean - Meaning, yes index is null OR Zero or greater than ZERO
*/
checkForNullOrZero(index: number): boolean {
this.isNdxNullOrZero = false;
if (index !== null && index >= 0) {
this.isNdxNullOrZero = true;
}
return this.isNdxNullOrZero;
}
/**
* @name: checkForEnterKeyUpDn
* @description: Checks for keyboard event if the user PRESSES the ENTER KEY
* @param event
* @returns: boolean - Meaning, YES the user has indeed pressed the ENTER KEY
* @default: variable DEFAULTS to FALSE
*/
checkForEnterKeyUpDn(event: KeyboardEvent): boolean {
this.isEnterKeyUpDn = false;
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
this.isEnterKeyUpDn = true;
}
return this.isEnterKeyUpDn;
}
/**
* @name: checkForSelectedItemIndexFiltered
* @description: Checks if the selected item has been filtered to a specific item index or not
* @param: NONE
* @returns: boolean - Meaning, YES the selected item has indeed been filter
* @default: variable DEFAULTS to FALSE
*/
checkForSelectedItemIndexFiltered(): boolean {
this.isSelectedItemNdxFoundFiltered = false;
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
return this.isSelectedItemNdxFoundFiltered;
}
checkForKeyEscape(event: KeyboardEvent): boolean {
this.isKeyDnUpEscape = false;
if ((event.key === 'Escape') && this.isFocused) {
this.isKeyDnUpEscape = true;
}
return this.isKeyDnUpEscape;
}
checkArrowKeysNullZero(arrownnullkey: string, nullzero: boolean, index: number): void {
this.autoCompleteInput.getAttribute('aria-activedescendant');
if (arrownnullkey) {
index = this.selectedItemIndex;
} else if (!arrownnullkey) {
index = this.selectedItemIndex - 2;
}
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.autoCompleteInput.value = this.filteredItems[index];
}
}
@HostListener('document:keydown', ['$event'])
handleOnKeydown(event: KeyboardEvent): void {
let item = this.filter;
let index = 0;
let nothingSelected = this.checkForSelected();
let arrowDnKeyUpDn = this.checkForArrowDnUp(event, index);
let enterKeyUpDn = this.checkForEnterKeyUpDn(event);
let indexNullOrZero = this.checkForNullOrZero(index);
let selectedItemNdxFiltered = this.checkForSelectedItemIndexFiltered();
let keyIsEscape = this.checkForKeyEscape(event);
if (nothingSelected) {
this.changeAria.emit('showall');
} else {
index = this.selectedItemIndex;
if (indexNullOrZero) {
this.changeAria.emit(this.filteredItems[index]);
} else {
this.checkArrowKeysNullZero(arrowDnKeyUpDn, indexNullOrZero, index);
}
if (index !== null && index >= 0) {
this.changeAria.emit(this.filteredItems[index]);
}
}
if (keyIsEscape) { // KeyCode = 27
this.clearFocus();
this.autoCompleteInput.focus();
} else {
if (enterKeyUpDn) {
this.enterKeyUpDown(item);
} else if (arrowDnKeyUpDn) {
this.arrowDownKeyUpDown(event, arrowDnKeyUpDn);
} else {
this.notArrowDownKeyUpDown(selectedItemNdxFiltered);
}
}
}
enterKeyUpDown(item: string): void {
switch (true) {
case this.selectedItemIndex === 0:
this.onItemSelect(null, item);
// this.onItemSelect(null, this.filteredItems[this.selectedItemIndex + 1]);
break;
case this.selectedItemIndex > 0:
this.onItemSelect(null, this.filteredItems[this.selectedItemIndex - 1]);
break;
default:
break;
}
}
arrowDownKeyUpDown(event: any, arrkeyvalue: string): void {
switch (arrkeyvalue) {
case 'ArrowUp':
case 'Up':
this.isArrowUp(event)
break;
case 'ArrowDown':
case 'Down':
this.isArrowDown(event);
break;
default:
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
}
break;
}
}
isArrowUp(event: KeyboardEvent): void {
if (this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null || this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
} else {
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
if (this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
}
}
}
}
}
isArrowDown(event: KeyboardEvent): void {
if (this.isFocused) {
event.preventDefault();
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else {
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
}
}
notArrowDownKeyUpDown(selectedItemNdxFiltered: boolean): void {
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else if (selectedItemNdxFiltered) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
最后,向上箭头根本不起作用。
如果我放回旧代码,一切都很好。
所以,这一定是我在重构中所做的事情。
我重构的原因是 SonarQube 不喜欢所有的 if、then、else if、嵌套的 if。
从 52 期中,我将它减少到零。
我只需要克服这个障碍。
谢谢。
欧文,先生,你能像以前一样伸出援手吗?
更新!请参阅下面的答案。我修好了。
重构更多后,这是我的解决方案:
TS 文件:
import {
AfterViewInit, Component, ContentChild, ElementRef, EventEmitter, HostListener,
Input, Output, Renderer2, OnChanges
} from '@angular/core';
@Component({
selector: 'uhc-autocomplete',
templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss']
})
export class AutocompleteComponent implements OnChanges, AfterViewInit {
@ContentChild('secondarySearch') inputRef: ElementRef;
@Input() filter = '';
@Input() items: Array<any> = [];
@Output() itemSelect: EventEmitter<any> = new EventEmitter<any>();
@Output() changeAria: EventEmitter<any> = new EventEmitter<any>();
isFocused = true;
filteredItems = [];
selectedItemIndex = 0;
autoCompleteInput: HTMLInputElement;
keyBoardEvent: string;
item: string;
isArrowUpOrDown: string;
textSelected: any;
selectedEvent: Event;
constructor(private renderer: Renderer2) { }
ngOnChanges() {
this.filterItems();
}
ngAfterViewInit() {
this.autoCompleteInput = <HTMLInputElement>document.querySelector('.search-input');
this.bindOnBlurStateEventCallback();
this.bindOnInputStateEventCallback();
this.bindOnFocusStateEventCallback();
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
this.renderer.setAttribute(this.autoCompleteInput, 'aria-autocomplete', 'both');
if (this.filter !== undefined && this.filter !== null && this.filter !== '') {
this.filter = '';
}
}
}
bindOnBlurStateEventCallback(): void {
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
console.log('You selected: ', this.autoCompleteInput.value);
const blurCallback = (message: string) => {
console.log('Callback Message: ', message);
}
console.log('blurCallback ', blurCallback);
this.bindingBlur();
}
}
bindingBlur(): string {
console.log('Binding Blur called');
this.autoCompleteInput.addEventListener('blur', () => {
console.log('You selected: ', this.autoCompleteInput.value);
this.clearFocus();
});
return this.autoCompleteInput.value;
}
bindOnInputStateEventCallback(): void {
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
this.autoCompleteInput.addEventListener('onInput', () => {
console.log('You selected: ', this.inputRef.nativeElement.value);
});
this.bindingInput();
}
}
bindingInput(): string {
console.log('Binding Input called');
this.autoCompleteInput.addEventListener('onInput', () => {
console.log('You selected: ', this.autoCompleteInput.value);
});
return this.autoCompleteInput.value;
}
bindOnFocusStateEventCallback(): void {
if (this.autoCompleteInput && this.autoCompleteInput.value.length > 0) {
this.inputRef.nativeElement.addEventListener('focus', this.onFocus.bind(this));
this.inputRef.nativeElement.addEventListener('onFocus', () => {
console.log('You selected: ', this.inputRef.nativeElement.value);
});
this.bindingFocus();
}
}
bindingFocus(): string {
console.log('Binding Focus called');
this.autoCompleteInput.addEventListener('onFocus', () => {
console.log('You selected: ', this.autoCompleteInput.value);
});
return this.autoCompleteInput.value;
}
clearFocus() {
if (this.autoCompleteInput !== undefined) {
console.log('Inside Clear Focus! ' + this.autoCompleteInput.value);
this.autoCompleteInput.blur();
}
}
onBlur() {
this.selectedItemIndex = 0;
this.isFocused = false;
}
onItemSelect(event: Event, item: string) {
if (item) {
this.selectedEvent = event;
this.itemSelect.emit(item);
this.clearFocus();
} else {
this.clearFocus();
}
}
onFocus(event: Event) {
this.selectedEvent = event;
this.filterItems();
this.isFocused = true;
}
onInput(event: Event) {
this.selectedEvent = event;
this.filterItems();
}
onMouseDown(event: { preventDefault: () => void; }) {
event.preventDefault();
}
mouseEnter(i: number) {
this.selectedItemIndex = i + 1;
}
uniq(a: Iterable<unknown>) {
return Array.from(new Set(a));
}
filterItems() {
this.filteredItems = this.uniq(this.items);
}
keyDownArrowDown(event: KeyboardEvent, index: number): void {
this.keyBoardEvent = event.key;
let kybrdEvnt = event.key
if ((kybrdEvnt === 'ArrowDown' || kybrdEvnt === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.inputRef.nativeElement.value = this.filteredItems[index];
}
} else if ((kybrdEvnt === 'ArrowUp' || kybrdEvnt === 'Up') && this.isFocused) {
index = this.selectedItemIndex - 2;
this.inputRef.nativeElement.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
this.inputRef.nativeElement.value = this.filteredItems[index];
}
} else if (index !== null && index >= 0) {
this.changeAria.emit(this.filteredItems[index]);
}
}
enterKeyDown(event: KeyboardEvent): void {
const item = (this.filter !== undefined && this.filter !== null && this.filter !== '') ? this.filter : '';
this.item = item;
if (this.selectedItemIndex === 0) {
event.preventDefault();
this.onItemSelect(null, item);
} else if (this.selectedItemIndex > 0) {
event.preventDefault();
this.onItemSelect(null, this.filteredItems[this.selectedItemIndex - 1]);
}
}
altArrowUpNoEnter(event: KeyboardEvent): void {
this.isArrowUpOrDown = event.key;
if (this.selectedItemIndex === null || this.selectedItemIndex === 0) {
this.selectedItemIndex = this.filteredItems.length;
} else {
if (this.selectedItemIndex > 0) {
this.selectedItemIndex--;
}
}
}
altArrowDnNoEnter(event: KeyboardEvent): void {
this.isArrowUpOrDown = event.key;
if (this.selectedItemIndex === null) {
this.selectedItemIndex = 0;
} else {
if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.filteredItems.length) {
this.selectedItemIndex++;
} else {
this.selectedItemIndex = 0;
}
}
}
@HostListener('document:keydown', ['$event'])
handleOnKeydown(event: KeyboardEvent) {
let index = 0;
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.changeAria.emit('showall');
} else {
this.keyDownArrowDown(event, index);
}
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
this.enterKeyDown(event);
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
event.preventDefault();
this.altArrowUpNoEnter(event);
} else if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
event.preventDefault();
this.altArrowDnNoEnter(event);
} else if ((event.key === 'Escape') && this.isFocused) {
this.clearFocus();
this.inputRef.nativeElement.focus();
}
}
patternTextForIdAttribute(text: any): string {
this.textSelected = text;
return text.replace(/\ /g, '_');
}
handleEvent = (event: KeyboardEvent) => {
const { key } = event;
}
}
规范 TS 文件:
import { TestBed, async, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement, ɵɵNgOnChangesFeature } from '@angular/core';
import { AutocompleteComponent } from './autocomplete.component';
import { not } from '@angular/compiler/src/output/output_ast';
describe('Auto Complete Component', () => {
let autoCompleteComponent: AutocompleteComponent;
let fixture: ComponentFixture<AutocompleteComponent>;
let autocompleteInput: HTMLInputElement;
let filteredItems: string[] = [];
let item: string;
let nbr: number;
let selectedItemIndex: number;
let e1: { ElementRef: { blur: any; }; };
let e2: { ElementRef: { focus: any; }; };
let e3: { ElementRef: { input: any; }; };
let nothingSelected: boolean;
let rendered: DebugElement;
let event: Event;
let eventListenerService: jasmine.SpyObj<AutocompleteComponent>;
const spy = jasmine.createSpy();
beforeEach(async(() => {
eventListenerService = jasmine.createSpyObj('onBlur', ['blur']);
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes([])
],
declarations: [
AutocompleteComponent
],
providers: [AutocompleteComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents().then(() => {
fixture = TestBed.createComponent(AutocompleteComponent);
autoCompleteComponent = fixture.componentInstance;
autocompleteInput = <HTMLInputElement>document.querySelector('.search-input');
});
}));
it('adds listener events', function () {
spyOn(document, 'addEventListener').and.callThrough();
spyOn(window, 'addEventListener').and.callThrough();
expect(document.addEventListener.prototype).not.toBeTruthy;
expect(window.addEventListener.prototype).not.toBeTruthy;
expect(document.addEventListener.prototype).toBeTruthy;
expect(window.addEventListener.prototype).toBeTruthy;
// expect(autoCompleteComponent.autoCompleteInput.addEventListener.prototype).toBeTruthy;
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AutocompleteComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it("when called multiple times ngAfterViewInit() and fires all three callbacks in order", () => {
spyOn(autoCompleteComponent, 'ngAfterViewInit').and.callThrough();
autoCompleteComponent.ngAfterViewInit();
expect(autoCompleteComponent.ngAfterViewInit).toHaveBeenCalled();
spyOn(autoCompleteComponent, 'bindOnBlurStateEventCallback').and.callThrough();
autoCompleteComponent.bindOnBlurStateEventCallback();
expect(autoCompleteComponent.bindOnBlurStateEventCallback).toBeTruthy();
expect(autoCompleteComponent.bindOnBlurStateEventCallback).toHaveBeenCalled();
spyOn(autoCompleteComponent, 'bindOnInputStateEventCallback').and.callThrough();
autoCompleteComponent.bindOnInputStateEventCallback();
expect(autoCompleteComponent.bindOnInputStateEventCallback).toBeTruthy();
expect(autoCompleteComponent.bindOnInputStateEventCallback).toHaveBeenCalled();
spyOn(autoCompleteComponent, 'bindOnFocusStateEventCallback').and.callThrough();
autoCompleteComponent.bindOnFocusStateEventCallback();
expect(autoCompleteComponent.bindOnFocusStateEventCallback).toBeTruthy();
expect(autoCompleteComponent.bindOnFocusStateEventCallback).toHaveBeenCalled();
});
it('uniq function call..', () => {
const filterKeywords = ['drug', 'drug'];
var removeDupes = autoCompleteComponent.uniq(filterKeywords);
expect(removeDupes).toEqual(['drug']);
});
it('should cover test case for filterItems function', () => {
const filterKeywords = ['drug', 'drug'];
autoCompleteComponent.filterItems();
autoCompleteComponent.filteredItems = autoCompleteComponent.uniq(filterKeywords);
expect(autoCompleteComponent.filteredItems).toEqual(['drug']);
});
it('should call patternTextForIdAttribute function ', () => {
var result = autoCompleteComponent.patternTextForIdAttribute('site search');
expect(result).toBe('site_search');
});
it('should create call ngOnChanges ', () => {
spyOn(autoCompleteComponent, 'ngOnChanges').and.callThrough();
autoCompleteComponent.ngOnChanges();
spyOn(autoCompleteComponent, 'filterItems').and.callThrough();
autoCompleteComponent.filterItems();
expect(autoCompleteComponent.filterItems).toHaveBeenCalled();
expect(autoCompleteComponent.ngOnChanges).toHaveBeenCalled();
});
it('should call onBlur Event ', () => {
spyOn(autoCompleteComponent, 'onBlur').and.callThrough();
autoCompleteComponent.onBlur();
expect(autoCompleteComponent.onBlur).toHaveBeenCalled();
});
it('should create call clearFocus ', () => {
const checkForInputBlur = spyOn(autoCompleteComponent, 'onBlur');
checkForInputBlur.and.callThrough();
let autoCompValue = '';
if (autoCompleteComponent.autoCompleteInput === undefined) {
autoCompValue = 'Drugs';
}
fixture.whenStable().then(() => {
expect(autoCompleteComponent.autoCompleteInput.blur).toHaveBeenCalled();
// expect(autoCompleteComponent.autoCompleteInput.onblur).toContain(autocompleteInput);
});
if (autoCompValue !== '' || autoCompValue !== undefined) {
spyOn(autoCompleteComponent,'clearFocus');
// expect(autoCompleteComponent).toContain('clearFocus');
expect(autoCompleteComponent.autoCompleteInput).toBeUndefined();
autoCompleteComponent.clearFocus();
expect(autoCompleteComponent.clearFocus).toHaveBeenCalled();
} else {
expect(autoCompleteComponent.autoCompleteInput).toBeUndefined();
expect(autoCompleteComponent.clearFocus()).not.toBeUndefined();
}
});
it('should call onItemSelect Event ', () => {
let item = 'drug';
spyOn(autoCompleteComponent, 'onItemSelect').and.callThrough();
if (autoCompleteComponent.item) {
autoCompleteComponent.onItemSelect(event, item);
expect(autoCompleteComponent.onItemSelect(event, item)).toHaveBeenCalled();
} else {
expect(autoCompleteComponent.onItemSelect(event, item)).toBeUndefined();
}
spyOn(autoCompleteComponent, 'clearFocus').and.callThrough();
// expect(autoCompleteComponent.clearFocus).toHaveBeenCalled();
});
it('should call onFocus Event ', () => {
autoCompleteComponent.onFocus(event);
spyOn(autoCompleteComponent, 'onFocus').and.callThrough();
autoCompleteComponent.filterItems();
spyOn(autoCompleteComponent, 'filterItems').and.callThrough();
expect(autoCompleteComponent.isFocused).toEqual(true);
expect(autoCompleteComponent.filteredItems).not.toBeNull;
expect(autoCompleteComponent.filterItems).not.toHaveBeenCalled();
expect(autoCompleteComponent.onFocus).not.toHaveBeenCalled();
});
it('should call onInput Event ', () => {
spyOn(autoCompleteComponent, 'onInput').and.callThrough();
autoCompleteComponent.onInput(event);
expect(autoCompleteComponent.onInput).toHaveBeenCalled();
});
it('should call mouseEnter Event ', () => {
nbr = 1;
spyOn(autoCompleteComponent, 'mouseEnter').and.callThrough();
autoCompleteComponent.mouseEnter(nbr);
expect(autoCompleteComponent.mouseEnter).toHaveBeenCalled();
});
it('should call keyDownArrowDown Event ', () => {
// const keyDnEvent = window.addEventListener('keydown', e => {
// autoCompleteComponent.handleEvent(e); // e is KeyboardEvent
// });
// const keyUpEvent = window.addEventListener('keyup', e => {
// autoCompleteComponent.handleEvent(e); // e is KeyboardEvent
// });
spyOn(autoCompleteComponent, 'keyDownArrowDown').and.callThrough();
let e = jasmine.createSpyObj('e', ['KeyboardEvent']);
let index = 0;
let isFocused: boolean;
isFocused = true;
let eventKey = autoCompleteComponent.keyBoardEvent;
if ((eventKey === 'ArrowDown' || eventKey === 'Down') && this.isFocused) {
index = this.selectedItemIndex;
autoCompleteComponent.autoCompleteInput.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
autoCompleteComponent.autoCompleteInput.value = autoCompleteComponent.filteredItems[index];
}
} else if ((eventKey === 'ArrowUp' || eventKey === 'Up') && autoCompleteComponent.isFocused) {
index = autoCompleteComponent.selectedItemIndex - 2;
autoCompleteComponent.autoCompleteInput.getAttribute('aria-activedescendant');
if (index >= 0 && index <= this.filteredItems.length - 1) {
autoCompleteComponent.autoCompleteInput.value = autoCompleteComponent.filteredItems[index];
}
} else if (index !== null && index >= 0) {
autoCompleteComponent.changeAria.emit(autoCompleteComponent.filteredItems[index]);
}
// expect(autoCompleteComponent.keyDownArrowDown).toEqual('ArrowDown');
// expect(autoCompleteComponent.keyDownArrowDown).toBeUndefined();
expect(autoCompleteComponent.keyDownArrowDown(e, index)).toBeUndefined();
});
it('should call handleOnKeydown function ', () => {
let item = autoCompleteComponent.filter;
let index = 0;
spyOn(autoCompleteComponent, 'handleOnKeydown').and.callFake((event: any): void => {
let index = 0;
if (this.selectedItemIndex === null || this.selectedItemIndex < 0) {
this.changeAria.emit('showall');
} else {
this.keyDownArrowDown(event, index);
}
if (event.key === 'Enter' && this.isFocused && this.selectedItemIndex > 0) {
this.enterKeyDown(event);
} else if ((event.key === 'ArrowUp' || event.key === 'Up') && this.isFocused) {
event.preventDefault();
this.altArrowUpNoEnter(event);
} else if ((event.key === 'ArrowDown' || event.key === 'Down') && this.isFocused) {
event.preventDefault();
this.altArrowDnNoEnter(event);
} else if ((event.key === 'Escape') && this.isFocused) {
this.clearFocus();
this.inputRef.nativeElement.focus();
}
});
});
it('should call enterKeyDown function ', () => {
let item = autoCompleteComponent.item;
let selectedItemIndex = autoCompleteComponent.selectedItemIndex;
let filtereditems = autoCompleteComponent.filteredItems;
spyOn(autoCompleteComponent, 'enterKeyDown').and.callFake(() => {
if (selectedItemIndex === 0) {
event.preventDefault();
autoCompleteComponent.onItemSelect(null, item);
} else if (selectedItemIndex > 0) {
event.preventDefault();
autoCompleteComponent.onItemSelect(null, filtereditems[selectedItemIndex - 1]);
}
expect(autoCompleteComponent.enterKeyDown).toBeUndefined();
expect(autoCompleteComponent.enterKeyDown).toHaveBeenCalled();
});
});
it('should call altArrowUpNoEnter (ARROWUP UP) function ', () => {
let e = jasmine.createSpyObj('e', ['preventDefault']);
autoCompleteComponent.altArrowUpNoEnter(e);
spyOn(autoCompleteComponent, 'altArrowUpNoEnter').and.callFake((): void => {
if (autoCompleteComponent.selectedItemIndex === null || autoCompleteComponent.selectedItemIndex === 0) {
autoCompleteComponent.selectedItemIndex = autoCompleteComponent.filteredItems.length;
} else {
if (autoCompleteComponent.selectedItemIndex > 0) {
autoCompleteComponent.selectedItemIndex--;
}
}
});
autoCompleteComponent.altArrowUpNoEnter(e);
expect(autoCompleteComponent.altArrowUpNoEnter).toBeTruthy();
});
it('should call altArrowDnNoEnter (ARROWUP DN) function ', () => {
let e = jasmine.createSpyObj('e', ['preventDefault']);
autoCompleteComponent.altArrowDnNoEnter(e);
spyOn(autoCompleteComponent, 'altArrowDnNoEnter').and.callFake((): void => {
if (autoCompleteComponent.selectedItemIndex === null || autoCompleteComponent.selectedItemIndex === 0) {
autoCompleteComponent.selectedItemIndex = autoCompleteComponent.filteredItems.length;
} else {
if (autoCompleteComponent.selectedItemIndex > 0) {
autoCompleteComponent.selectedItemIndex--;
}
}
});
autoCompleteComponent.altArrowDnNoEnter(e);
expect(autoCompleteComponent.altArrowDnNoEnter).toBeTruthy();
});
it('should call patternTextForIdAttribute function ', () => {
let text = autoCompleteComponent.textSelected;
let retValText: string;
let retVal = spyOn(autoCompleteComponent, 'patternTextForIdAttribute').and.callFake(() => {
if (text) {
retValText = text.replace(/\ /g, '_');
} else {
retValText = '';
}
return retValText;
});
autoCompleteComponent.patternTextForIdAttribute(text);
expect(autoCompleteComponent.patternTextForIdAttribute).toBeTruthy();
});
});