| /******************************************************************************** |
| * Copyright (c) 2020 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0 |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| ********************************************************************************/ |
| |
| import {Component, ViewChild} from "@angular/core"; |
| import {async, ComponentFixture, TestBed} from "@angular/core/testing"; |
| import {FormsModule, NgForm, NgModel} from "@angular/forms"; |
| import {By} from "@angular/platform-browser"; |
| import {EKeyboardKeys} from "../../../util/events"; |
| import {ISelectOption} from "../ISelectOption"; |
| import {SelectModule} from "../select.module"; |
| import {SelectComponent} from "./select.component"; |
| |
| describe("SelectComponent", () => { |
| let component: SelectSpecComponent; |
| let fixture: ComponentFixture<SelectSpecComponent>; |
| |
| function getSelectToggle(): HTMLButtonElement { |
| return fixture.debugElement.query(By.css(".select-toggle"))?.nativeElement; |
| } |
| |
| function getSelectToggleText(): HTMLSpanElement { |
| return fixture.debugElement.query(By.css(".select-toggle-text"))?.nativeElement; |
| } |
| |
| function getOptions(): HTMLElement[] { |
| return fixture.debugElement |
| .queryAll(By.css(".select-options-button")) |
| .map((e) => e?.nativeElement) |
| .filter((e) => e != null); |
| } |
| |
| beforeEach(async(() => { |
| TestBed.configureTestingModule({ |
| declarations: [ |
| SelectSpecComponent |
| ], |
| imports: [ |
| SelectModule, |
| FormsModule |
| ] |
| }).compileComponents(); |
| })); |
| |
| beforeEach(() => { |
| fixture = TestBed.createComponent(SelectSpecComponent); |
| component = fixture.componentInstance; |
| fixture.detectChanges(); |
| }); |
| |
| it("should create", () => { |
| expect(component).toBeTruthy(); |
| expect(component.selectComponentRef).toBeTruthy(); |
| expect(component.ngModel).toBeTruthy(); |
| }); |
| |
| it("should register to Angular forms", async () => { |
| component.modelValue = 19; |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| expect(component.ngForm.value).toEqual({select: 19}); |
| expect(component.selectComponentRef.appValue).toBe(19); |
| |
| component.ngForm.control.disable(); |
| expect(component.selectComponentRef.appDisabled).toBeTrue(); |
| }); |
| |
| it("should display the placeholder or the selected option", async () => { |
| component.appPlaceholder = "" + Math.random(); |
| fixture.detectChanges(); |
| |
| expect(getSelectToggleText().innerText).toBeDefined(); |
| expect(getSelectToggleText().innerText).toBe(component.appPlaceholder); |
| |
| component.appValue = 19; |
| fixture.detectChanges(); |
| |
| expect(getSelectToggleText().innerText).toBeDefined(); |
| expect(getSelectToggleText().innerText).toBe(component.appPlaceholder); |
| |
| component.setOptions(100); |
| fixture.detectChanges(); |
| |
| expect(getSelectToggleText().innerText).toBeDefined(); |
| expect(getSelectToggleText().innerText).toBe("Option 19"); |
| }); |
| |
| it("should change value when clicking on option", async () => { |
| const toggle = getSelectToggle(); |
| |
| component.setOptions(100); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| toggle.click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| const options = getOptions(); |
| expect(options.length).toBe(100); |
| |
| options[19].click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.appValue).toBe(19); |
| expect(component.ngModel.value).toBe(19); |
| expect(component.ngForm.value).toEqual({select: 19}); |
| expect(getOptions().length).toBe(0); |
| }); |
| |
| it("should not change value when disabled", async () => { |
| const toggle = getSelectToggle(); |
| |
| component.setOptions(100); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| toggle.click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| component.appDisabled = true; |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| component.selectComponentRef.onClickOnOption(19); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.appValue).not.toBeDefined(); |
| expect(component.ngModel.value).not.toBeDefined(); |
| expect(component.ngForm.value).toEqual({select: undefined}); |
| expect(getOptions().length).toBe(0); |
| }); |
| |
| it("should increase value with keyboard input", async () => { |
| const KEYS = [EKeyboardKeys.ARROW_DOWN, EKeyboardKeys.ARROW_RIGHT]; |
| const NUMBER_OF_OPTIONS = 10; |
| |
| const toggle = getSelectToggle(); |
| component.setOptions(NUMBER_OF_OPTIONS); |
| |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.appValue).toBe(undefined); |
| |
| let selectedId = 0; |
| |
| for (let i = 0; i < 2 * NUMBER_OF_OPTIONS; i++) { |
| for (const key of KEYS) { |
| toggle.dispatchEvent(new KeyboardEvent("keydown", {key})); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| expect(component.appValue).toBe(selectedId); |
| expect(component.ngModel.value).toBe(selectedId); |
| expect(component.ngForm.value).toEqual({select: selectedId}); |
| selectedId = selectedId + 1 >= NUMBER_OF_OPTIONS ? 0 : selectedId + 1; |
| } |
| } |
| }); |
| |
| it("should decrease value with keyboard input", async () => { |
| const KEYS = [EKeyboardKeys.ARROW_UP, EKeyboardKeys.ARROW_LEFT]; |
| const NUMBER_OF_OPTIONS = 10; |
| |
| const toggle = getSelectToggle(); |
| |
| component.setOptions(NUMBER_OF_OPTIONS); |
| |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.appValue).toBe(undefined); |
| |
| let selectedId = NUMBER_OF_OPTIONS - 1; |
| |
| for (let i = 0; i < 2 * NUMBER_OF_OPTIONS; i++) { |
| for (const key of KEYS) { |
| toggle.dispatchEvent(new KeyboardEvent("keydown", {key})); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| expect(component.appValue).toBe(selectedId); |
| expect(component.ngModel.value).toBe(selectedId); |
| expect(component.ngForm.value).toEqual({select: selectedId}); |
| selectedId = selectedId <= 0 ? NUMBER_OF_OPTIONS - 1 : selectedId - 1; |
| } |
| } |
| }); |
| |
| it("should do nothing on keyboard inputs when no options are available", async () => { |
| const KEYS = [EKeyboardKeys.ARROW_DOWN, EKeyboardKeys.ARROW_RIGHT, EKeyboardKeys.ARROW_UP, EKeyboardKeys.ARROW_LEFT]; |
| |
| const toggle = getSelectToggle(); |
| |
| for (const key of KEYS) { |
| toggle.dispatchEvent(new KeyboardEvent("keydown", {key})); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| expect(component.appValue).not.toBeDefined(); |
| expect(component.ngModel.value).not.toBeDefined(); |
| expect(component.ngForm.value).toEqual({select: undefined}); |
| } |
| }); |
| |
| it("should do nothing on keyboard inputs when disabled", async () => { |
| const KEYS = [EKeyboardKeys.ARROW_DOWN, EKeyboardKeys.ARROW_RIGHT, EKeyboardKeys.ARROW_UP, EKeyboardKeys.ARROW_LEFT]; |
| |
| const toggle = getSelectToggle(); |
| |
| component.setOptions(100); |
| component.appDisabled = true; |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| for (const key of KEYS) { |
| toggle.dispatchEvent(new KeyboardEvent("keydown", {key})); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| expect(component.appValue).not.toBeDefined(); |
| expect(component.ngModel.value).not.toBeDefined(); |
| expect(component.ngForm.value).toEqual({select: undefined}); |
| } |
| }); |
| |
| it("should toggle drop down menu on click", async () => { |
| const toggle = getSelectToggle(); |
| |
| component.setOptions(100); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| toggle.click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.selectComponentRef.dropDown.isOpen).toBeTrue(); |
| expect(getOptions().length).toBe(100); |
| |
| toggle.click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.selectComponentRef.dropDown.isOpen).toBeFalse(); |
| expect(getOptions().length).toBe(0); |
| }); |
| |
| it("should not toggle drop down menu when disabled or no options are available", async () => { |
| const toggle = getSelectToggle(); |
| |
| toggle.click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.selectComponentRef.dropDown.isOpen).toBeFalse(); |
| expect(getOptions().length).toBe(0); |
| |
| component.setOptions(100); |
| component.appDisabled = true; |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.selectComponentRef.dropDown.isOpen).toBeFalse(); |
| expect(getOptions().length).toBe(0); |
| }); |
| |
| it("should close drop down on pressing tab or escape", async () => { |
| const KEYS = [EKeyboardKeys.TAB, EKeyboardKeys.ESCAPE]; |
| |
| const toggle = getSelectToggle(); |
| |
| component.setOptions(100); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| for (const key of KEYS) { |
| toggle.click(); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.selectComponentRef.dropDown.isOpen).toBeTrue(); |
| expect(getOptions().length).toBe(100); |
| |
| toggle.dispatchEvent(new KeyboardEvent("keydown", {key})); |
| fixture.detectChanges(); |
| await fixture.whenStable(); |
| |
| expect(component.selectComponentRef.dropDown.isOpen).toBeFalse(); |
| expect(getOptions().length).toBe(0); |
| } |
| }); |
| |
| }); |
| |
| @Component({ |
| selector: "app-select-spec", |
| template: ` |
| <form> |
| <app-select |
| [(appValue)]="appValue" |
| [appDisabled]="appDisabled" |
| [appOptions]="appOptions" |
| [appPlaceholder]="appPlaceholder" |
| [ngModel]="modelValue" |
| name="select"> |
| </app-select> |
| </form> |
| ` |
| }) |
| class SelectSpecComponent { |
| |
| @ViewChild(NgModel) |
| public ngModel: NgModel; |
| |
| @ViewChild(NgForm) |
| public ngForm: NgForm; |
| |
| @ViewChild(SelectComponent) |
| public selectComponentRef: SelectComponent; |
| |
| public appValue: number; |
| |
| public appOptions: ISelectOption[]; |
| |
| public appDisabled: boolean; |
| |
| public appPlaceholder: string; |
| |
| public modelValue: number; |
| |
| public setOptions(size: number) { |
| this.appOptions = size == null ? undefined : Array(size) |
| .fill(0).map((_, value) => ({value, label: "Option " + value})); |
| } |
| |
| } |