///
/// 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;
}