/// /// Copyright 2015-2026 Micro Focus or one of its affiliates. /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// import { Component, ViewChild } from '@angular/core'; import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { dispatchMouseEvent } from '../../common/testing'; import { TabsetComponent } from './tabset.component'; import { TabsetModule } from './tabset.module'; @Component({ selector: 'app-tabset-test', template: ` @for (tab of tabs; track tab) { {{ tab.title }}

{{ tab.title }}

{{ tab.content }}

}
`, imports: [TabsetModule], }) export class TabsetTestComponent { tabs: Tab[] = [ { content: 'test text', title: 'Schedule', }, { content: 'test text', title: 'Protection', }, { content: 'test text', title: 'Solution', }, { content: 'test text', title: 'Analytics', }, ]; // eslint-disable-next-line @typescript-eslint/no-empty-function activeChange(_title: string, _active: boolean) {} // eslint-disable-next-line @typescript-eslint/no-empty-function onTabActivated(): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function onTabDeactivated(): void {} @ViewChild('tabset', { static: true }) public tabset: TabsetComponent; } describe('Tabset Component', () => { let component: TabsetTestComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [TabsetModule, TabsetTestComponent], }).compileComponents(); fixture = TestBed.createComponent(TabsetTestComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); await fixture.whenStable(); fixture.detectChanges(); }); it('should emit activated output when tab is clicked', async () => { spyOn(component, 'onTabActivated'); await clickTab(1); expect(component.onTabActivated).toHaveBeenCalled(); }); it('should emit deactivated output when tab is deselected', async () => { spyOn(component, 'onTabDeactivated'); await clickTab(2); expect(component.onTabDeactivated).toHaveBeenCalled(); }); it('should emit activeChange output when tab is selected', async () => { const activeChangeSpy = spyOn(component, 'activeChange'); await clickTab(2); expect(activeChangeSpy.calls.allArgs()).toEqual([ ['Schedule', false], ['Solution', true], ]); }); it('should change to new tab when tab is programmatically selected by tab index', fakeAsync(() => { const tab1 = getTabItem(0); const tab3 = getTabItem(2); // check first tab is active expect(tab1.classList.contains('active')).toBeTruthy(); spyOn(component, 'onTabActivated'); // programatically change tab by index component.tabset.selectTab(2); tick(20); // check second tab is now active/first tab is not active expect(tab3.classList.contains('active')).toBeTruthy(); expect(tab1.classList.contains('active')).toBeFalsy(); // check tab contains correct content expect(nativeElement.querySelector('.tab-pane[aria-hidden="false"] h4').innerHTML).toEqual( 'Solution' ); expect(component.onTabActivated).toHaveBeenCalled(); })); it('should change to new tab when tab is programmatically selected by tab instance', fakeAsync(() => { const tab1 = getTabItem(0); const tab3 = getTabItem(2); // check first tab is active expect(tab1.classList.contains('active')).toBeTruthy(); spyOn(component, 'onTabActivated'); // programatically change tab by instance component.tabset.selectTab(component.tabset._tabset.tabs[2]); tick(20); // check second tab is now active/first tab is not active expect(tab3.classList.contains('active')).toBeTruthy(); expect(tab1.classList.contains('active')).toBeFalsy(); // check tab contains correct content expect(nativeElement.querySelector('.tab-pane[aria-hidden="false"] h4').innerHTML).toEqual( 'Solution' ); expect(component.onTabActivated).toHaveBeenCalled(); })); it('should change to a tab when the `active` property is set to true', async () => { const tab1 = getTabItem(0); const tab3 = getTabItem(2); // check first tab is active expect(tab1.classList.contains('active')).toBe(true); // set tab 3 to be active component.tabs[2].active = true; fixture.detectChanges(); await fixture.whenStable(); expect(tab1.classList.contains('active')).toBe(false); expect(tab3.classList.contains('active')).toBe(true); // check tab contains correct content expect(nativeElement.querySelector('.tab-pane[aria-hidden="false"] h4').innerHTML).toEqual( 'Solution' ); }); it('should change to the most recent tab when multiple `active` properties are set to true', async () => { const tab1 = getTabItem(0); const tab2 = getTabItem(1); const tab3 = getTabItem(2); // check first tab is active expect(tab1.classList.contains('active')).toBe(true); // set tab 2 to be active component.tabs[1].active = true; fixture.detectChanges(); await fixture.whenStable(); // set tab 3 to be active component.tabs[2].active = true; fixture.detectChanges(); await fixture.whenStable(); expect(tab1.classList.contains('active')).toBe(false); expect(tab2.classList.contains('active')).toBe(false); expect(tab3.classList.contains('active')).toBe(true); // check tab contains correct content expect(nativeElement.querySelector('.tab-pane[aria-hidden="false"] h4').innerHTML).toEqual( 'Solution' ); }); async function clickTab(index: number): Promise { const tab = getTabLink(index); dispatchMouseEvent(tab, 'mousedown'); await fixture.whenStable(); } function getTabLink(index: number): HTMLElement { return nativeElement.querySelectorAll('ux-tabset ul .nav-link').item(index); } function getTabItem(index: number): HTMLElement { return nativeElement.querySelectorAll('ux-tabset ul .nav-item').item(index); } }); export interface Tab { title: string; content: string; active?: boolean; }