17

I'm writing a test for an Angular 4 component that is a login form. The form can be submitted by clicking on the "submit" button or by hitting enter in any input fields. This behaviour is dictated by the Angular form directive.

I can write a test case that validates that a button click submits the form, but I can't trigger the submit behaviour with a keypress event.

Template:

<form (ngSubmit)="onLoginSubmit()" #loginForm="ngForm">
<div class="form-group">
    <label for="userid">User ID</label>
    <input type="text" class="form-control" name="userid" id="userid" required
        [(ngModel)]="model.userId" #userid="ngModel">
    <div [hidden]="userid.valid || userid.untouched" class="alert alert-danger">
        User ID is required
    </div>
</div>
<div class="form-group">
    <label for="password">Password</label>
    <input type="password" class="form-control" name="password" id="password" required
        [(ngModel)]="model.password" #password="ngModel">
    <div [hidden]="password.valid || password.untouched" class="alert alert-danger">
        Password is required
    </div>
</div>
<button type="submit" class="btn btn-success" [disabled]="loginForm.form.invalid">Submit</button>    

Spec:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement, Component, ViewChild } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { Observable } from 'rxjs/Observable';

import { LoginFormComponent } from './login-form.component';
import { ILoginService } from '../../service/ILoginService';
import { IAuthService } from '../../service/IAuthService';


describe('Login Form', () => {
    let comp: LoginFormComponent;
    let fixture: ComponentFixture<LoginFormComponent>;
    let userIdElement: DebugElement;
    let passwordElement: DebugElement;
    let submitElement: DebugElement;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [FormsModule, ReactiveFormsModule],
            declarations: [LoginFormComponent],
            providers: [
                { provide: 'IloginService', useClass: UserServiceMock },
                { provide: 'IAuthService', useClass: MockAuthService }]
        });
        fixture = TestBed.createComponent(LoginFormComponent);

        comp = fixture.componentInstance;

        userIdElement = fixture.debugElement.query(By.css('input[name=userid]'));
        passwordElement = fixture.debugElement.query(By.css('input[name=password]'));
        submitElement = fixture.debugElement.query(By.css('button'));
    });

    describe('Submit', () => {
        let authService: IAuthService;
        let authServiceSpy: jasmine.Spy;
        let loginService: ILoginService;
        let loginServiceSpy: jasmine.Spy;

        beforeEach(() => {
            comp.model.userId = 'mock user';
            comp.model.password = 'mock password';
            comp.loginUrl = 'mock url';

            authService = fixture.debugElement.injector.get('IAuthService');
            authServiceSpy = spyOn(authService, 'login').and.returnValue(null);

            loginService = fixture.debugElement.injector.get('IloginService');
            loginServiceSpy = spyOn(loginService, 'handleLoginResult');
        });

        it('should invoke the auth and login services when submit is clicked', () => {
            submitElement.nativeElement.click();
        });

        xit('should submit the form on enter key pressed in userId input', () => {
            userIdElement.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
        });

        xit('should submit the form on enter key pressed in password input', () => {
            passwordElement.nativeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }))
        });

        afterEach(() => {
            fixture.detectChanges();
            fixture.whenStable().then(() => {
                expect(authService.login).toHaveBeenCalledWith('mock user', 'mock password', 'mock url');
                expect(loginService.handleLoginResult).toHaveBeenCalled();
            });
        });
    });
});

The test that dispatches a 'click' event from the button passes, but the tests (currently disabled) that dispatch a keydown event from input elements fail.

Is there a different event I can dispatch to trigger the form's ngSubmit handler to fire?

1
  • have you found the answer for that? Commented Jan 24, 2021 at 13:08

2 Answers 2

2

Try using keypress instead of 'keydown`

new KeyboardEvent('keypress', { key: 'Enter' })
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the advice, but 'keypress' doesn't work any better than 'keydown' or 'keyup'
-1

(Late answer but I don't see an accepted one here) Just dispatching the event is not enough you must send in the event to the component too. So in your example, you just have a form submit, so you would need to call:

component.onLoginSubmit();

However to separate out the logic, perhaps have a hostlistener that is doing the work for you:

  @HostListener('keypress.enter', ['$event'])
  keypressEnter(event: KeyboardEvent) {
     // Maybe some further logic due to keys? Validation?
     this.onLoginSubmit();
  }

Then in your test you must also call:

component.keypressEnter(new KeyboardEvent('keypress', { key: 'Enter' }));

Finally a fixture.detectChanges() will complete the duty cycle.

Calling dispatch event on it's own will not work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.