Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test when a button is enabled using Karma in Angular2

I have an Angular2 project setup using angular CLI. I'm trying to test a form component. It has two fields: email and password. Both are required. There's a login button of the type submit. It's enabled only after the user has provided valid inputs in both the fields.

<form (ngSubmit)="login()" #loginForm="ngForm">

        <md-input-container class="md-block">
            <input md-input [(ngModel)]="user.email" class="userEmail"
                name="userEmail" type="email" placeholder="Email" 
                ngControl="userEmail" 
            required>
        </md-input-container>
        <br>

        <md-input-container class="md-block">
            <input md-input [(ngModel)]="user.password" class="userPassword"
                name="userPassword" type="password" placeholder="Password" 
                ngControl="userPassword" 
            required>
        </md-input-container>
        <br>



        <!--the button is enabled only after all form fields are valid-->
        <button  color="primary" md-button 
            type="submit" class='loginButton'
        [disabled]="!loginForm.form.valid">
            Login
         </button>

Now, I want to test the button using karma. I went through the docs and was able to test the input, by giving a random input during testing and verifying it:

//imports...

describe('LoginComponent (inline template)', () => {
  let comp:    LoginComponent;
  let fixture: ComponentFixture<TmLoginComponent>;
  let userEmail: HTMLInputElement;
  let userPassword: HTMLInputElement;
  let loginBtn: HTMLElement;
  let title: HTMLElement;



  beforeEach(() => {

    TestBed.configureTestingModule({
      declarations: [ LoginComponent ], // declare the test component
      imports: [ MaterialModule.forRoot(), FormsModule, 
                  RouterTestingModule.withRoutes(
                  [{path: 'login', component: LoginComponent}, ])
                ],
      providers: [{provide: UserAuthenticationService, useValue: uaServiceStub }, CookieService],

    });

    fixture = TestBed.createComponent(LoginComponent);

    comp = fixture.componentInstance; //LoginComponent test instance

    // query by CSS element selector
    userEmail = fixture.debugElement.query(By.css('.userEmail')).nativeElement;
    userPassword = fixture.debugElement.query(By.css('.userPassword')).nativeElement;
    loginBtn = fixture.debugElement.query(By.css('.loginButton')).nativeElement;


//tests

//this test is successful
  it('should check initial input', () => {
    fixture.detectChanges();
    expect(userEmail.value).toBe('')
  });

//this test is successful
  it('should check later input', async(() => {    
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      userEmail.value = 'someValue';
      userEmail.dispatchEvent(new Event('change'));

      expect(userEmail.value).toBe('someValue');
    });

  }));

//EDITED: NEW TEST
it('should check loginBtn is disabled initially', () => {
  fixture.detectChanges();
  loginBtn =   fixture.debugElement.query(By.css('.loginButton')).nativeElement;
  fixture.whenStable().then(() => {
    expect(loginBtn.disabled).toBe(true)
   })
 });


//this test fails. "Expected true to be false"
  it('should check loginBtn is enabled after inputs check out', async(() => {
   fixture.detectChanges();
   fixture.whenStable().then(() => {
   userEmail.value = '[email protected]';//valid
   userEmail.dispatchEvent(new Event('change'));

   userPassword.value = 'asdf';//vaild
   userPassword.dispatchEvent(new Event('change'));
   fixture.detectChanges();
   expect(loginBtn.disabled).toBe(false)
  })
 }));

});

I don't see why the test fails. Can anybody help?

like image 321
Snowman Avatar asked Jan 09 '17 09:01

Snowman


People also ask

What is the purpose of the fixture detectChanges () call in this unit test?

Fixtures have access to a debugElement , which will give you access to the internals of the component fixture. Change detection isn't done automatically, so you'll call detectChanges on a fixture to tell Angular to run change detection.


1 Answers

If you take a look at DefaultValueAccessor source code:

host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},

https://github.com/angular/angular/blob/2.4.2/modules/%40angular/forms/src/directives/default_value_accessor.ts#L36

you can notice that main your mistake is wrong event name.

You have to use input event instead of change

it('should check loginBtn is enabled after inputs check out', async(() => {
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    userEmail.value = '[email protected]';
    userEmail.dispatchEvent(new Event('input'));

    userPassword.value = 'asdf';
    userPassword.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    expect(loginBtn.disabled).toBe(false)
  });
}));

Plunker Example

like image 91
yurzui Avatar answered Oct 05 '22 19:10

yurzui