Reputation: 1058
I have created an Angular form component that uses <ng-select>
as a replacement for HTML <select>
elements. Now I want to unit test my component. To test the component logic I have methods that find HTML elements in the DOM and examine or modify their value. This works fine with native HTML elements. But <ng-select>
is difficult.
I can find the current value with something like this, but it feels like a very brittle solution.
document.querySelector("ng-select[formControlName=country] .ng-value .ng-value-label").innerText
And how do I change the selection? With a standard input component you can set the value, then fire an input event. There's an <input type="text" role="combobox">
inside <ng-select/>
, but changing its value doesn't seem to affect the FormControl value.
I'm not trying to test ng-select, but I do need to verify that my component reacts appropriately to selection changes. It will also be necessary to make selections in the UI when running automated integration tests.
Upvotes: 3
Views: 5816
Reputation: 51
I stumbled over the same problem. Chris is right the ng-select source provides some testing functions from which I just build this file. Which makes it hopefully a little easier to just select an option of a select which is queried by a css-selector. Let me know if it works for you.
import { DebugElement } from '@angular/core';
import { ComponentFixture, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
export class TestsErrorHandler {}
export enum KeyCode {
Tab = 9,
Enter = 13,
Esc = 27,
Space = 32,
ArrowUp = 38,
ArrowDown = 40,
Backspace = 8
}
export function tickAndDetectChanges(fixture: ComponentFixture<any>) {
fixture.detectChanges();
tick();
}
export function selectOption(fixture, selector: string, arrowDowns: number) {
triggerKeyDownEvent(getNgSelectElement(fixture, selector), KeyCode.Space); // open
tickAndDetectChanges(fixture); // need to tick and detect changes
for (let i = 0; i < arrowDowns; i++) {
triggerKeyDownEvent(getNgSelectElement(fixture, selector), KeyCode.ArrowDown);
}
tickAndDetectChanges(fixture); dropdown fully inits after promise is resolved
triggerKeyDownEvent(getNgSelectElement(fixture, selector), KeyCode.Enter); // select
fixture.detectChanges();
}
export function getNgSelectElement(fixture: ComponentFixture<any>, selector: string): DebugElement {
return fixture.debugElement.query(By.css(selector));
}
export function triggerKeyDownEvent(element: DebugElement, which: number, key = ''): void {
element.triggerEventHandler('keydown', {
which: which,
key: key,
preventDefault: () => { },
});
}
Upvotes: 3
Reputation: 1058
The answer is to check out the ng-select source and look at the test code. There are several examples of tests that change select values.
Upvotes: -3
Reputation: 1896
You can use a Harness (MatselectHarness) to interact with an Angular Material component. This will make the tests more robust:
it('should have 20 countries in the select box', async () => {
...
const select = await loader.getHarness(MatSelectHarness.with(MatSelectHarness);
//Click the select element host
(await selectHarness.host()).click();
const actual = (await selectHarness.getOptions()).length;
expect(actual).toBe(20);
});
Select a value (example code from angular doc):
it('should switch to bug report template', async () => {
expect(fixture.debugElement.query('bug-report-form')).toBeNull();
const select = await loader.getHarness(MatSelect);
await select.open();
const bugOption = await select.getOption({text: 'Bug'});
await bugOption.click();
expect(fixture.debugElement.query('bug-report-form')).not.toBeNull();
});
See: using-component-harnesses
An other way is to move the logic into a service. A service can be unit tested easily.
Upvotes: -1