I would like to change the value of an input field from within an Angular 2 unit test.
<input type="text" class="form-control" [(ngModel)]="abc.value" />
I can't just change the ngModel
because abc
object is private:
private abc: Abc = new Abc();
In Angular 2 testing, can I simulate the user typing into the input field so that the ngModel
will be updated with what the user has typed from within a unit test?
I can grab the DebugElement
and the nativeElement
of the input field without a problem. (Just setting a the value
property on the nativeElement
of the input field doesn't seem to work as it doesn't update the ngModel
with what I've set for the value).
Maybe inputDebugEl.triggerEventHandler
can be called, but I'm not sure what arguments to give it so it will simulate the user having typed a particular string of input.
You're right that you can't just set the input, you also need to dispatch the 'input'
event. Here is a function I wrote earlier this evening to input text:
function sendInput(text: string) { inputElement.value = text; inputElement.dispatchEvent(new Event('input')); fixture.detectChanges(); return fixture.whenStable(); }
Here fixture
is the ComponentFixture
and inputElement
is the relevant HTTPInputElement
from the fixture's nativeElement
. This returns a promise, so you'll probably have to resolve it sendInput('whatever').then(...)
.
In context: https://github.com/textbook/known-for-web/blob/52c8aec4c2699c2f146a33c07786e1e32891c8b6/src/app/actor/actor.component.spec.ts#L134
Update:
We had some issues getting this to work in Angular 2.1, it didn't like creating a new Event(...)
, so instead we did:
import { dispatchEvent } from '@angular/platform-browser/testing/browser-util'; ... function sendInput(text: string) { inputElement.value = text; dispatchEvent(fixture.nativeElement, 'input'); fixture.detectChanges(); return fixture.whenStable(); }
The accepted solution didn't quite work for me in Angular 2.4. The value I had set was not appearing in the (test) UI, even after detectChanges() was called.
The way I got it to work was to set up my test as follows:
describe('TemplateComponent', function () { let comp: TemplateComponent; let fixture: ComponentFixture<TemplateComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ FormsModule ], declarations: [ TemplateComponent ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(TemplateComponent); comp = fixture.componentInstance; }); it('should allow us to set a bound input field', fakeAsync(() => { setInputValue('#test2', 'Tommy'); expect(comp.personName).toEqual('Tommy'); })); // must be called from within fakeAsync due to use of tick() function setInputValue(selector: string, value: string) { fixture.detectChanges(); tick(); let input = fixture.debugElement.query(By.css(selector)).nativeElement; input.value = value; input.dispatchEvent(new Event('input')); tick(); } });
My TemplateComponent
component has a property named personName
in this example, which was the model property I am binding to in my template:
<input id="test2" type="text" [(ngModel)]="personName" />
I also had trouble getting jonrsharpe's answer to work with Angular 2.4. I found that the calls to fixture.detectChanges()
and fixture.whenStable()
caused the form component to reset. It seems that some initialization function is still pending when the test starts. I solved this by adding extra calls to these methods before each test. Here is a snippet of my code:
beforeEach(() => {
TestBed.configureTestingModule({
// ...etc...
});
fixture = TestBed.createComponent(LoginComponent);
comp = fixture.componentInstance;
usernameBox = fixture.debugElement.query(By.css('input[name="username"]'));
passwordBox = fixture.debugElement.query(By.css('input[type="password"]'));
loginButton = fixture.debugElement.query(By.css('.btn-primary'));
formElement = fixture.debugElement.query(By.css('form'));
});
beforeEach(async(() => {
// The magic sauce!!
// Because this is in an async wrapper it will automatically wait
// for the call to whenStable() to complete
fixture.detectChanges();
fixture.whenStable();
}));
function sendInput(inputElement: any, text: string) {
inputElement.value = text;
inputElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
return fixture.whenStable();
}
it('should log in correctly', async(() => {
sendInput(usernameBox.nativeElement, 'User1')
.then(() => {
return sendInput(passwordBox.nativeElement, 'Password1')
}).then(() => {
formElement.triggerEventHandler('submit', null);
fixture.detectChanges();
let spinner = fixture.debugElement.query(By.css('img'));
expect(Helper.isHidden(spinner)).toBeFalsy('Spinner should be visible');
// ...etc...
});
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With