/// /// 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 { APP_BASE_HREF, PlatformLocation } from '@angular/common'; import { MockPlatformLocation } from '@angular/common/testing'; import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterModule } from '@angular/router'; import { ColorServiceModule, colorSets } from '../../services/color'; import { Breadcrumb } from '../breadcrumbs/breadcrumbs.component'; import { PageHeaderNavigationItem } from './navigation/navigation.component'; import { PageHeaderModule } from './page-header.module'; @Component({ selector: 'app-page-header-test', template: ` `, imports: [PageHeaderModule], }) export class PageHeaderTestComponent { items: PageHeaderNavigationItem[] = [ { icon: 'home', title: 'Home', id: 'home', }, { icon: 'analytics', title: 'Analytics', id: 'analytics', children: [ { title: 'Bar Charts', id: 'bar-charts', }, { title: 'Pie Charts', id: 'pie-charts', children: [ { title: 'Daily View', id: 'daily-view', }, { title: 'Weekly View', id: 'weekly-view', }, { title: 'Monthly View', id: 'monthly-view', }, ], }, ], }, ]; crumbs: Breadcrumb[] = [ { title: 'Home', routerLink: 'home', }, { title: 'Archive', // eslint-disable-next-line @typescript-eslint/no-empty-function onClick: () => {}, }, { title: 'Page', routerLink: 'page', // eslint-disable-next-line @typescript-eslint/no-empty-function onClick: () => {}, }, ]; // eslint-disable-next-line @typescript-eslint/no-empty-function onLogoClick(): void {} // eslint-disable-next-line @typescript-eslint/no-empty-function onBackClick(): void {} } describe('Page Header Component', () => { let component: PageHeaderTestComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ PageHeaderModule, RouterModule.forRoot([]), ColorServiceModule.forRoot(colorSets.keppel), NoopAnimationsModule, PageHeaderTestComponent, ], providers: [ { provide: APP_BASE_HREF, useValue: '/' }, { provide: PlatformLocation, useClass: MockPlatformLocation }, ], }).compileComponents(); fixture = TestBed.createComponent(PageHeaderTestComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); /** * We cannot do this in the existing E2E Page Header * tests as in the Keppel theme the logo container * is not visible and therefore we cannot perform the * necessary click. */ it('should emit event on logo click', () => { // find the logo container const logoContainer: HTMLElement = nativeElement.querySelector('.page-header-logo-container'); expect(logoContainer).toBeTruthy(); // set up a spy for the onLogoClick function spyOn(component, 'onLogoClick'); logoContainer.click(); expect(component.onLogoClick).toHaveBeenCalled(); }); it('should emit event on back click', () => { // Ensure back button is visible const backButton: HTMLElement = nativeElement.querySelector('.page-header-back-button'); expect(backButton).toBeTruthy(); // set up a spy for the onBackClick function spyOn(component, 'onBackClick'); backButton.click(); expect(component.onBackClick).toHaveBeenCalled(); }); it('should have a product header of UX', () => { // Checks that the page header us 'UX' const pageHeader: HTMLElement = nativeElement.querySelector('.page-header-acronym'); expect(pageHeader.textContent).toContain('UX'); }); it('should add href to breadcrumbs with a routerLink', () => { const homeBreadcrumb = nativeElement.querySelector('.breadcrumb a'); expect(homeBreadcrumb.textContent.trim()).toBe('Home'); expect(homeBreadcrumb.hasAttribute('href')).toBe(true); }); it('should not add href to breadcrumbs without a routerLink', () => { const archiveBreadcrumb = nativeElement.querySelectorAll('.breadcrumb a')[1]; expect(archiveBreadcrumb.textContent.trim()).toBe('Archive'); expect(archiveBreadcrumb.hasAttribute('href')).toBe(false); }); it('should only add one for each breadcrumb', () => { const breadcrumbs = nativeElement.querySelectorAll('.breadcrumb a'); expect(breadcrumbs.length).toBe(3); }); it('should propagate ids through menu and sub menu items', async () => { const navigation = nativeElement.querySelector('.page-header-navigation'); const navItems = navigation.querySelectorAll('button'); expect(navItems[0].id).toBe('home'); expect(navItems[1].id).toBe('analytics'); // open and check menu items navItems[1].click(); fixture.detectChanges(); const menu = document.querySelector('.ux-menu'); const menuItems = menu.querySelectorAll('button'); expect(menuItems[0].id).toBe('bar-charts'); expect(menuItems[1].id).toBe('pie-charts'); // open and check sub menu items menuItems[1].dispatchEvent(new MouseEvent('mouseenter')); fixture.detectChanges(); await fixture.whenStable(); const subMenu = document.querySelector('.horizontal-navigation-dropdown-submenu'); const subMenuItems = subMenu.querySelectorAll('button'); expect(subMenuItems[0].id).toBe('daily-view'); expect(subMenuItems[1].id).toBe('weekly-view'); expect(subMenuItems[2].id).toBe('monthly-view'); }); it('should add the menubar role when there are items', () => { const menuBar = document.querySelector('ux-page-header-horizontal-navigation'); expect(menuBar.getAttribute('role')).toContain('menubar'); }); it('should not add the menubar role when there are no items', () => { component.items = []; fixture.detectChanges(); const menuBar = document.querySelector('ux-page-header-horizontal-navigation'); expect(menuBar.getAttribute('role')).not.toBe('menubar'); }); }); @Component({ selector: 'app-page-header-secondary-test', template: ` `, imports: [PageHeaderModule], }) export class PageHeaderSecondaryTestComponent { items: PageHeaderNavigationItem[] = [ { title: 'Home', id: 'home', }, { title: 'Secondary', id: 'secondary', children: [ { title: 'Secondary Menu 1', id: 'secondary-menu-1', }, { title: 'Secondary Menu 2', id: 'secondary-menu-2', }, { title: 'Secondary Menu 3', id: 'secondary-menu-3', }, ], }, ]; } describe('Page Header Secondary Component', () => { let component: PageHeaderSecondaryTestComponent; let fixture: ComponentFixture; let nativeElement: HTMLElement; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [ PageHeaderModule, RouterModule.forRoot([]), ColorServiceModule.forRoot(colorSets.keppel), NoopAnimationsModule, PageHeaderSecondaryTestComponent, ], providers: [ { provide: APP_BASE_HREF, useValue: '/' }, { provide: PlatformLocation, useClass: MockPlatformLocation }, ], }).compileComponents(); fixture = TestBed.createComponent(PageHeaderSecondaryTestComponent); component = fixture.componentInstance; nativeElement = fixture.nativeElement; fixture.detectChanges(); }); it('should propagate ids through secondary menu items', async () => { const navigation = nativeElement.querySelector('.page-header-navigation'); const navItems = navigation.querySelectorAll('button'); expect(navItems[0].id).toBe('home'); expect(navItems[1].id).toBe('secondary'); // open and check menu items navItems[1].click(); fixture.detectChanges(); await fixture.whenStable(); const navTabs = document.querySelector('.nav-tabs'); const tabItem = navTabs.querySelectorAll('.nav-link'); expect(tabItem[0].id).toBe('secondary-menu-1'); expect(tabItem[1].id).toBe('secondary-menu-2'); expect(tabItem[2].id).toBe('secondary-menu-3'); }); });