How to change tabs in the unit test of a component that uses Angular Material Tabs
Asked Answered
S

5

9

I have a LoginView component in which I have Angular Material Tabs. In one tab there's a LoginForm component displayed and in a second tab there's a RegistrationForm component.

What I try to test in LoginView is that when I click on a second tab, a RegistrationForm would be displayed. However, I have no idea how to click a tab. I've tried adding name or id to mat-tab but it isn't being generated in DOM, querySelectorAll() also returns null.

Source:

<mat-tab-group dynamicHeight class="py-5">
  <mat-tab label="{{'form.letsLogIn' | translate}}">
    <app-login-form></app-login-form>
  </mat-tab>
  <mat-tab label="{{'form.letsRegister' | translate}}">
      <app-registration-form></app-registration-form>
  </mat-tab>
</mat-tab-group>

Spec file:

import { Component } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginViewComponent } from './login-view.component';
import { TranslateModule } from '@ngx-translate/core';
import { MatTabsModule } from '@angular/material';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@Component({selector: 'app-login-form', template: ''})
class LoginFormStubComponent {}

@Component({selector: 'app-registration-form', template: ''})
class RegistrationFormStubComponent {}

describe('LoginViewComponent', () => {
  let component: LoginViewComponent;
  let fixture: ComponentFixture<LoginViewComponent>;
  let compiled: any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ 
        LoginViewComponent,
        LoginFormStubComponent,
        RegistrationFormStubComponent ],
      imports: [
        TranslateModule.forRoot(),
        MatTabsModule,
        BrowserAnimationsModule
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginViewComponent);
    component = fixture.componentInstance;
    compiled = fixture.nativeElement;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should have a title', () => {
    expect(compiled.querySelector('h1')).toBeTruthy();
  });

  it('should display login form at start', () => {
    expect(compiled.querySelector('app-login-form')).toBeTruthy();
  });

  it('should display registration form after clicking second tab', () => {
    compiled = fixture.nativeElement;
    compiled.querySelectorAll('mat-tab')[1].click();
    fixture.detectChanges();
    expect(compiled.querySelector('app-registration-form')).toBeTruthy();
  });
});
Schmid answered 21/9, 2018 at 15:20 Comment(2)
Please add all the spec file. The BeforeEach is essential to help you.Tertiary
@Tertiary I've edited my code.Schmid
M
9

I just worked the same problem. Your test is fine. Just add async() and use whenStable().

it('should display registration form after clicking second tab', async(() => {
  compiled = fixture.nativeElement;
  compiled.querySelectorAll('mat-tab')[1].click();
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    expect(compiled.querySelector('app-registration-form')).toBeTruthy();
  });
}));
Malaise answered 3/5, 2019 at 8:31 Comment(0)
H
6

The mat-tab element is just the container. The element that handles the click uses the class mat-tab-label. Try:

fixture.debugElement.queryAll(By.css('.mat-tab-label'))[1].nativeElement.click();

EDIT:

Alternatively, include inside your component a reference to the MatTabGroup component, and then set MatTabGroup.selectedIndex directly or from a component function:

Component HTML:

<mat-tab-group dynamicHeight class="py-5" #tabGroup=="matTabGroup">
...

Component TS:

@ViewChild('tabGroup') tabGroup: MatTabGroup;

setTab(index: number) {
    // maybe add some bounds and null checking
    this.tabGroup.selectedIndex = index;
}

Unit test usage:

component.setTab(1):
fixture.detectChanges();
Halftruth answered 21/9, 2018 at 16:47 Comment(6)
still the same, returns nullSchmid
Strange. That works for me (mat-tab doesn't). It might be easier to include a reference to the MatTabGroup in your component, and set MatTabGroup.selectedIndex directly (or via a component function).Halftruth
it still doesn't work, hovewer, I think it's becase of Material animation - until the animation of changing tab is not finished, it shows selectedIndex to the old tab. So the question is how can I wait for animation end in unit tests? I've tried some wait or sleep which I know from Protractor, but couldn't find it for Karma.Schmid
I've used setTimeout(); and it works, but I believe, there must be a better way to wait for animation end.Schmid
Look into fakeAsync and tick or fixture.whenStable().Halftruth
You can subscribe to matTabGroup.animationDone as wellUnmeaning
N
1

It doesn't work. Use MatTabHarness for changing index of the mat tab. Don't forget to use async-await.

let loader: HarnessLoader;
let fixture: ComponentFixture<MyComponent>

describe('test scenerio', ()=>{
  
  beforeEach( async()=>{
    await TestBed.configureTestingModule({
      imports: [MyModule], 
      declarations: [MyComponent]
    }).compileComponents();
    
    fixture = TestBed.createComponent(MyComponent)
    loader = TestbedHarnessEnvironment.loader(fixture)
  })

  
  if('test case', ()=>{
    let matTab = await loader.getHarness(MatTabGroupHarness);
    await matTab.select({label: 'myLabel'})
  })
})
Nowt answered 10/12, 2023 at 12:42 Comment(0)
C
0

adding up to above ans by G. Tranter here

it('tab change', () => {
    component.onTabChanged(1);
    fixture.detectChanges();
})

'tab change' is the attribute I was missing

Concavoconcave answered 8/11, 2021 at 12:19 Comment(1)
Why do you prefer this approach over the approaches proposed in the previous answers? Does this take advantage of new capabilities? Are there particular scenarios where this is more appropriate?Endymion
M
0

You could try this:

HTML:

<mat-tab-group #tabGroup

TS:

@ViewChild('tabGroup') tabGroup: MatTabGroup;

goToTab(index: number): void {
    this.tabGroup.selectedIndex = index;
}

test.spec.ts

it('should change tab', () => {
    fixture.debugElement.componentInstance.goToTab(1);

    fixture.detectChanges();

    fixture.whenStable().then(() => {
        // tab is now visible. expects go here
    });
});
Mayence answered 10/3, 2023 at 16:18 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.